VirtualBox

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

Last change on this file since 63184 was 63147, checked in by vboxsync, 9 years ago

Main: warnings

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 348.7 KB
Line 
1/* $Id: MediumImpl.cpp 63147 2016-08-08 11:12:33Z 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.215389.xyz. 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();
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 return (m->fMoveThisMedium == true) ? true:false;
6423}
6424
6425bool Medium::i_resetMoveOperationData()
6426{
6427 m->strNewLocationFull.setNull();
6428 m->fMoveThisMedium = false;
6429 return true;
6430}
6431
6432Utf8Str Medium::i_getNewLocationForMoving() const
6433{
6434 if(m->fMoveThisMedium == true)
6435 return m->strNewLocationFull;
6436 else
6437 return Utf8Str();
6438}
6439////////////////////////////////////////////////////////////////////////////////
6440//
6441// Private methods
6442//
6443////////////////////////////////////////////////////////////////////////////////
6444
6445/**
6446 * Queries information from the medium.
6447 *
6448 * As a result of this call, the accessibility state and data members such as
6449 * size and description will be updated with the current information.
6450 *
6451 * @note This method may block during a system I/O call that checks storage
6452 * accessibility.
6453 *
6454 * @note Caller MUST NOT hold the media tree or medium lock.
6455 *
6456 * @note Locks m->pParent for reading. Locks this object for writing.
6457 *
6458 * @param fSetImageId Whether to reset the UUID contained in the image file to the UUID in the medium instance data (see SetIDs())
6459 * @param fSetParentId Whether to reset the parent UUID contained in the image file to the parent
6460 * UUID in the medium instance data (see SetIDs())
6461 * @return
6462 */
6463HRESULT Medium::i_queryInfo(bool fSetImageId, bool fSetParentId, AutoCaller &autoCaller)
6464{
6465 Assert(!isWriteLockOnCurrentThread());
6466 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6467
6468 if ( ( m->state != MediumState_Created
6469 && m->state != MediumState_Inaccessible
6470 && m->state != MediumState_LockedRead)
6471 || m->fClosing)
6472 return E_FAIL;
6473
6474 HRESULT rc = S_OK;
6475
6476 int vrc = VINF_SUCCESS;
6477
6478 /* check if a blocking i_queryInfo() call is in progress on some other thread,
6479 * and wait for it to finish if so instead of querying data ourselves */
6480 if (m->queryInfoRunning)
6481 {
6482 Assert( m->state == MediumState_LockedRead
6483 || m->state == MediumState_LockedWrite);
6484
6485 while (m->queryInfoRunning)
6486 {
6487 alock.release();
6488 /* must not hold the object lock now */
6489 Assert(!isWriteLockOnCurrentThread());
6490 {
6491 AutoReadLock qlock(m->queryInfoSem COMMA_LOCKVAL_SRC_POS);
6492 }
6493 alock.acquire();
6494 }
6495
6496 return S_OK;
6497 }
6498
6499 bool success = false;
6500 Utf8Str lastAccessError;
6501
6502 /* are we dealing with a new medium constructed using the existing
6503 * location? */
6504 bool isImport = m->id.isZero();
6505 unsigned uOpenFlags = VD_OPEN_FLAGS_INFO;
6506
6507 /* Note that we don't use VD_OPEN_FLAGS_READONLY when opening new
6508 * media because that would prevent necessary modifications
6509 * when opening media of some third-party formats for the first
6510 * time in VirtualBox (such as VMDK for which VDOpen() needs to
6511 * generate an UUID if it is missing) */
6512 if ( m->hddOpenMode == OpenReadOnly
6513 || m->type == MediumType_Readonly
6514 || (!isImport && !fSetImageId && !fSetParentId)
6515 )
6516 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
6517
6518 /* Open shareable medium with the appropriate flags */
6519 if (m->type == MediumType_Shareable)
6520 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
6521
6522 /* Lock the medium, which makes the behavior much more consistent, must be
6523 * done before dropping the object lock and setting queryInfoRunning. */
6524 ComPtr<IToken> pToken;
6525 if (uOpenFlags & (VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SHAREABLE))
6526 rc = LockRead(pToken.asOutParam());
6527 else
6528 rc = LockWrite(pToken.asOutParam());
6529 if (FAILED(rc)) return rc;
6530
6531 /* Copies of the input state fields which are not read-only,
6532 * as we're dropping the lock. CAUTION: be extremely careful what
6533 * you do with the contents of this medium object, as you will
6534 * create races if there are concurrent changes. */
6535 Utf8Str format(m->strFormat);
6536 Utf8Str location(m->strLocationFull);
6537 ComObjPtr<MediumFormat> formatObj = m->formatObj;
6538
6539 /* "Output" values which can't be set because the lock isn't held
6540 * at the time the values are determined. */
6541 Guid mediumId = m->id;
6542 uint64_t mediumSize = 0;
6543 uint64_t mediumLogicalSize = 0;
6544
6545 /* Flag whether a base image has a non-zero parent UUID and thus
6546 * need repairing after it was closed again. */
6547 bool fRepairImageZeroParentUuid = false;
6548
6549 ComObjPtr<VirtualBox> pVirtualBox = m->pVirtualBox;
6550
6551 /* must be set before leaving the object lock the first time */
6552 m->queryInfoRunning = true;
6553
6554 /* must leave object lock now, because a lock from a higher lock class
6555 * is needed and also a lengthy operation is coming */
6556 alock.release();
6557 autoCaller.release();
6558
6559 /* Note that taking the queryInfoSem after leaving the object lock above
6560 * can lead to short spinning of the loops waiting for i_queryInfo() to
6561 * complete. This is unavoidable since the other order causes a lock order
6562 * violation: here it would be requesting the object lock (at the beginning
6563 * of the method), then queryInfoSem, and below the other way round. */
6564 AutoWriteLock qlock(m->queryInfoSem COMMA_LOCKVAL_SRC_POS);
6565
6566 /* take the opportunity to have a media tree lock, released initially */
6567 Assert(!isWriteLockOnCurrentThread());
6568 Assert(!pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
6569 AutoWriteLock treeLock(pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
6570 treeLock.release();
6571
6572 /* re-take the caller, but not the object lock, to keep uninit away */
6573 autoCaller.add();
6574 if (FAILED(autoCaller.rc()))
6575 {
6576 m->queryInfoRunning = false;
6577 return autoCaller.rc();
6578 }
6579
6580 try
6581 {
6582 /* skip accessibility checks for host drives */
6583 if (m->hostDrive)
6584 {
6585 success = true;
6586 throw S_OK;
6587 }
6588
6589 PVBOXHDD hdd;
6590 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
6591 ComAssertRCThrow(vrc, E_FAIL);
6592
6593 try
6594 {
6595 /** @todo This kind of opening of media is assuming that diff
6596 * media can be opened as base media. Should be documented that
6597 * it must work for all medium format backends. */
6598 vrc = VDOpen(hdd,
6599 format.c_str(),
6600 location.c_str(),
6601 uOpenFlags | m->uOpenFlagsDef,
6602 m->vdImageIfaces);
6603 if (RT_FAILURE(vrc))
6604 {
6605 lastAccessError = Utf8StrFmt(tr("Could not open the medium '%s'%s"),
6606 location.c_str(), i_vdError(vrc).c_str());
6607 throw S_OK;
6608 }
6609
6610 if (formatObj->i_getCapabilities() & MediumFormatCapabilities_Uuid)
6611 {
6612 /* Modify the UUIDs if necessary. The associated fields are
6613 * not modified by other code, so no need to copy. */
6614 if (fSetImageId)
6615 {
6616 alock.acquire();
6617 vrc = VDSetUuid(hdd, 0, m->uuidImage.raw());
6618 alock.release();
6619 if (RT_FAILURE(vrc))
6620 {
6621 lastAccessError = Utf8StrFmt(tr("Could not update the UUID of medium '%s'%s"),
6622 location.c_str(), i_vdError(vrc).c_str());
6623 throw S_OK;
6624 }
6625 mediumId = m->uuidImage;
6626 }
6627 if (fSetParentId)
6628 {
6629 alock.acquire();
6630 vrc = VDSetParentUuid(hdd, 0, m->uuidParentImage.raw());
6631 alock.release();
6632 if (RT_FAILURE(vrc))
6633 {
6634 lastAccessError = Utf8StrFmt(tr("Could not update the parent UUID of medium '%s'%s"),
6635 location.c_str(), i_vdError(vrc).c_str());
6636 throw S_OK;
6637 }
6638 }
6639 /* zap the information, these are no long-term members */
6640 alock.acquire();
6641 unconst(m->uuidImage).clear();
6642 unconst(m->uuidParentImage).clear();
6643 alock.release();
6644
6645 /* check the UUID */
6646 RTUUID uuid;
6647 vrc = VDGetUuid(hdd, 0, &uuid);
6648 ComAssertRCThrow(vrc, E_FAIL);
6649
6650 if (isImport)
6651 {
6652 mediumId = uuid;
6653
6654 if (mediumId.isZero() && (m->hddOpenMode == OpenReadOnly))
6655 // only when importing a VDMK that has no UUID, create one in memory
6656 mediumId.create();
6657 }
6658 else
6659 {
6660 Assert(!mediumId.isZero());
6661
6662 if (mediumId != uuid)
6663 {
6664 /** @todo r=klaus this always refers to VirtualBox.xml as the medium registry, even for new VMs */
6665 lastAccessError = Utf8StrFmt(
6666 tr("UUID {%RTuuid} of the medium '%s' does not match the value {%RTuuid} stored in the media registry ('%s')"),
6667 &uuid,
6668 location.c_str(),
6669 mediumId.raw(),
6670 pVirtualBox->i_settingsFilePath().c_str());
6671 throw S_OK;
6672 }
6673 }
6674 }
6675 else
6676 {
6677 /* the backend does not support storing UUIDs within the
6678 * underlying storage so use what we store in XML */
6679
6680 if (fSetImageId)
6681 {
6682 /* set the UUID if an API client wants to change it */
6683 alock.acquire();
6684 mediumId = m->uuidImage;
6685 alock.release();
6686 }
6687 else if (isImport)
6688 {
6689 /* generate an UUID for an imported UUID-less medium */
6690 mediumId.create();
6691 }
6692 }
6693
6694 /* set the image uuid before the below parent uuid handling code
6695 * might place it somewhere in the media tree, so that the medium
6696 * UUID is valid at this point */
6697 alock.acquire();
6698 if (isImport || fSetImageId)
6699 unconst(m->id) = mediumId;
6700 alock.release();
6701
6702 /* get the medium variant */
6703 unsigned uImageFlags;
6704 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
6705 ComAssertRCThrow(vrc, E_FAIL);
6706 alock.acquire();
6707 m->variant = (MediumVariant_T)uImageFlags;
6708 alock.release();
6709
6710 /* check/get the parent uuid and update corresponding state */
6711 if (uImageFlags & VD_IMAGE_FLAGS_DIFF)
6712 {
6713 RTUUID parentId;
6714 vrc = VDGetParentUuid(hdd, 0, &parentId);
6715 ComAssertRCThrow(vrc, E_FAIL);
6716
6717 /* streamOptimized VMDK images are only accepted as base
6718 * images, as this allows automatic repair of OVF appliances.
6719 * Since such images don't support random writes they will not
6720 * be created for diff images. Only an overly smart user might
6721 * manually create this case. Too bad for him. */
6722 if ( (isImport || fSetParentId)
6723 && !(uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED))
6724 {
6725 /* the parent must be known to us. Note that we freely
6726 * call locking methods of mVirtualBox and parent, as all
6727 * relevant locks must be already held. There may be no
6728 * concurrent access to the just opened medium on other
6729 * threads yet (and init() will fail if this method reports
6730 * MediumState_Inaccessible) */
6731
6732 ComObjPtr<Medium> pParent;
6733 if (RTUuidIsNull(&parentId))
6734 rc = VBOX_E_OBJECT_NOT_FOUND;
6735 else
6736 rc = pVirtualBox->i_findHardDiskById(Guid(parentId), false /* aSetError */, &pParent);
6737 if (FAILED(rc))
6738 {
6739 if (fSetImageId && !fSetParentId)
6740 {
6741 /* If the image UUID gets changed for an existing
6742 * image then the parent UUID can be stale. In such
6743 * cases clear the parent information. The parent
6744 * information may/will be re-set later if the
6745 * API client wants to adjust a complete medium
6746 * hierarchy one by one. */
6747 rc = S_OK;
6748 alock.acquire();
6749 RTUuidClear(&parentId);
6750 vrc = VDSetParentUuid(hdd, 0, &parentId);
6751 alock.release();
6752 ComAssertRCThrow(vrc, E_FAIL);
6753 }
6754 else
6755 {
6756 lastAccessError = Utf8StrFmt(tr("Parent medium with UUID {%RTuuid} of the medium '%s' is not found in the media registry ('%s')"),
6757 &parentId, location.c_str(),
6758 pVirtualBox->i_settingsFilePath().c_str());
6759 throw S_OK;
6760 }
6761 }
6762
6763 /* must drop the caller before taking the tree lock */
6764 autoCaller.release();
6765 /* we set m->pParent & children() */
6766 treeLock.acquire();
6767 autoCaller.add();
6768 if (FAILED(autoCaller.rc()))
6769 throw autoCaller.rc();
6770
6771 if (m->pParent)
6772 i_deparent();
6773
6774 if (!pParent.isNull())
6775 if (pParent->i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX)
6776 {
6777 AutoReadLock plock(pParent COMMA_LOCKVAL_SRC_POS);
6778 throw setError(VBOX_E_INVALID_OBJECT_STATE,
6779 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"),
6780 pParent->m->strLocationFull.c_str());
6781 }
6782 i_setParent(pParent);
6783
6784 treeLock.release();
6785 }
6786 else
6787 {
6788 /* must drop the caller before taking the tree lock */
6789 autoCaller.release();
6790 /* we access m->pParent */
6791 treeLock.acquire();
6792 autoCaller.add();
6793 if (FAILED(autoCaller.rc()))
6794 throw autoCaller.rc();
6795
6796 /* check that parent UUIDs match. Note that there's no need
6797 * for the parent's AutoCaller (our lifetime is bound to
6798 * it) */
6799
6800 if (m->pParent.isNull())
6801 {
6802 /* Due to a bug in VDCopy() in VirtualBox 3.0.0-3.0.14
6803 * and 3.1.0-3.1.8 there are base images out there
6804 * which have a non-zero parent UUID. No point in
6805 * complaining about them, instead automatically
6806 * repair the problem. Later we can bring back the
6807 * error message, but we should wait until really
6808 * most users have repaired their images, either with
6809 * VBoxFixHdd or this way. */
6810#if 1
6811 fRepairImageZeroParentUuid = true;
6812#else /* 0 */
6813 lastAccessError = Utf8StrFmt(
6814 tr("Medium type of '%s' is differencing but it is not associated with any parent medium in the media registry ('%s')"),
6815 location.c_str(),
6816 pVirtualBox->settingsFilePath().c_str());
6817 treeLock.release();
6818 throw S_OK;
6819#endif /* 0 */
6820 }
6821
6822 {
6823 autoCaller.release();
6824 AutoReadLock parentLock(m->pParent COMMA_LOCKVAL_SRC_POS);
6825 autoCaller.add();
6826 if (FAILED(autoCaller.rc()))
6827 throw autoCaller.rc();
6828
6829 if ( !fRepairImageZeroParentUuid
6830 && m->pParent->i_getState() != MediumState_Inaccessible
6831 && m->pParent->i_getId() != parentId)
6832 {
6833 /** @todo r=klaus this always refers to VirtualBox.xml as the medium registry, even for new VMs */
6834 lastAccessError = Utf8StrFmt(
6835 tr("Parent UUID {%RTuuid} of the medium '%s' does not match UUID {%RTuuid} of its parent medium stored in the media registry ('%s')"),
6836 &parentId, location.c_str(),
6837 m->pParent->i_getId().raw(),
6838 pVirtualBox->i_settingsFilePath().c_str());
6839 parentLock.release();
6840 treeLock.release();
6841 throw S_OK;
6842 }
6843 }
6844
6845 /// @todo NEWMEDIA what to do if the parent is not
6846 /// accessible while the diff is? Probably nothing. The
6847 /// real code will detect the mismatch anyway.
6848
6849 treeLock.release();
6850 }
6851 }
6852
6853 mediumSize = VDGetFileSize(hdd, 0);
6854 mediumLogicalSize = VDGetSize(hdd, 0);
6855
6856 success = true;
6857 }
6858 catch (HRESULT aRC)
6859 {
6860 rc = aRC;
6861 }
6862
6863 vrc = VDDestroy(hdd);
6864 if (RT_FAILURE(vrc))
6865 {
6866 lastAccessError = Utf8StrFmt(tr("Could not update and close the medium '%s'%s"),
6867 location.c_str(), i_vdError(vrc).c_str());
6868 success = false;
6869 throw S_OK;
6870 }
6871 }
6872 catch (HRESULT aRC)
6873 {
6874 rc = aRC;
6875 }
6876
6877 autoCaller.release();
6878 treeLock.acquire();
6879 autoCaller.add();
6880 if (FAILED(autoCaller.rc()))
6881 {
6882 m->queryInfoRunning = false;
6883 return autoCaller.rc();
6884 }
6885 alock.acquire();
6886
6887 if (success)
6888 {
6889 m->size = mediumSize;
6890 m->logicalSize = mediumLogicalSize;
6891 m->strLastAccessError.setNull();
6892 }
6893 else
6894 {
6895 m->strLastAccessError = lastAccessError;
6896 Log1WarningFunc(("'%s' is not accessible (error='%s', rc=%Rhrc, vrc=%Rrc)\n",
6897 location.c_str(), m->strLastAccessError.c_str(), rc, vrc));
6898 }
6899
6900 /* Set the proper state according to the result of the check */
6901 if (success)
6902 m->preLockState = MediumState_Created;
6903 else
6904 m->preLockState = MediumState_Inaccessible;
6905
6906 /* unblock anyone waiting for the i_queryInfo results */
6907 qlock.release();
6908 m->queryInfoRunning = false;
6909
6910 pToken->Abandon();
6911 pToken.setNull();
6912
6913 if (FAILED(rc)) return rc;
6914
6915 /* If this is a base image which incorrectly has a parent UUID set,
6916 * repair the image now by zeroing the parent UUID. This is only done
6917 * when we have structural information from a config file, on import
6918 * this is not possible. If someone would accidentally call openMedium
6919 * with a diff image before the base is registered this would destroy
6920 * the diff. Not acceptable. */
6921 if (fRepairImageZeroParentUuid)
6922 {
6923 rc = LockWrite(pToken.asOutParam());
6924 if (FAILED(rc)) return rc;
6925
6926 alock.release();
6927
6928 try
6929 {
6930 PVBOXHDD hdd;
6931 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
6932 ComAssertRCThrow(vrc, E_FAIL);
6933
6934 try
6935 {
6936 vrc = VDOpen(hdd,
6937 format.c_str(),
6938 location.c_str(),
6939 (uOpenFlags & ~VD_OPEN_FLAGS_READONLY) | m->uOpenFlagsDef,
6940 m->vdImageIfaces);
6941 if (RT_FAILURE(vrc))
6942 throw S_OK;
6943
6944 RTUUID zeroParentUuid;
6945 RTUuidClear(&zeroParentUuid);
6946 vrc = VDSetParentUuid(hdd, 0, &zeroParentUuid);
6947 ComAssertRCThrow(vrc, E_FAIL);
6948 }
6949 catch (HRESULT aRC)
6950 {
6951 rc = aRC;
6952 }
6953
6954 VDDestroy(hdd);
6955 }
6956 catch (HRESULT aRC)
6957 {
6958 rc = aRC;
6959 }
6960
6961 pToken->Abandon();
6962 pToken.setNull();
6963 if (FAILED(rc)) return rc;
6964 }
6965
6966 return rc;
6967}
6968
6969/**
6970 * Performs extra checks if the medium can be closed and returns S_OK in
6971 * this case. Otherwise, returns a respective error message. Called by
6972 * Close() under the medium tree lock and the medium lock.
6973 *
6974 * @note Also reused by Medium::Reset().
6975 *
6976 * @note Caller must hold the media tree write lock!
6977 */
6978HRESULT Medium::i_canClose()
6979{
6980 Assert(m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
6981
6982 if (i_getChildren().size() != 0)
6983 return setError(VBOX_E_OBJECT_IN_USE,
6984 tr("Cannot close medium '%s' because it has %d child media"),
6985 m->strLocationFull.c_str(), i_getChildren().size());
6986
6987 return S_OK;
6988}
6989
6990/**
6991 * Unregisters this medium with mVirtualBox. Called by close() under the medium tree lock.
6992 *
6993 * @note Caller must have locked the media tree lock for writing!
6994 */
6995HRESULT Medium::i_unregisterWithVirtualBox()
6996{
6997 /* Note that we need to de-associate ourselves from the parent to let
6998 * VirtualBox::i_unregisterMedium() properly save the registry */
6999
7000 /* we modify m->pParent and access children */
7001 Assert(m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
7002
7003 Medium *pParentBackup = m->pParent;
7004 AssertReturn(i_getChildren().size() == 0, E_FAIL);
7005 if (m->pParent)
7006 i_deparent();
7007
7008 HRESULT rc = m->pVirtualBox->i_unregisterMedium(this);
7009 if (FAILED(rc))
7010 {
7011 if (pParentBackup)
7012 {
7013 // re-associate with the parent as we are still relatives in the registry
7014 i_setParent(pParentBackup);
7015 }
7016 }
7017
7018 return rc;
7019}
7020
7021/**
7022 * Like SetProperty but do not trigger a settings store. Only for internal use!
7023 */
7024HRESULT Medium::i_setPropertyDirect(const Utf8Str &aName, const Utf8Str &aValue)
7025{
7026 AutoCaller autoCaller(this);
7027 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7028
7029 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
7030
7031 switch (m->state)
7032 {
7033 case MediumState_Created:
7034 case MediumState_Inaccessible:
7035 break;
7036 default:
7037 return i_setStateError();
7038 }
7039
7040 m->mapProperties[aName] = aValue;
7041
7042 return S_OK;
7043}
7044
7045/**
7046 * Sets the extended error info according to the current media state.
7047 *
7048 * @note Must be called from under this object's write or read lock.
7049 */
7050HRESULT Medium::i_setStateError()
7051{
7052 HRESULT rc = E_FAIL;
7053
7054 switch (m->state)
7055 {
7056 case MediumState_NotCreated:
7057 {
7058 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
7059 tr("Storage for the medium '%s' is not created"),
7060 m->strLocationFull.c_str());
7061 break;
7062 }
7063 case MediumState_Created:
7064 {
7065 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
7066 tr("Storage for the medium '%s' is already created"),
7067 m->strLocationFull.c_str());
7068 break;
7069 }
7070 case MediumState_LockedRead:
7071 {
7072 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
7073 tr("Medium '%s' is locked for reading by another task"),
7074 m->strLocationFull.c_str());
7075 break;
7076 }
7077 case MediumState_LockedWrite:
7078 {
7079 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
7080 tr("Medium '%s' is locked for writing by another task"),
7081 m->strLocationFull.c_str());
7082 break;
7083 }
7084 case MediumState_Inaccessible:
7085 {
7086 /* be in sync with Console::powerUpThread() */
7087 if (!m->strLastAccessError.isEmpty())
7088 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
7089 tr("Medium '%s' is not accessible. %s"),
7090 m->strLocationFull.c_str(), m->strLastAccessError.c_str());
7091 else
7092 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
7093 tr("Medium '%s' is not accessible"),
7094 m->strLocationFull.c_str());
7095 break;
7096 }
7097 case MediumState_Creating:
7098 {
7099 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
7100 tr("Storage for the medium '%s' is being created"),
7101 m->strLocationFull.c_str());
7102 break;
7103 }
7104 case MediumState_Deleting:
7105 {
7106 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
7107 tr("Storage for the medium '%s' is being deleted"),
7108 m->strLocationFull.c_str());
7109 break;
7110 }
7111 default:
7112 {
7113 AssertFailed();
7114 break;
7115 }
7116 }
7117
7118 return rc;
7119}
7120
7121/**
7122 * Sets the value of m->strLocationFull. The given location must be a fully
7123 * qualified path; relative paths are not supported here.
7124 *
7125 * As a special exception, if the specified location is a file path that ends with '/'
7126 * then the file name part will be generated by this method automatically in the format
7127 * '{<uuid>}.<ext>' where <uuid> is a fresh UUID that this method will generate
7128 * and assign to this medium, and <ext> is the default extension for this
7129 * medium's storage format. Note that this procedure requires the media state to
7130 * be NotCreated and will return a failure otherwise.
7131 *
7132 * @param aLocation Location of the storage unit. If the location is a FS-path,
7133 * then it can be relative to the VirtualBox home directory.
7134 * @param aFormat Optional fallback format if it is an import and the format
7135 * cannot be determined.
7136 *
7137 * @note Must be called from under this object's write lock.
7138 */
7139HRESULT Medium::i_setLocation(const Utf8Str &aLocation,
7140 const Utf8Str &aFormat /* = Utf8Str::Empty */)
7141{
7142 AssertReturn(!aLocation.isEmpty(), E_FAIL);
7143
7144 AutoCaller autoCaller(this);
7145 AssertComRCReturnRC(autoCaller.rc());
7146
7147 /* formatObj may be null only when initializing from an existing path and
7148 * no format is known yet */
7149 AssertReturn( (!m->strFormat.isEmpty() && !m->formatObj.isNull())
7150 || ( getObjectState().getState() == ObjectState::InInit
7151 && m->state != MediumState_NotCreated
7152 && m->id.isZero()
7153 && m->strFormat.isEmpty()
7154 && m->formatObj.isNull()),
7155 E_FAIL);
7156
7157 /* are we dealing with a new medium constructed using the existing
7158 * location? */
7159 bool isImport = m->strFormat.isEmpty();
7160
7161 if ( isImport
7162 || ( (m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File)
7163 && !m->hostDrive))
7164 {
7165 Guid id;
7166
7167 Utf8Str locationFull(aLocation);
7168
7169 if (m->state == MediumState_NotCreated)
7170 {
7171 /* must be a file (formatObj must be already known) */
7172 Assert(m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File);
7173
7174 if (RTPathFilename(aLocation.c_str()) == NULL)
7175 {
7176 /* no file name is given (either an empty string or ends with a
7177 * slash), generate a new UUID + file name if the state allows
7178 * this */
7179
7180 ComAssertMsgRet(!m->formatObj->i_getFileExtensions().empty(),
7181 ("Must be at least one extension if it is MediumFormatCapabilities_File\n"),
7182 E_FAIL);
7183
7184 Utf8Str strExt = m->formatObj->i_getFileExtensions().front();
7185 ComAssertMsgRet(!strExt.isEmpty(),
7186 ("Default extension must not be empty\n"),
7187 E_FAIL);
7188
7189 id.create();
7190
7191 locationFull = Utf8StrFmt("%s{%RTuuid}.%s",
7192 aLocation.c_str(), id.raw(), strExt.c_str());
7193 }
7194 }
7195
7196 // we must always have full paths now (if it refers to a file)
7197 if ( ( m->formatObj.isNull()
7198 || m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File)
7199 && !RTPathStartsWithRoot(locationFull.c_str()))
7200 return setError(VBOX_E_FILE_ERROR,
7201 tr("The given path '%s' is not fully qualified"),
7202 locationFull.c_str());
7203
7204 /* detect the backend from the storage unit if importing */
7205 if (isImport)
7206 {
7207 VDTYPE enmType = VDTYPE_INVALID;
7208 char *backendName = NULL;
7209
7210 int vrc = VINF_SUCCESS;
7211
7212 /* is it a file? */
7213 {
7214 RTFILE file;
7215 vrc = RTFileOpen(&file, locationFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
7216 if (RT_SUCCESS(vrc))
7217 RTFileClose(file);
7218 }
7219 if (RT_SUCCESS(vrc))
7220 {
7221 vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
7222 locationFull.c_str(), &backendName, &enmType);
7223 }
7224 else if ( vrc != VERR_FILE_NOT_FOUND
7225 && vrc != VERR_PATH_NOT_FOUND
7226 && vrc != VERR_ACCESS_DENIED
7227 && locationFull != aLocation)
7228 {
7229 /* assume it's not a file, restore the original location */
7230 locationFull = aLocation;
7231 vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
7232 locationFull.c_str(), &backendName, &enmType);
7233 }
7234
7235 if (RT_FAILURE(vrc))
7236 {
7237 if (vrc == VERR_ACCESS_DENIED)
7238 return setError(VBOX_E_FILE_ERROR,
7239 tr("Permission problem accessing the file for the medium '%s' (%Rrc)"),
7240 locationFull.c_str(), vrc);
7241 else if (vrc == VERR_FILE_NOT_FOUND || vrc == VERR_PATH_NOT_FOUND)
7242 return setError(VBOX_E_FILE_ERROR,
7243 tr("Could not find file for the medium '%s' (%Rrc)"),
7244 locationFull.c_str(), vrc);
7245 else if (aFormat.isEmpty())
7246 return setError(VBOX_E_IPRT_ERROR,
7247 tr("Could not get the storage format of the medium '%s' (%Rrc)"),
7248 locationFull.c_str(), vrc);
7249 else
7250 {
7251 HRESULT rc = i_setFormat(aFormat);
7252 /* setFormat() must not fail since we've just used the backend so
7253 * the format object must be there */
7254 AssertComRCReturnRC(rc);
7255 }
7256 }
7257 else if ( enmType == VDTYPE_INVALID
7258 || m->devType != i_convertToDeviceType(enmType))
7259 {
7260 /*
7261 * The user tried to use a image as a device which is not supported
7262 * by the backend.
7263 */
7264 return setError(E_FAIL,
7265 tr("The medium '%s' can't be used as the requested device type"),
7266 locationFull.c_str());
7267 }
7268 else
7269 {
7270 ComAssertRet(backendName != NULL && *backendName != '\0', E_FAIL);
7271
7272 HRESULT rc = i_setFormat(backendName);
7273 RTStrFree(backendName);
7274
7275 /* setFormat() must not fail since we've just used the backend so
7276 * the format object must be there */
7277 AssertComRCReturnRC(rc);
7278 }
7279 }
7280
7281 m->strLocationFull = locationFull;
7282
7283 /* is it still a file? */
7284 if ( (m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File)
7285 && (m->state == MediumState_NotCreated)
7286 )
7287 /* assign a new UUID (this UUID will be used when calling
7288 * VDCreateBase/VDCreateDiff as a wanted UUID). Note that we
7289 * also do that if we didn't generate it to make sure it is
7290 * either generated by us or reset to null */
7291 unconst(m->id) = id;
7292 }
7293 else
7294 m->strLocationFull = aLocation;
7295
7296 return S_OK;
7297}
7298
7299/**
7300 * Checks that the format ID is valid and sets it on success.
7301 *
7302 * Note that this method will caller-reference the format object on success!
7303 * This reference must be released somewhere to let the MediumFormat object be
7304 * uninitialized.
7305 *
7306 * @note Must be called from under this object's write lock.
7307 */
7308HRESULT Medium::i_setFormat(const Utf8Str &aFormat)
7309{
7310 /* get the format object first */
7311 {
7312 SystemProperties *pSysProps = m->pVirtualBox->i_getSystemProperties();
7313 AutoReadLock propsLock(pSysProps COMMA_LOCKVAL_SRC_POS);
7314
7315 unconst(m->formatObj) = pSysProps->i_mediumFormat(aFormat);
7316 if (m->formatObj.isNull())
7317 return setError(E_INVALIDARG,
7318 tr("Invalid medium storage format '%s'"),
7319 aFormat.c_str());
7320
7321 /* get properties (preinsert them as keys in the map). Note that the
7322 * map doesn't grow over the object life time since the set of
7323 * properties is meant to be constant. */
7324
7325 Assert(m->mapProperties.empty());
7326
7327 for (MediumFormat::PropertyArray::const_iterator it = m->formatObj->i_getProperties().begin();
7328 it != m->formatObj->i_getProperties().end();
7329 ++it)
7330 {
7331 m->mapProperties.insert(std::make_pair(it->strName, Utf8Str::Empty));
7332 }
7333 }
7334
7335 unconst(m->strFormat) = aFormat;
7336
7337 return S_OK;
7338}
7339
7340/**
7341 * Converts the Medium device type to the VD type.
7342 */
7343VDTYPE Medium::i_convertDeviceType()
7344{
7345 VDTYPE enmType;
7346
7347 switch (m->devType)
7348 {
7349 case DeviceType_HardDisk:
7350 enmType = VDTYPE_HDD;
7351 break;
7352 case DeviceType_DVD:
7353 enmType = VDTYPE_DVD;
7354 break;
7355 case DeviceType_Floppy:
7356 enmType = VDTYPE_FLOPPY;
7357 break;
7358 default:
7359 ComAssertFailedRet(VDTYPE_INVALID);
7360 }
7361
7362 return enmType;
7363}
7364
7365/**
7366 * Converts from the VD type to the medium type.
7367 */
7368DeviceType_T Medium::i_convertToDeviceType(VDTYPE enmType)
7369{
7370 DeviceType_T devType;
7371
7372 switch (enmType)
7373 {
7374 case VDTYPE_HDD:
7375 devType = DeviceType_HardDisk;
7376 break;
7377 case VDTYPE_DVD:
7378 devType = DeviceType_DVD;
7379 break;
7380 case VDTYPE_FLOPPY:
7381 devType = DeviceType_Floppy;
7382 break;
7383 default:
7384 ComAssertFailedRet(DeviceType_Null);
7385 }
7386
7387 return devType;
7388}
7389
7390/**
7391 * Internal method which checks whether a property name is for a filter plugin.
7392 */
7393bool Medium::i_isPropertyForFilter(const com::Utf8Str &aName)
7394{
7395 /* If the name contains "/" use the part before as a filter name and lookup the filter. */
7396 size_t offSlash;
7397 if ((offSlash = aName.find("/", 0)) != aName.npos)
7398 {
7399 com::Utf8Str strFilter;
7400 com::Utf8Str strKey;
7401
7402 HRESULT rc = strFilter.assignEx(aName, 0, offSlash);
7403 if (FAILED(rc))
7404 return false;
7405
7406 rc = strKey.assignEx(aName, offSlash + 1, aName.length() - offSlash - 1); /* Skip slash */
7407 if (FAILED(rc))
7408 return false;
7409
7410 VDFILTERINFO FilterInfo;
7411 int vrc = VDFilterInfoOne(strFilter.c_str(), &FilterInfo);
7412 if (RT_SUCCESS(vrc))
7413 {
7414 /* Check that the property exists. */
7415 PCVDCONFIGINFO paConfig = FilterInfo.paConfigInfo;
7416 while (paConfig->pszKey)
7417 {
7418 if (strKey.equals(paConfig->pszKey))
7419 return true;
7420 paConfig++;
7421 }
7422 }
7423 }
7424
7425 return false;
7426}
7427
7428/**
7429 * Returns the last error message collected by the i_vdErrorCall callback and
7430 * resets it.
7431 *
7432 * The error message is returned prepended with a dot and a space, like this:
7433 * <code>
7434 * ". <error_text> (%Rrc)"
7435 * </code>
7436 * to make it easily appendable to a more general error message. The @c %Rrc
7437 * format string is given @a aVRC as an argument.
7438 *
7439 * If there is no last error message collected by i_vdErrorCall or if it is a
7440 * null or empty string, then this function returns the following text:
7441 * <code>
7442 * " (%Rrc)"
7443 * </code>
7444 *
7445 * @note Doesn't do any object locking; it is assumed that the caller makes sure
7446 * the callback isn't called by more than one thread at a time.
7447 *
7448 * @param aVRC VBox error code to use when no error message is provided.
7449 */
7450Utf8Str Medium::i_vdError(int aVRC)
7451{
7452 Utf8Str error;
7453
7454 if (m->vdError.isEmpty())
7455 error = Utf8StrFmt(" (%Rrc)", aVRC);
7456 else
7457 error = Utf8StrFmt(".\n%s", m->vdError.c_str());
7458
7459 m->vdError.setNull();
7460
7461 return error;
7462}
7463
7464/**
7465 * Error message callback.
7466 *
7467 * Puts the reported error message to the m->vdError field.
7468 *
7469 * @note Doesn't do any object locking; it is assumed that the caller makes sure
7470 * the callback isn't called by more than one thread at a time.
7471 *
7472 * @param pvUser The opaque data passed on container creation.
7473 * @param rc The VBox error code.
7474 * @param RT_SRC_POS_DECL Use RT_SRC_POS.
7475 * @param pszFormat Error message format string.
7476 * @param va Error message arguments.
7477 */
7478/*static*/
7479DECLCALLBACK(void) Medium::i_vdErrorCall(void *pvUser, int rc, RT_SRC_POS_DECL,
7480 const char *pszFormat, va_list va)
7481{
7482 NOREF(pszFile); NOREF(iLine); NOREF(pszFunction); /* RT_SRC_POS_DECL */
7483
7484 Medium *that = static_cast<Medium*>(pvUser);
7485 AssertReturnVoid(that != NULL);
7486
7487 if (that->m->vdError.isEmpty())
7488 that->m->vdError =
7489 Utf8StrFmt("%s (%Rrc)", Utf8Str(pszFormat, va).c_str(), rc);
7490 else
7491 that->m->vdError =
7492 Utf8StrFmt("%s.\n%s (%Rrc)", that->m->vdError.c_str(),
7493 Utf8Str(pszFormat, va).c_str(), rc);
7494}
7495
7496/* static */
7497DECLCALLBACK(bool) Medium::i_vdConfigAreKeysValid(void *pvUser,
7498 const char * /* pszzValid */)
7499{
7500 Medium *that = static_cast<Medium*>(pvUser);
7501 AssertReturn(that != NULL, false);
7502
7503 /* we always return true since the only keys we have are those found in
7504 * VDBACKENDINFO */
7505 return true;
7506}
7507
7508/* static */
7509DECLCALLBACK(int) Medium::i_vdConfigQuerySize(void *pvUser,
7510 const char *pszName,
7511 size_t *pcbValue)
7512{
7513 AssertReturn(VALID_PTR(pcbValue), VERR_INVALID_POINTER);
7514
7515 Medium *that = static_cast<Medium*>(pvUser);
7516 AssertReturn(that != NULL, VERR_GENERAL_FAILURE);
7517
7518 settings::StringsMap::const_iterator it = that->m->mapProperties.find(Utf8Str(pszName));
7519 if (it == that->m->mapProperties.end())
7520 return VERR_CFGM_VALUE_NOT_FOUND;
7521
7522 /* we interpret null values as "no value" in Medium */
7523 if (it->second.isEmpty())
7524 return VERR_CFGM_VALUE_NOT_FOUND;
7525
7526 *pcbValue = it->second.length() + 1 /* include terminator */;
7527
7528 return VINF_SUCCESS;
7529}
7530
7531/* static */
7532DECLCALLBACK(int) Medium::i_vdConfigQuery(void *pvUser,
7533 const char *pszName,
7534 char *pszValue,
7535 size_t cchValue)
7536{
7537 AssertReturn(VALID_PTR(pszValue), VERR_INVALID_POINTER);
7538
7539 Medium *that = static_cast<Medium*>(pvUser);
7540 AssertReturn(that != NULL, VERR_GENERAL_FAILURE);
7541
7542 settings::StringsMap::const_iterator it = that->m->mapProperties.find(Utf8Str(pszName));
7543 if (it == that->m->mapProperties.end())
7544 return VERR_CFGM_VALUE_NOT_FOUND;
7545
7546 /* we interpret null values as "no value" in Medium */
7547 if (it->second.isEmpty())
7548 return VERR_CFGM_VALUE_NOT_FOUND;
7549
7550 const Utf8Str &value = it->second;
7551 if (value.length() >= cchValue)
7552 return VERR_CFGM_NOT_ENOUGH_SPACE;
7553
7554 memcpy(pszValue, value.c_str(), value.length() + 1);
7555
7556 return VINF_SUCCESS;
7557}
7558
7559DECLCALLBACK(int) Medium::i_vdTcpSocketCreate(uint32_t fFlags, PVDSOCKET pSock)
7560{
7561 PVDSOCKETINT pSocketInt = NULL;
7562
7563 if ((fFlags & VD_INTERFACETCPNET_CONNECT_EXTENDED_SELECT) != 0)
7564 return VERR_NOT_SUPPORTED;
7565
7566 pSocketInt = (PVDSOCKETINT)RTMemAllocZ(sizeof(VDSOCKETINT));
7567 if (!pSocketInt)
7568 return VERR_NO_MEMORY;
7569
7570 pSocketInt->hSocket = NIL_RTSOCKET;
7571 *pSock = pSocketInt;
7572 return VINF_SUCCESS;
7573}
7574
7575DECLCALLBACK(int) Medium::i_vdTcpSocketDestroy(VDSOCKET Sock)
7576{
7577 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
7578
7579 if (pSocketInt->hSocket != NIL_RTSOCKET)
7580 RTTcpClientCloseEx(pSocketInt->hSocket, false /*fGracefulShutdown*/);
7581
7582 RTMemFree(pSocketInt);
7583
7584 return VINF_SUCCESS;
7585}
7586
7587DECLCALLBACK(int) Medium::i_vdTcpClientConnect(VDSOCKET Sock, const char *pszAddress, uint32_t uPort,
7588 RTMSINTERVAL cMillies)
7589{
7590 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
7591
7592 return RTTcpClientConnectEx(pszAddress, uPort, &pSocketInt->hSocket, cMillies, NULL);
7593}
7594
7595DECLCALLBACK(int) Medium::i_vdTcpClientClose(VDSOCKET Sock)
7596{
7597 int rc = VINF_SUCCESS;
7598 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
7599
7600 rc = RTTcpClientCloseEx(pSocketInt->hSocket, false /*fGracefulShutdown*/);
7601 pSocketInt->hSocket = NIL_RTSOCKET;
7602 return rc;
7603}
7604
7605DECLCALLBACK(bool) Medium::i_vdTcpIsClientConnected(VDSOCKET Sock)
7606{
7607 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
7608 return pSocketInt->hSocket != NIL_RTSOCKET;
7609}
7610
7611DECLCALLBACK(int) Medium::i_vdTcpSelectOne(VDSOCKET Sock, RTMSINTERVAL cMillies)
7612{
7613 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
7614 return RTTcpSelectOne(pSocketInt->hSocket, cMillies);
7615}
7616
7617DECLCALLBACK(int) Medium::i_vdTcpRead(VDSOCKET Sock, void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
7618{
7619 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
7620 return RTTcpRead(pSocketInt->hSocket, pvBuffer, cbBuffer, pcbRead);
7621}
7622
7623DECLCALLBACK(int) Medium::i_vdTcpWrite(VDSOCKET Sock, const void *pvBuffer, size_t cbBuffer)
7624{
7625 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
7626 return RTTcpWrite(pSocketInt->hSocket, pvBuffer, cbBuffer);
7627}
7628
7629DECLCALLBACK(int) Medium::i_vdTcpSgWrite(VDSOCKET Sock, PCRTSGBUF pSgBuf)
7630{
7631 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
7632 return RTTcpSgWrite(pSocketInt->hSocket, pSgBuf);
7633}
7634
7635DECLCALLBACK(int) Medium::i_vdTcpFlush(VDSOCKET Sock)
7636{
7637 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
7638 return RTTcpFlush(pSocketInt->hSocket);
7639}
7640
7641DECLCALLBACK(int) Medium::i_vdTcpSetSendCoalescing(VDSOCKET Sock, bool fEnable)
7642{
7643 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
7644 return RTTcpSetSendCoalescing(pSocketInt->hSocket, fEnable);
7645}
7646
7647DECLCALLBACK(int) Medium::i_vdTcpGetLocalAddress(VDSOCKET Sock, PRTNETADDR pAddr)
7648{
7649 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
7650 return RTTcpGetLocalAddress(pSocketInt->hSocket, pAddr);
7651}
7652
7653DECLCALLBACK(int) Medium::i_vdTcpGetPeerAddress(VDSOCKET Sock, PRTNETADDR pAddr)
7654{
7655 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
7656 return RTTcpGetPeerAddress(pSocketInt->hSocket, pAddr);
7657}
7658
7659DECLCALLBACK(bool) Medium::i_vdCryptoConfigAreKeysValid(void *pvUser, const char *pszzValid)
7660{
7661 /* Just return always true here. */
7662 NOREF(pvUser);
7663 NOREF(pszzValid);
7664 return true;
7665}
7666
7667DECLCALLBACK(int) Medium::i_vdCryptoConfigQuerySize(void *pvUser, const char *pszName, size_t *pcbValue)
7668{
7669 Medium::CryptoFilterSettings *pSettings = (Medium::CryptoFilterSettings *)pvUser;
7670 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
7671 AssertReturn(VALID_PTR(pcbValue), VERR_INVALID_POINTER);
7672
7673 size_t cbValue = 0;
7674 if (!strcmp(pszName, "Algorithm"))
7675 cbValue = strlen(pSettings->pszCipher) + 1;
7676 else if (!strcmp(pszName, "KeyId"))
7677 cbValue = sizeof("irrelevant");
7678 else if (!strcmp(pszName, "KeyStore"))
7679 {
7680 if (!pSettings->pszKeyStoreLoad)
7681 return VERR_CFGM_VALUE_NOT_FOUND;
7682 cbValue = strlen(pSettings->pszKeyStoreLoad) + 1;
7683 }
7684 else if (!strcmp(pszName, "CreateKeyStore"))
7685 cbValue = 2; /* Single digit + terminator. */
7686 else
7687 return VERR_CFGM_VALUE_NOT_FOUND;
7688
7689 *pcbValue = cbValue + 1 /* include terminator */;
7690
7691 return VINF_SUCCESS;
7692}
7693
7694DECLCALLBACK(int) Medium::i_vdCryptoConfigQuery(void *pvUser, const char *pszName,
7695 char *pszValue, size_t cchValue)
7696{
7697 Medium::CryptoFilterSettings *pSettings = (Medium::CryptoFilterSettings *)pvUser;
7698 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
7699 AssertReturn(VALID_PTR(pszValue), VERR_INVALID_POINTER);
7700
7701 const char *psz = NULL;
7702 if (!strcmp(pszName, "Algorithm"))
7703 psz = pSettings->pszCipher;
7704 else if (!strcmp(pszName, "KeyId"))
7705 psz = "irrelevant";
7706 else if (!strcmp(pszName, "KeyStore"))
7707 psz = pSettings->pszKeyStoreLoad;
7708 else if (!strcmp(pszName, "CreateKeyStore"))
7709 {
7710 if (pSettings->fCreateKeyStore)
7711 psz = "1";
7712 else
7713 psz = "0";
7714 }
7715 else
7716 return VERR_CFGM_VALUE_NOT_FOUND;
7717
7718 size_t cch = strlen(psz);
7719 if (cch >= cchValue)
7720 return VERR_CFGM_NOT_ENOUGH_SPACE;
7721
7722 memcpy(pszValue, psz, cch + 1);
7723 return VINF_SUCCESS;
7724}
7725
7726DECLCALLBACK(int) Medium::i_vdCryptoKeyRetain(void *pvUser, const char *pszId,
7727 const uint8_t **ppbKey, size_t *pcbKey)
7728{
7729 Medium::CryptoFilterSettings *pSettings = (Medium::CryptoFilterSettings *)pvUser;
7730 NOREF(pszId);
7731 NOREF(ppbKey);
7732 NOREF(pcbKey);
7733 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
7734 AssertMsgFailedReturn(("This method should not be called here!\n"), VERR_INVALID_STATE);
7735}
7736
7737DECLCALLBACK(int) Medium::i_vdCryptoKeyRelease(void *pvUser, const char *pszId)
7738{
7739 Medium::CryptoFilterSettings *pSettings = (Medium::CryptoFilterSettings *)pvUser;
7740 NOREF(pszId);
7741 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
7742 AssertMsgFailedReturn(("This method should not be called here!\n"), VERR_INVALID_STATE);
7743}
7744
7745DECLCALLBACK(int) Medium::i_vdCryptoKeyStorePasswordRetain(void *pvUser, const char *pszId, const char **ppszPassword)
7746{
7747 Medium::CryptoFilterSettings *pSettings = (Medium::CryptoFilterSettings *)pvUser;
7748 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
7749
7750 NOREF(pszId);
7751 *ppszPassword = pSettings->pszPassword;
7752 return VINF_SUCCESS;
7753}
7754
7755DECLCALLBACK(int) Medium::i_vdCryptoKeyStorePasswordRelease(void *pvUser, const char *pszId)
7756{
7757 Medium::CryptoFilterSettings *pSettings = (Medium::CryptoFilterSettings *)pvUser;
7758 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
7759 NOREF(pszId);
7760 return VINF_SUCCESS;
7761}
7762
7763DECLCALLBACK(int) Medium::i_vdCryptoKeyStoreSave(void *pvUser, const void *pvKeyStore, size_t cbKeyStore)
7764{
7765 Medium::CryptoFilterSettings *pSettings = (Medium::CryptoFilterSettings *)pvUser;
7766 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
7767
7768 pSettings->pszKeyStore = (char *)RTMemAllocZ(cbKeyStore);
7769 if (!pSettings->pszKeyStore)
7770 return VERR_NO_MEMORY;
7771
7772 memcpy(pSettings->pszKeyStore, pvKeyStore, cbKeyStore);
7773 return VINF_SUCCESS;
7774}
7775
7776DECLCALLBACK(int) Medium::i_vdCryptoKeyStoreReturnParameters(void *pvUser, const char *pszCipher,
7777 const uint8_t *pbDek, size_t cbDek)
7778{
7779 Medium::CryptoFilterSettings *pSettings = (Medium::CryptoFilterSettings *)pvUser;
7780 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
7781
7782 pSettings->pszCipherReturned = RTStrDup(pszCipher);
7783 pSettings->pbDek = pbDek;
7784 pSettings->cbDek = cbDek;
7785
7786 return pSettings->pszCipherReturned ? VINF_SUCCESS : VERR_NO_MEMORY;
7787}
7788
7789/**
7790 * Implementation code for the "create base" task.
7791 *
7792 * This only gets started from Medium::CreateBaseStorage() and always runs
7793 * asynchronously. As a result, we always save the VirtualBox.xml file when
7794 * we're done here.
7795 *
7796 * @param task
7797 * @return
7798 */
7799HRESULT Medium::i_taskCreateBaseHandler(Medium::CreateBaseTask &task)
7800{
7801 /** @todo r=klaus The code below needs to be double checked with regard
7802 * to lock order violations, it probably causes lock order issues related
7803 * to the AutoCaller usage. */
7804 HRESULT rc = S_OK;
7805
7806 /* these parameters we need after creation */
7807 uint64_t size = 0, logicalSize = 0;
7808 MediumVariant_T variant = MediumVariant_Standard;
7809 bool fGenerateUuid = false;
7810
7811 try
7812 {
7813 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
7814
7815 /* The object may request a specific UUID (through a special form of
7816 * the setLocation() argument). Otherwise we have to generate it */
7817 Guid id = m->id;
7818
7819 fGenerateUuid = id.isZero();
7820 if (fGenerateUuid)
7821 {
7822 id.create();
7823 /* VirtualBox::i_registerMedium() will need UUID */
7824 unconst(m->id) = id;
7825 }
7826
7827 Utf8Str format(m->strFormat);
7828 Utf8Str location(m->strLocationFull);
7829 uint64_t capabilities = m->formatObj->i_getCapabilities();
7830 ComAssertThrow(capabilities & ( MediumFormatCapabilities_CreateFixed
7831 | MediumFormatCapabilities_CreateDynamic), E_FAIL);
7832 Assert(m->state == MediumState_Creating);
7833
7834 PVBOXHDD hdd;
7835 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
7836 ComAssertRCThrow(vrc, E_FAIL);
7837
7838 /* unlock before the potentially lengthy operation */
7839 thisLock.release();
7840
7841 try
7842 {
7843 /* ensure the directory exists */
7844 if (capabilities & MediumFormatCapabilities_File)
7845 {
7846 rc = VirtualBox::i_ensureFilePathExists(location, !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
7847 if (FAILED(rc))
7848 throw rc;
7849 }
7850
7851 VDGEOMETRY geo = { 0, 0, 0 }; /* auto-detect */
7852
7853 vrc = VDCreateBase(hdd,
7854 format.c_str(),
7855 location.c_str(),
7856 task.mSize,
7857 task.mVariant & ~MediumVariant_NoCreateDir,
7858 NULL,
7859 &geo,
7860 &geo,
7861 id.raw(),
7862 VD_OPEN_FLAGS_NORMAL | m->uOpenFlagsDef,
7863 m->vdImageIfaces,
7864 task.mVDOperationIfaces);
7865 if (RT_FAILURE(vrc))
7866 {
7867 if (vrc == VERR_VD_INVALID_TYPE)
7868 throw setError(VBOX_E_FILE_ERROR,
7869 tr("Parameters for creating the medium storage unit '%s' are invalid%s"),
7870 location.c_str(), i_vdError(vrc).c_str());
7871 else
7872 throw setError(VBOX_E_FILE_ERROR,
7873 tr("Could not create the medium storage unit '%s'%s"),
7874 location.c_str(), i_vdError(vrc).c_str());
7875 }
7876
7877 size = VDGetFileSize(hdd, 0);
7878 logicalSize = VDGetSize(hdd, 0);
7879 unsigned uImageFlags;
7880 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
7881 if (RT_SUCCESS(vrc))
7882 variant = (MediumVariant_T)uImageFlags;
7883 }
7884 catch (HRESULT aRC) { rc = aRC; }
7885
7886 VDDestroy(hdd);
7887 }
7888 catch (HRESULT aRC) { rc = aRC; }
7889
7890 if (SUCCEEDED(rc))
7891 {
7892 /* register with mVirtualBox as the last step and move to
7893 * Created state only on success (leaving an orphan file is
7894 * better than breaking media registry consistency) */
7895 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
7896 ComObjPtr<Medium> pMedium;
7897 rc = m->pVirtualBox->i_registerMedium(this, &pMedium, treeLock);
7898 Assert(this == pMedium);
7899 }
7900
7901 // re-acquire the lock before changing state
7902 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
7903
7904 if (SUCCEEDED(rc))
7905 {
7906 m->state = MediumState_Created;
7907
7908 m->size = size;
7909 m->logicalSize = logicalSize;
7910 m->variant = variant;
7911
7912 thisLock.release();
7913 i_markRegistriesModified();
7914 if (task.isAsync())
7915 {
7916 // in asynchronous mode, save settings now
7917 m->pVirtualBox->i_saveModifiedRegistries();
7918 }
7919 }
7920 else
7921 {
7922 /* back to NotCreated on failure */
7923 m->state = MediumState_NotCreated;
7924
7925 /* reset UUID to prevent it from being reused next time */
7926 if (fGenerateUuid)
7927 unconst(m->id).clear();
7928 }
7929
7930 return rc;
7931}
7932
7933/**
7934 * Implementation code for the "create diff" task.
7935 *
7936 * This task always gets started from Medium::createDiffStorage() and can run
7937 * synchronously or asynchronously depending on the "wait" parameter passed to
7938 * that function. If we run synchronously, the caller expects the medium
7939 * registry modification to be set before returning; otherwise (in asynchronous
7940 * mode), we save the settings ourselves.
7941 *
7942 * @param task
7943 * @return
7944 */
7945HRESULT Medium::i_taskCreateDiffHandler(Medium::CreateDiffTask &task)
7946{
7947 /** @todo r=klaus The code below needs to be double checked with regard
7948 * to lock order violations, it probably causes lock order issues related
7949 * to the AutoCaller usage. */
7950 HRESULT rcTmp = S_OK;
7951
7952 const ComObjPtr<Medium> &pTarget = task.mTarget;
7953
7954 uint64_t size = 0, logicalSize = 0;
7955 MediumVariant_T variant = MediumVariant_Standard;
7956 bool fGenerateUuid = false;
7957
7958 try
7959 {
7960 if (i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX)
7961 {
7962 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7963 throw setError(VBOX_E_INVALID_OBJECT_STATE,
7964 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"),
7965 m->strLocationFull.c_str());
7966 }
7967
7968 /* Lock both in {parent,child} order. */
7969 AutoMultiWriteLock2 mediaLock(this, pTarget COMMA_LOCKVAL_SRC_POS);
7970
7971 /* The object may request a specific UUID (through a special form of
7972 * the setLocation() argument). Otherwise we have to generate it */
7973 Guid targetId = pTarget->m->id;
7974
7975 fGenerateUuid = targetId.isZero();
7976 if (fGenerateUuid)
7977 {
7978 targetId.create();
7979 /* VirtualBox::i_registerMedium() will need UUID */
7980 unconst(pTarget->m->id) = targetId;
7981 }
7982
7983 Guid id = m->id;
7984
7985 Utf8Str targetFormat(pTarget->m->strFormat);
7986 Utf8Str targetLocation(pTarget->m->strLocationFull);
7987 uint64_t capabilities = pTarget->m->formatObj->i_getCapabilities();
7988 ComAssertThrow(capabilities & MediumFormatCapabilities_CreateDynamic, E_FAIL);
7989
7990 Assert(pTarget->m->state == MediumState_Creating);
7991 Assert(m->state == MediumState_LockedRead);
7992
7993 PVBOXHDD hdd;
7994 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
7995 ComAssertRCThrow(vrc, E_FAIL);
7996
7997 /* the two media are now protected by their non-default states;
7998 * unlock the media before the potentially lengthy operation */
7999 mediaLock.release();
8000
8001 try
8002 {
8003 /* Open all media in the target chain but the last. */
8004 MediumLockList::Base::const_iterator targetListBegin =
8005 task.mpMediumLockList->GetBegin();
8006 MediumLockList::Base::const_iterator targetListEnd =
8007 task.mpMediumLockList->GetEnd();
8008 for (MediumLockList::Base::const_iterator it = targetListBegin;
8009 it != targetListEnd;
8010 ++it)
8011 {
8012 const MediumLock &mediumLock = *it;
8013 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
8014
8015 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
8016
8017 /* Skip over the target diff medium */
8018 if (pMedium->m->state == MediumState_Creating)
8019 continue;
8020
8021 /* sanity check */
8022 Assert(pMedium->m->state == MediumState_LockedRead);
8023
8024 /* Open all media in appropriate mode. */
8025 vrc = VDOpen(hdd,
8026 pMedium->m->strFormat.c_str(),
8027 pMedium->m->strLocationFull.c_str(),
8028 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
8029 pMedium->m->vdImageIfaces);
8030 if (RT_FAILURE(vrc))
8031 throw setError(VBOX_E_FILE_ERROR,
8032 tr("Could not open the medium storage unit '%s'%s"),
8033 pMedium->m->strLocationFull.c_str(),
8034 i_vdError(vrc).c_str());
8035 }
8036
8037 /* ensure the target directory exists */
8038 if (capabilities & MediumFormatCapabilities_File)
8039 {
8040 HRESULT rc = VirtualBox::i_ensureFilePathExists(targetLocation,
8041 !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
8042 if (FAILED(rc))
8043 throw rc;
8044 }
8045
8046 vrc = VDCreateDiff(hdd,
8047 targetFormat.c_str(),
8048 targetLocation.c_str(),
8049 (task.mVariant & ~MediumVariant_NoCreateDir) | VD_IMAGE_FLAGS_DIFF,
8050 NULL,
8051 targetId.raw(),
8052 id.raw(),
8053 VD_OPEN_FLAGS_NORMAL | m->uOpenFlagsDef,
8054 pTarget->m->vdImageIfaces,
8055 task.mVDOperationIfaces);
8056 if (RT_FAILURE(vrc))
8057 {
8058 if (vrc == VERR_VD_INVALID_TYPE)
8059 throw setError(VBOX_E_FILE_ERROR,
8060 tr("Parameters for creating the differencing medium storage unit '%s' are invalid%s"),
8061 targetLocation.c_str(), i_vdError(vrc).c_str());
8062 else
8063 throw setError(VBOX_E_FILE_ERROR,
8064 tr("Could not create the differencing medium storage unit '%s'%s"),
8065 targetLocation.c_str(), i_vdError(vrc).c_str());
8066 }
8067
8068 size = VDGetFileSize(hdd, VD_LAST_IMAGE);
8069 logicalSize = VDGetSize(hdd, VD_LAST_IMAGE);
8070 unsigned uImageFlags;
8071 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
8072 if (RT_SUCCESS(vrc))
8073 variant = (MediumVariant_T)uImageFlags;
8074 }
8075 catch (HRESULT aRC) { rcTmp = aRC; }
8076
8077 VDDestroy(hdd);
8078 }
8079 catch (HRESULT aRC) { rcTmp = aRC; }
8080
8081 MultiResult mrc(rcTmp);
8082
8083 if (SUCCEEDED(mrc))
8084 {
8085 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
8086
8087 Assert(pTarget->m->pParent.isNull());
8088
8089 /* associate child with the parent, maximum depth was checked above */
8090 pTarget->i_setParent(this);
8091
8092 /* diffs for immutable media are auto-reset by default */
8093 bool fAutoReset;
8094 {
8095 ComObjPtr<Medium> pBase = i_getBase();
8096 AutoReadLock block(pBase COMMA_LOCKVAL_SRC_POS);
8097 fAutoReset = (pBase->m->type == MediumType_Immutable);
8098 }
8099 {
8100 AutoWriteLock tlock(pTarget COMMA_LOCKVAL_SRC_POS);
8101 pTarget->m->autoReset = fAutoReset;
8102 }
8103
8104 /* register with mVirtualBox as the last step and move to
8105 * Created state only on success (leaving an orphan file is
8106 * better than breaking media registry consistency) */
8107 ComObjPtr<Medium> pMedium;
8108 mrc = m->pVirtualBox->i_registerMedium(pTarget, &pMedium, treeLock);
8109 Assert(pTarget == pMedium);
8110
8111 if (FAILED(mrc))
8112 /* break the parent association on failure to register */
8113 i_deparent();
8114 }
8115
8116 AutoMultiWriteLock2 mediaLock(this, pTarget COMMA_LOCKVAL_SRC_POS);
8117
8118 if (SUCCEEDED(mrc))
8119 {
8120 pTarget->m->state = MediumState_Created;
8121
8122 pTarget->m->size = size;
8123 pTarget->m->logicalSize = logicalSize;
8124 pTarget->m->variant = variant;
8125 }
8126 else
8127 {
8128 /* back to NotCreated on failure */
8129 pTarget->m->state = MediumState_NotCreated;
8130
8131 pTarget->m->autoReset = false;
8132
8133 /* reset UUID to prevent it from being reused next time */
8134 if (fGenerateUuid)
8135 unconst(pTarget->m->id).clear();
8136 }
8137
8138 // deregister the task registered in createDiffStorage()
8139 Assert(m->numCreateDiffTasks != 0);
8140 --m->numCreateDiffTasks;
8141
8142 mediaLock.release();
8143 i_markRegistriesModified();
8144 if (task.isAsync())
8145 {
8146 // in asynchronous mode, save settings now
8147 m->pVirtualBox->i_saveModifiedRegistries();
8148 }
8149
8150 /* Note that in sync mode, it's the caller's responsibility to
8151 * unlock the medium. */
8152
8153 return mrc;
8154}
8155
8156/**
8157 * Implementation code for the "merge" task.
8158 *
8159 * This task always gets started from Medium::mergeTo() and can run
8160 * synchronously or asynchronously depending on the "wait" parameter passed to
8161 * that function. If we run synchronously, the caller expects the medium
8162 * registry modification to be set before returning; otherwise (in asynchronous
8163 * mode), we save the settings ourselves.
8164 *
8165 * @param task
8166 * @return
8167 */
8168HRESULT Medium::i_taskMergeHandler(Medium::MergeTask &task)
8169{
8170 /** @todo r=klaus The code below needs to be double checked with regard
8171 * to lock order violations, it probably causes lock order issues related
8172 * to the AutoCaller usage. */
8173 HRESULT rcTmp = S_OK;
8174
8175 const ComObjPtr<Medium> &pTarget = task.mTarget;
8176
8177 try
8178 {
8179 if (!task.mParentForTarget.isNull())
8180 if (task.mParentForTarget->i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX)
8181 {
8182 AutoReadLock plock(task.mParentForTarget COMMA_LOCKVAL_SRC_POS);
8183 throw setError(VBOX_E_INVALID_OBJECT_STATE,
8184 tr("Cannot merge image for medium '%s', because it exceeds the medium tree depth limit. Please merge some images which you no longer need"),
8185 task.mParentForTarget->m->strLocationFull.c_str());
8186 }
8187
8188 PVBOXHDD hdd;
8189 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
8190 ComAssertRCThrow(vrc, E_FAIL);
8191
8192 try
8193 {
8194 // Similar code appears in SessionMachine::onlineMergeMedium, so
8195 // if you make any changes below check whether they are applicable
8196 // in that context as well.
8197
8198 unsigned uTargetIdx = VD_LAST_IMAGE;
8199 unsigned uSourceIdx = VD_LAST_IMAGE;
8200 /* Open all media in the chain. */
8201 MediumLockList::Base::iterator lockListBegin =
8202 task.mpMediumLockList->GetBegin();
8203 MediumLockList::Base::iterator lockListEnd =
8204 task.mpMediumLockList->GetEnd();
8205 unsigned i = 0;
8206 for (MediumLockList::Base::iterator it = lockListBegin;
8207 it != lockListEnd;
8208 ++it)
8209 {
8210 MediumLock &mediumLock = *it;
8211 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
8212
8213 if (pMedium == this)
8214 uSourceIdx = i;
8215 else if (pMedium == pTarget)
8216 uTargetIdx = i;
8217
8218 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
8219
8220 /*
8221 * complex sanity (sane complexity)
8222 *
8223 * The current medium must be in the Deleting (medium is merged)
8224 * or LockedRead (parent medium) state if it is not the target.
8225 * If it is the target it must be in the LockedWrite state.
8226 */
8227 Assert( ( pMedium != pTarget
8228 && ( pMedium->m->state == MediumState_Deleting
8229 || pMedium->m->state == MediumState_LockedRead))
8230 || ( pMedium == pTarget
8231 && pMedium->m->state == MediumState_LockedWrite));
8232 /*
8233 * Medium must be the target, in the LockedRead state
8234 * or Deleting state where it is not allowed to be attached
8235 * to a virtual machine.
8236 */
8237 Assert( pMedium == pTarget
8238 || pMedium->m->state == MediumState_LockedRead
8239 || ( pMedium->m->backRefs.size() == 0
8240 && pMedium->m->state == MediumState_Deleting));
8241 /* The source medium must be in Deleting state. */
8242 Assert( pMedium != this
8243 || pMedium->m->state == MediumState_Deleting);
8244
8245 unsigned uOpenFlags = VD_OPEN_FLAGS_NORMAL;
8246
8247 if ( pMedium->m->state == MediumState_LockedRead
8248 || pMedium->m->state == MediumState_Deleting)
8249 uOpenFlags = VD_OPEN_FLAGS_READONLY;
8250 if (pMedium->m->type == MediumType_Shareable)
8251 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
8252
8253 /* Open the medium */
8254 vrc = VDOpen(hdd,
8255 pMedium->m->strFormat.c_str(),
8256 pMedium->m->strLocationFull.c_str(),
8257 uOpenFlags | m->uOpenFlagsDef,
8258 pMedium->m->vdImageIfaces);
8259 if (RT_FAILURE(vrc))
8260 throw vrc;
8261
8262 i++;
8263 }
8264
8265 ComAssertThrow( uSourceIdx != VD_LAST_IMAGE
8266 && uTargetIdx != VD_LAST_IMAGE, E_FAIL);
8267
8268 vrc = VDMerge(hdd, uSourceIdx, uTargetIdx,
8269 task.mVDOperationIfaces);
8270 if (RT_FAILURE(vrc))
8271 throw vrc;
8272
8273 /* update parent UUIDs */
8274 if (!task.mfMergeForward)
8275 {
8276 /* we need to update UUIDs of all source's children
8277 * which cannot be part of the container at once so
8278 * add each one in there individually */
8279 if (task.mpChildrenToReparent)
8280 {
8281 MediumLockList::Base::iterator childrenBegin = task.mpChildrenToReparent->GetBegin();
8282 MediumLockList::Base::iterator childrenEnd = task.mpChildrenToReparent->GetEnd();
8283 for (MediumLockList::Base::iterator it = childrenBegin;
8284 it != childrenEnd;
8285 ++it)
8286 {
8287 Medium *pMedium = it->GetMedium();
8288 /* VD_OPEN_FLAGS_INFO since UUID is wrong yet */
8289 vrc = VDOpen(hdd,
8290 pMedium->m->strFormat.c_str(),
8291 pMedium->m->strLocationFull.c_str(),
8292 VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
8293 pMedium->m->vdImageIfaces);
8294 if (RT_FAILURE(vrc))
8295 throw vrc;
8296
8297 vrc = VDSetParentUuid(hdd, VD_LAST_IMAGE,
8298 pTarget->m->id.raw());
8299 if (RT_FAILURE(vrc))
8300 throw vrc;
8301
8302 vrc = VDClose(hdd, false /* fDelete */);
8303 if (RT_FAILURE(vrc))
8304 throw vrc;
8305 }
8306 }
8307 }
8308 }
8309 catch (HRESULT aRC) { rcTmp = aRC; }
8310 catch (int aVRC)
8311 {
8312 rcTmp = setError(VBOX_E_FILE_ERROR,
8313 tr("Could not merge the medium '%s' to '%s'%s"),
8314 m->strLocationFull.c_str(),
8315 pTarget->m->strLocationFull.c_str(),
8316 i_vdError(aVRC).c_str());
8317 }
8318
8319 VDDestroy(hdd);
8320 }
8321 catch (HRESULT aRC) { rcTmp = aRC; }
8322
8323 ErrorInfoKeeper eik;
8324 MultiResult mrc(rcTmp);
8325 HRESULT rc2;
8326
8327 if (SUCCEEDED(mrc))
8328 {
8329 /* all media but the target were successfully deleted by
8330 * VDMerge; reparent the last one and uninitialize deleted media. */
8331
8332 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
8333
8334 if (task.mfMergeForward)
8335 {
8336 /* first, unregister the target since it may become a base
8337 * medium which needs re-registration */
8338 rc2 = m->pVirtualBox->i_unregisterMedium(pTarget);
8339 AssertComRC(rc2);
8340
8341 /* then, reparent it and disconnect the deleted branch at both ends
8342 * (chain->parent() is source's parent). Depth check above. */
8343 pTarget->i_deparent();
8344 pTarget->i_setParent(task.mParentForTarget);
8345 if (task.mParentForTarget)
8346 i_deparent();
8347
8348 /* then, register again */
8349 ComObjPtr<Medium> pMedium;
8350 rc2 = m->pVirtualBox->i_registerMedium(pTarget, &pMedium,
8351 treeLock);
8352 AssertComRC(rc2);
8353 }
8354 else
8355 {
8356 Assert(pTarget->i_getChildren().size() == 1);
8357 Medium *targetChild = pTarget->i_getChildren().front();
8358
8359 /* disconnect the deleted branch at the elder end */
8360 targetChild->i_deparent();
8361
8362 /* reparent source's children and disconnect the deleted
8363 * branch at the younger end */
8364 if (task.mpChildrenToReparent)
8365 {
8366 /* obey {parent,child} lock order */
8367 AutoWriteLock sourceLock(this COMMA_LOCKVAL_SRC_POS);
8368
8369 MediumLockList::Base::iterator childrenBegin = task.mpChildrenToReparent->GetBegin();
8370 MediumLockList::Base::iterator childrenEnd = task.mpChildrenToReparent->GetEnd();
8371 for (MediumLockList::Base::iterator it = childrenBegin;
8372 it != childrenEnd;
8373 ++it)
8374 {
8375 Medium *pMedium = it->GetMedium();
8376 AutoWriteLock childLock(pMedium COMMA_LOCKVAL_SRC_POS);
8377
8378 pMedium->i_deparent(); // removes pMedium from source
8379 // no depth check, reduces depth
8380 pMedium->i_setParent(pTarget);
8381 }
8382 }
8383 }
8384
8385 /* unregister and uninitialize all media removed by the merge */
8386 MediumLockList::Base::iterator lockListBegin =
8387 task.mpMediumLockList->GetBegin();
8388 MediumLockList::Base::iterator lockListEnd =
8389 task.mpMediumLockList->GetEnd();
8390 for (MediumLockList::Base::iterator it = lockListBegin;
8391 it != lockListEnd;
8392 )
8393 {
8394 MediumLock &mediumLock = *it;
8395 /* Create a real copy of the medium pointer, as the medium
8396 * lock deletion below would invalidate the referenced object. */
8397 const ComObjPtr<Medium> pMedium = mediumLock.GetMedium();
8398
8399 /* The target and all media not merged (readonly) are skipped */
8400 if ( pMedium == pTarget
8401 || pMedium->m->state == MediumState_LockedRead)
8402 {
8403 ++it;
8404 continue;
8405 }
8406
8407 rc2 = pMedium->m->pVirtualBox->i_unregisterMedium(pMedium);
8408 AssertComRC(rc2);
8409
8410 /* now, uninitialize the deleted medium (note that
8411 * due to the Deleting state, uninit() will not touch
8412 * the parent-child relationship so we need to
8413 * uninitialize each disk individually) */
8414
8415 /* note that the operation initiator medium (which is
8416 * normally also the source medium) is a special case
8417 * -- there is one more caller added by Task to it which
8418 * we must release. Also, if we are in sync mode, the
8419 * caller may still hold an AutoCaller instance for it
8420 * and therefore we cannot uninit() it (it's therefore
8421 * the caller's responsibility) */
8422 if (pMedium == this)
8423 {
8424 Assert(i_getChildren().size() == 0);
8425 Assert(m->backRefs.size() == 0);
8426 task.mMediumCaller.release();
8427 }
8428
8429 /* Delete the medium lock list entry, which also releases the
8430 * caller added by MergeChain before uninit() and updates the
8431 * iterator to point to the right place. */
8432 rc2 = task.mpMediumLockList->RemoveByIterator(it);
8433 AssertComRC(rc2);
8434
8435 if (task.isAsync() || pMedium != this)
8436 {
8437 treeLock.release();
8438 pMedium->uninit();
8439 treeLock.acquire();
8440 }
8441 }
8442 }
8443
8444 i_markRegistriesModified();
8445 if (task.isAsync())
8446 {
8447 // in asynchronous mode, save settings now
8448 eik.restore();
8449 m->pVirtualBox->i_saveModifiedRegistries();
8450 eik.fetch();
8451 }
8452
8453 if (FAILED(mrc))
8454 {
8455 /* Here we come if either VDMerge() failed (in which case we
8456 * assume that it tried to do everything to make a further
8457 * retry possible -- e.g. not deleted intermediate media
8458 * and so on) or VirtualBox::saveRegistries() failed (where we
8459 * should have the original tree but with intermediate storage
8460 * units deleted by VDMerge()). We have to only restore states
8461 * (through the MergeChain dtor) unless we are run synchronously
8462 * in which case it's the responsibility of the caller as stated
8463 * in the mergeTo() docs. The latter also implies that we
8464 * don't own the merge chain, so release it in this case. */
8465 if (task.isAsync())
8466 i_cancelMergeTo(task.mpChildrenToReparent, task.mpMediumLockList);
8467 }
8468
8469 return mrc;
8470}
8471
8472/**
8473 * Implementation code for the "clone" task.
8474 *
8475 * This only gets started from Medium::CloneTo() and always runs asynchronously.
8476 * As a result, we always save the VirtualBox.xml file when we're done here.
8477 *
8478 * @param task
8479 * @return
8480 */
8481HRESULT Medium::i_taskCloneHandler(Medium::CloneTask &task)
8482{
8483 /** @todo r=klaus The code below needs to be double checked with regard
8484 * to lock order violations, it probably causes lock order issues related
8485 * to the AutoCaller usage. */
8486 HRESULT rcTmp = S_OK;
8487
8488 const ComObjPtr<Medium> &pTarget = task.mTarget;
8489 const ComObjPtr<Medium> &pParent = task.mParent;
8490
8491 bool fCreatingTarget = false;
8492
8493 uint64_t size = 0, logicalSize = 0;
8494 MediumVariant_T variant = MediumVariant_Standard;
8495 bool fGenerateUuid = false;
8496
8497 try
8498 {
8499 if (!pParent.isNull())
8500 {
8501
8502 if (pParent->i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX)
8503 {
8504 AutoReadLock plock(pParent COMMA_LOCKVAL_SRC_POS);
8505 throw setError(VBOX_E_INVALID_OBJECT_STATE,
8506 tr("Cannot clone image for medium '%s', because it exceeds the medium tree depth limit. Please merge some images which you no longer need"),
8507 pParent->m->strLocationFull.c_str());
8508 }
8509 }
8510
8511 /* Lock all in {parent,child} order. The lock is also used as a
8512 * signal from the task initiator (which releases it only after
8513 * RTThreadCreate()) that we can start the job. */
8514 AutoMultiWriteLock3 thisLock(this, pTarget, pParent COMMA_LOCKVAL_SRC_POS);
8515
8516 fCreatingTarget = pTarget->m->state == MediumState_Creating;
8517
8518 /* The object may request a specific UUID (through a special form of
8519 * the setLocation() argument). Otherwise we have to generate it */
8520 Guid targetId = pTarget->m->id;
8521
8522 fGenerateUuid = targetId.isZero();
8523 if (fGenerateUuid)
8524 {
8525 targetId.create();
8526 /* VirtualBox::registerMedium() will need UUID */
8527 unconst(pTarget->m->id) = targetId;
8528 }
8529
8530 PVBOXHDD hdd;
8531 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
8532 ComAssertRCThrow(vrc, E_FAIL);
8533
8534 try
8535 {
8536 /* Open all media in the source chain. */
8537 MediumLockList::Base::const_iterator sourceListBegin =
8538 task.mpSourceMediumLockList->GetBegin();
8539 MediumLockList::Base::const_iterator sourceListEnd =
8540 task.mpSourceMediumLockList->GetEnd();
8541 for (MediumLockList::Base::const_iterator it = sourceListBegin;
8542 it != sourceListEnd;
8543 ++it)
8544 {
8545 const MediumLock &mediumLock = *it;
8546 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
8547 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
8548
8549 /* sanity check */
8550 Assert(pMedium->m->state == MediumState_LockedRead);
8551
8552 /** Open all media in read-only mode. */
8553 vrc = VDOpen(hdd,
8554 pMedium->m->strFormat.c_str(),
8555 pMedium->m->strLocationFull.c_str(),
8556 VD_OPEN_FLAGS_READONLY | m->uOpenFlagsDef,
8557 pMedium->m->vdImageIfaces);
8558 if (RT_FAILURE(vrc))
8559 throw setError(VBOX_E_FILE_ERROR,
8560 tr("Could not open the medium storage unit '%s'%s"),
8561 pMedium->m->strLocationFull.c_str(),
8562 i_vdError(vrc).c_str());
8563 }
8564
8565 Utf8Str targetFormat(pTarget->m->strFormat);
8566 Utf8Str targetLocation(pTarget->m->strLocationFull);
8567 uint64_t capabilities = pTarget->m->formatObj->i_getCapabilities();
8568
8569 Assert( pTarget->m->state == MediumState_Creating
8570 || pTarget->m->state == MediumState_LockedWrite);
8571 Assert(m->state == MediumState_LockedRead);
8572 Assert( pParent.isNull()
8573 || pParent->m->state == MediumState_LockedRead);
8574
8575 /* unlock before the potentially lengthy operation */
8576 thisLock.release();
8577
8578 /* ensure the target directory exists */
8579 if (capabilities & MediumFormatCapabilities_File)
8580 {
8581 HRESULT rc = VirtualBox::i_ensureFilePathExists(targetLocation,
8582 !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
8583 if (FAILED(rc))
8584 throw rc;
8585 }
8586
8587 PVBOXHDD targetHdd;
8588 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &targetHdd);
8589 ComAssertRCThrow(vrc, E_FAIL);
8590
8591 try
8592 {
8593 /* Open all media in the target chain. */
8594 MediumLockList::Base::const_iterator targetListBegin =
8595 task.mpTargetMediumLockList->GetBegin();
8596 MediumLockList::Base::const_iterator targetListEnd =
8597 task.mpTargetMediumLockList->GetEnd();
8598 for (MediumLockList::Base::const_iterator it = targetListBegin;
8599 it != targetListEnd;
8600 ++it)
8601 {
8602 const MediumLock &mediumLock = *it;
8603 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
8604
8605 /* If the target medium is not created yet there's no
8606 * reason to open it. */
8607 if (pMedium == pTarget && fCreatingTarget)
8608 continue;
8609
8610 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
8611
8612 /* sanity check */
8613 Assert( pMedium->m->state == MediumState_LockedRead
8614 || pMedium->m->state == MediumState_LockedWrite);
8615
8616 unsigned uOpenFlags = VD_OPEN_FLAGS_NORMAL;
8617 if (pMedium->m->state != MediumState_LockedWrite)
8618 uOpenFlags = VD_OPEN_FLAGS_READONLY;
8619 if (pMedium->m->type == MediumType_Shareable)
8620 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
8621
8622 /* Open all media in appropriate mode. */
8623 vrc = VDOpen(targetHdd,
8624 pMedium->m->strFormat.c_str(),
8625 pMedium->m->strLocationFull.c_str(),
8626 uOpenFlags | m->uOpenFlagsDef,
8627 pMedium->m->vdImageIfaces);
8628 if (RT_FAILURE(vrc))
8629 throw setError(VBOX_E_FILE_ERROR,
8630 tr("Could not open the medium storage unit '%s'%s"),
8631 pMedium->m->strLocationFull.c_str(),
8632 i_vdError(vrc).c_str());
8633 }
8634
8635 /* target isn't locked, but no changing data is accessed */
8636 if (task.midxSrcImageSame == UINT32_MAX)
8637 {
8638 vrc = VDCopy(hdd,
8639 VD_LAST_IMAGE,
8640 targetHdd,
8641 targetFormat.c_str(),
8642 (fCreatingTarget) ? targetLocation.c_str() : (char *)NULL,
8643 false /* fMoveByRename */,
8644 0 /* cbSize */,
8645 task.mVariant & ~MediumVariant_NoCreateDir,
8646 targetId.raw(),
8647 VD_OPEN_FLAGS_NORMAL | m->uOpenFlagsDef,
8648 NULL /* pVDIfsOperation */,
8649 pTarget->m->vdImageIfaces,
8650 task.mVDOperationIfaces);
8651 }
8652 else
8653 {
8654 vrc = VDCopyEx(hdd,
8655 VD_LAST_IMAGE,
8656 targetHdd,
8657 targetFormat.c_str(),
8658 (fCreatingTarget) ? targetLocation.c_str() : (char *)NULL,
8659 false /* fMoveByRename */,
8660 0 /* cbSize */,
8661 task.midxSrcImageSame,
8662 task.midxDstImageSame,
8663 task.mVariant & ~MediumVariant_NoCreateDir,
8664 targetId.raw(),
8665 VD_OPEN_FLAGS_NORMAL | m->uOpenFlagsDef,
8666 NULL /* pVDIfsOperation */,
8667 pTarget->m->vdImageIfaces,
8668 task.mVDOperationIfaces);
8669 }
8670 if (RT_FAILURE(vrc))
8671 throw setError(VBOX_E_FILE_ERROR,
8672 tr("Could not create the clone medium '%s'%s"),
8673 targetLocation.c_str(), i_vdError(vrc).c_str());
8674
8675 size = VDGetFileSize(targetHdd, VD_LAST_IMAGE);
8676 logicalSize = VDGetSize(targetHdd, VD_LAST_IMAGE);
8677 unsigned uImageFlags;
8678 vrc = VDGetImageFlags(targetHdd, 0, &uImageFlags);
8679 if (RT_SUCCESS(vrc))
8680 variant = (MediumVariant_T)uImageFlags;
8681 }
8682 catch (HRESULT aRC) { rcTmp = aRC; }
8683
8684 VDDestroy(targetHdd);
8685 }
8686 catch (HRESULT aRC) { rcTmp = aRC; }
8687
8688 VDDestroy(hdd);
8689 }
8690 catch (HRESULT aRC) { rcTmp = aRC; }
8691
8692 ErrorInfoKeeper eik;
8693 MultiResult mrc(rcTmp);
8694
8695 /* Only do the parent changes for newly created media. */
8696 if (SUCCEEDED(mrc) && fCreatingTarget)
8697 {
8698 /* we set m->pParent & children() */
8699 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
8700
8701 Assert(pTarget->m->pParent.isNull());
8702
8703 if (pParent)
8704 {
8705 /* Associate the clone with the parent and deassociate
8706 * from VirtualBox. Depth check above. */
8707 pTarget->i_setParent(pParent);
8708
8709 /* register with mVirtualBox as the last step and move to
8710 * Created state only on success (leaving an orphan file is
8711 * better than breaking media registry consistency) */
8712 eik.restore();
8713 ComObjPtr<Medium> pMedium;
8714 mrc = pParent->m->pVirtualBox->i_registerMedium(pTarget, &pMedium,
8715 treeLock);
8716 Assert( FAILED(mrc)
8717 || pTarget == pMedium);
8718 eik.fetch();
8719
8720 if (FAILED(mrc))
8721 /* break parent association on failure to register */
8722 pTarget->i_deparent(); // removes target from parent
8723 }
8724 else
8725 {
8726 /* just register */
8727 eik.restore();
8728 ComObjPtr<Medium> pMedium;
8729 mrc = m->pVirtualBox->i_registerMedium(pTarget, &pMedium,
8730 treeLock);
8731 Assert( FAILED(mrc)
8732 || pTarget == pMedium);
8733 eik.fetch();
8734 }
8735 }
8736
8737 if (fCreatingTarget)
8738 {
8739 AutoWriteLock mLock(pTarget COMMA_LOCKVAL_SRC_POS);
8740
8741 if (SUCCEEDED(mrc))
8742 {
8743 pTarget->m->state = MediumState_Created;
8744
8745 pTarget->m->size = size;
8746 pTarget->m->logicalSize = logicalSize;
8747 pTarget->m->variant = variant;
8748 }
8749 else
8750 {
8751 /* back to NotCreated on failure */
8752 pTarget->m->state = MediumState_NotCreated;
8753
8754 /* reset UUID to prevent it from being reused next time */
8755 if (fGenerateUuid)
8756 unconst(pTarget->m->id).clear();
8757 }
8758 }
8759
8760 /* Copy any filter related settings over to the target. */
8761 if (SUCCEEDED(mrc))
8762 {
8763 /* Copy any filter related settings over. */
8764 ComObjPtr<Medium> pBase = i_getBase();
8765 ComObjPtr<Medium> pTargetBase = pTarget->i_getBase();
8766 std::vector<com::Utf8Str> aFilterPropNames;
8767 std::vector<com::Utf8Str> aFilterPropValues;
8768 mrc = pBase->i_getFilterProperties(aFilterPropNames, aFilterPropValues);
8769 if (SUCCEEDED(mrc))
8770 {
8771 /* Go through the properties and add them to the target medium. */
8772 for (unsigned idx = 0; idx < aFilterPropNames.size(); idx++)
8773 {
8774 mrc = pTargetBase->i_setPropertyDirect(aFilterPropNames[idx], aFilterPropValues[idx]);
8775 if (FAILED(mrc)) break;
8776 }
8777
8778 // now, at the end of this task (always asynchronous), save the settings
8779 if (SUCCEEDED(mrc))
8780 {
8781 // save the settings
8782 i_markRegistriesModified();
8783 /* collect multiple errors */
8784 eik.restore();
8785 m->pVirtualBox->i_saveModifiedRegistries();
8786 eik.fetch();
8787 }
8788 }
8789 }
8790
8791 /* Everything is explicitly unlocked when the task exits,
8792 * as the task destruction also destroys the source chain. */
8793
8794 /* Make sure the source chain is released early. It could happen
8795 * that we get a deadlock in Appliance::Import when Medium::Close
8796 * is called & the source chain is released at the same time. */
8797 task.mpSourceMediumLockList->Clear();
8798
8799 return mrc;
8800}
8801
8802/**
8803 * Implementation code for the "move" task.
8804 *
8805 * This only gets started from Medium::SetLocation() and always
8806 * runs asynchronously.
8807 *
8808 * @param task
8809 * @return
8810 */
8811HRESULT Medium::i_taskMoveHandler(Medium::MoveTask &task)
8812{
8813
8814 HRESULT rcOut = S_OK;
8815
8816 /* pTarget is equal "this" in our case */
8817 const ComObjPtr<Medium> &pTarget = task.mMedium;
8818
8819 uint64_t size = 0; NOREF(size);
8820 uint64_t logicalSize = 0; NOREF(logicalSize);
8821 MediumVariant_T variant = MediumVariant_Standard; NOREF(variant);
8822
8823 /*
8824 * it's exactly moving, not cloning
8825 */
8826 if (!i_isMoveOperation(pTarget))
8827 {
8828 HRESULT rc = setError(VBOX_E_FILE_ERROR,
8829 tr("Wrong preconditions for moving the medium %s"),
8830 pTarget->m->strLocationFull.c_str());
8831 return rc;
8832 }
8833
8834 try
8835 {
8836 /* Lock all in {parent,child} order. The lock is also used as a
8837 * signal from the task initiator (which releases it only after
8838 * RTThreadCreate()) that we can start the job. */
8839
8840 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
8841
8842 PVBOXHDD hdd;
8843 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
8844 ComAssertRCThrow(vrc, E_FAIL);
8845
8846 try
8847 {
8848 /* Open all media in the source chain. */
8849 MediumLockList::Base::const_iterator sourceListBegin =
8850 task.mpMediumLockList->GetBegin();
8851 MediumLockList::Base::const_iterator sourceListEnd =
8852 task.mpMediumLockList->GetEnd();
8853 for (MediumLockList::Base::const_iterator it = sourceListBegin;
8854 it != sourceListEnd;
8855 ++it)
8856 {
8857 const MediumLock &mediumLock = *it;
8858 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
8859 AutoWriteLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
8860
8861 /* sanity check */
8862 Assert(pMedium->m->state == MediumState_LockedWrite);
8863
8864 vrc = VDOpen(hdd,
8865 pMedium->m->strFormat.c_str(),
8866 pMedium->m->strLocationFull.c_str(),
8867 VD_OPEN_FLAGS_NORMAL,
8868 pMedium->m->vdImageIfaces);
8869 if (RT_FAILURE(vrc))
8870 throw setError(VBOX_E_FILE_ERROR,
8871 tr("Could not open the medium storage unit '%s'%s"),
8872 pMedium->m->strLocationFull.c_str(),
8873 i_vdError(vrc).c_str());
8874 }
8875
8876 /* we can directly use pTarget->m->"variables" but for better reading we use local copies */
8877 Guid targetId = pTarget->m->id;
8878 Utf8Str targetFormat(pTarget->m->strFormat);
8879 uint64_t targetCapabilities = pTarget->m->formatObj->i_getCapabilities();
8880
8881 /*
8882 * change target location
8883 * m->strNewLocationFull has been set already together with m->fMoveThisMedium in
8884 * i_preparationForMoving()
8885 */
8886 Utf8Str targetLocation = i_getNewLocationForMoving();
8887
8888 /* unlock before the potentially lengthy operation */
8889 thisLock.release();
8890
8891 /* ensure the target directory exists */
8892 if (targetCapabilities & MediumFormatCapabilities_File)
8893 {
8894 HRESULT rc = VirtualBox::i_ensureFilePathExists(targetLocation,
8895 !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
8896 if (FAILED(rc))
8897 throw rc;
8898 }
8899
8900 try
8901 {
8902 vrc = VDCopy(hdd,
8903 VD_LAST_IMAGE,
8904 hdd,
8905 targetFormat.c_str(),
8906 targetLocation.c_str(),
8907 true /* fMoveByRename */,
8908 0 /* cbSize */,
8909 VD_IMAGE_FLAGS_NONE,
8910 targetId.raw(),
8911 VD_OPEN_FLAGS_NORMAL,
8912 NULL /* pVDIfsOperation */,
8913 NULL,
8914 NULL);
8915 if (RT_FAILURE(vrc))
8916 throw setError(VBOX_E_FILE_ERROR,
8917 tr("Could not move medium '%s'%s"),
8918 targetLocation.c_str(), i_vdError(vrc).c_str());
8919 size = VDGetFileSize(hdd, VD_LAST_IMAGE);
8920 logicalSize = VDGetSize(hdd, VD_LAST_IMAGE);
8921 unsigned uImageFlags;
8922 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
8923 if (RT_SUCCESS(vrc))
8924 variant = (MediumVariant_T)uImageFlags;
8925
8926 /*
8927 * set current location, because VDCopy\VDCopyEx doesn't do it.
8928 * also reset moving flag
8929 */
8930 i_resetMoveOperationData();
8931 m->strLocationFull = targetLocation;
8932
8933 }
8934 catch (HRESULT aRC) { rcOut = aRC; }
8935
8936 }
8937 catch (HRESULT aRC) { rcOut = aRC; }
8938
8939 VDDestroy(hdd);
8940 }
8941 catch (HRESULT aRC) { rcOut = aRC; }
8942
8943 ErrorInfoKeeper eik;
8944 MultiResult mrc(rcOut);
8945
8946 // now, at the end of this task (always asynchronous), save the settings
8947 if (SUCCEEDED(mrc))
8948 {
8949 // save the settings
8950 i_markRegistriesModified();
8951 /* collect multiple errors */
8952 eik.restore();
8953 m->pVirtualBox->i_saveModifiedRegistries();
8954 eik.fetch();
8955 }
8956
8957 /* Everything is explicitly unlocked when the task exits,
8958 * as the task destruction also destroys the source chain. */
8959
8960 task.mpMediumLockList->Clear();
8961
8962 return mrc;
8963}
8964
8965/**
8966 * Implementation code for the "delete" task.
8967 *
8968 * This task always gets started from Medium::deleteStorage() and can run
8969 * synchronously or asynchronously depending on the "wait" parameter passed to
8970 * that function.
8971 *
8972 * @param task
8973 * @return
8974 */
8975HRESULT Medium::i_taskDeleteHandler(Medium::DeleteTask &task)
8976{
8977 NOREF(task);
8978 HRESULT rc = S_OK;
8979
8980 try
8981 {
8982 /* The lock is also used as a signal from the task initiator (which
8983 * releases it only after RTThreadCreate()) that we can start the job */
8984 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
8985
8986 PVBOXHDD hdd;
8987 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
8988 ComAssertRCThrow(vrc, E_FAIL);
8989
8990 Utf8Str format(m->strFormat);
8991 Utf8Str location(m->strLocationFull);
8992
8993 /* unlock before the potentially lengthy operation */
8994 Assert(m->state == MediumState_Deleting);
8995 thisLock.release();
8996
8997 try
8998 {
8999 vrc = VDOpen(hdd,
9000 format.c_str(),
9001 location.c_str(),
9002 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
9003 m->vdImageIfaces);
9004 if (RT_SUCCESS(vrc))
9005 vrc = VDClose(hdd, true /* fDelete */);
9006
9007 if (RT_FAILURE(vrc))
9008 throw setError(VBOX_E_FILE_ERROR,
9009 tr("Could not delete the medium storage unit '%s'%s"),
9010 location.c_str(), i_vdError(vrc).c_str());
9011
9012 }
9013 catch (HRESULT aRC) { rc = aRC; }
9014
9015 VDDestroy(hdd);
9016 }
9017 catch (HRESULT aRC) { rc = aRC; }
9018
9019 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
9020
9021 /* go to the NotCreated state even on failure since the storage
9022 * may have been already partially deleted and cannot be used any
9023 * more. One will be able to manually re-open the storage if really
9024 * needed to re-register it. */
9025 m->state = MediumState_NotCreated;
9026
9027 /* Reset UUID to prevent Create* from reusing it again */
9028 unconst(m->id).clear();
9029
9030 return rc;
9031}
9032
9033/**
9034 * Implementation code for the "reset" task.
9035 *
9036 * This always gets started asynchronously from Medium::Reset().
9037 *
9038 * @param task
9039 * @return
9040 */
9041HRESULT Medium::i_taskResetHandler(Medium::ResetTask &task)
9042{
9043 HRESULT rc = S_OK;
9044
9045 uint64_t size = 0, logicalSize = 0;
9046 MediumVariant_T variant = MediumVariant_Standard;
9047
9048 try
9049 {
9050 /* The lock is also used as a signal from the task initiator (which
9051 * releases it only after RTThreadCreate()) that we can start the job */
9052 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
9053
9054 /// @todo Below we use a pair of delete/create operations to reset
9055 /// the diff contents but the most efficient way will of course be
9056 /// to add a VDResetDiff() API call
9057
9058 PVBOXHDD hdd;
9059 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
9060 ComAssertRCThrow(vrc, E_FAIL);
9061
9062 Guid id = m->id;
9063 Utf8Str format(m->strFormat);
9064 Utf8Str location(m->strLocationFull);
9065
9066 Medium *pParent = m->pParent;
9067 Guid parentId = pParent->m->id;
9068 Utf8Str parentFormat(pParent->m->strFormat);
9069 Utf8Str parentLocation(pParent->m->strLocationFull);
9070
9071 Assert(m->state == MediumState_LockedWrite);
9072
9073 /* unlock before the potentially lengthy operation */
9074 thisLock.release();
9075
9076 try
9077 {
9078 /* Open all media in the target chain but the last. */
9079 MediumLockList::Base::const_iterator targetListBegin =
9080 task.mpMediumLockList->GetBegin();
9081 MediumLockList::Base::const_iterator targetListEnd =
9082 task.mpMediumLockList->GetEnd();
9083 for (MediumLockList::Base::const_iterator it = targetListBegin;
9084 it != targetListEnd;
9085 ++it)
9086 {
9087 const MediumLock &mediumLock = *it;
9088 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
9089
9090 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
9091
9092 /* sanity check, "this" is checked above */
9093 Assert( pMedium == this
9094 || pMedium->m->state == MediumState_LockedRead);
9095
9096 /* Open all media in appropriate mode. */
9097 vrc = VDOpen(hdd,
9098 pMedium->m->strFormat.c_str(),
9099 pMedium->m->strLocationFull.c_str(),
9100 VD_OPEN_FLAGS_READONLY | m->uOpenFlagsDef,
9101 pMedium->m->vdImageIfaces);
9102 if (RT_FAILURE(vrc))
9103 throw setError(VBOX_E_FILE_ERROR,
9104 tr("Could not open the medium storage unit '%s'%s"),
9105 pMedium->m->strLocationFull.c_str(),
9106 i_vdError(vrc).c_str());
9107
9108 /* Done when we hit the media which should be reset */
9109 if (pMedium == this)
9110 break;
9111 }
9112
9113 /* first, delete the storage unit */
9114 vrc = VDClose(hdd, true /* fDelete */);
9115 if (RT_FAILURE(vrc))
9116 throw setError(VBOX_E_FILE_ERROR,
9117 tr("Could not delete the medium storage unit '%s'%s"),
9118 location.c_str(), i_vdError(vrc).c_str());
9119
9120 /* next, create it again */
9121 vrc = VDOpen(hdd,
9122 parentFormat.c_str(),
9123 parentLocation.c_str(),
9124 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
9125 m->vdImageIfaces);
9126 if (RT_FAILURE(vrc))
9127 throw setError(VBOX_E_FILE_ERROR,
9128 tr("Could not open the medium storage unit '%s'%s"),
9129 parentLocation.c_str(), i_vdError(vrc).c_str());
9130
9131 vrc = VDCreateDiff(hdd,
9132 format.c_str(),
9133 location.c_str(),
9134 /// @todo use the same medium variant as before
9135 VD_IMAGE_FLAGS_NONE,
9136 NULL,
9137 id.raw(),
9138 parentId.raw(),
9139 VD_OPEN_FLAGS_NORMAL,
9140 m->vdImageIfaces,
9141 task.mVDOperationIfaces);
9142 if (RT_FAILURE(vrc))
9143 {
9144 if (vrc == VERR_VD_INVALID_TYPE)
9145 throw setError(VBOX_E_FILE_ERROR,
9146 tr("Parameters for creating the differencing medium storage unit '%s' are invalid%s"),
9147 location.c_str(), i_vdError(vrc).c_str());
9148 else
9149 throw setError(VBOX_E_FILE_ERROR,
9150 tr("Could not create the differencing medium storage unit '%s'%s"),
9151 location.c_str(), i_vdError(vrc).c_str());
9152 }
9153
9154 size = VDGetFileSize(hdd, VD_LAST_IMAGE);
9155 logicalSize = VDGetSize(hdd, VD_LAST_IMAGE);
9156 unsigned uImageFlags;
9157 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
9158 if (RT_SUCCESS(vrc))
9159 variant = (MediumVariant_T)uImageFlags;
9160 }
9161 catch (HRESULT aRC) { rc = aRC; }
9162
9163 VDDestroy(hdd);
9164 }
9165 catch (HRESULT aRC) { rc = aRC; }
9166
9167 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
9168
9169 m->size = size;
9170 m->logicalSize = logicalSize;
9171 m->variant = variant;
9172
9173 /* Everything is explicitly unlocked when the task exits,
9174 * as the task destruction also destroys the media chain. */
9175
9176 return rc;
9177}
9178
9179/**
9180 * Implementation code for the "compact" task.
9181 *
9182 * @param task
9183 * @return
9184 */
9185HRESULT Medium::i_taskCompactHandler(Medium::CompactTask &task)
9186{
9187 HRESULT rc = S_OK;
9188
9189 /* Lock all in {parent,child} order. The lock is also used as a
9190 * signal from the task initiator (which releases it only after
9191 * RTThreadCreate()) that we can start the job. */
9192 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
9193
9194 try
9195 {
9196 PVBOXHDD hdd;
9197 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
9198 ComAssertRCThrow(vrc, E_FAIL);
9199
9200 try
9201 {
9202 /* Open all media in the chain. */
9203 MediumLockList::Base::const_iterator mediumListBegin =
9204 task.mpMediumLockList->GetBegin();
9205 MediumLockList::Base::const_iterator mediumListEnd =
9206 task.mpMediumLockList->GetEnd();
9207 MediumLockList::Base::const_iterator mediumListLast =
9208 mediumListEnd;
9209 --mediumListLast;
9210 for (MediumLockList::Base::const_iterator it = mediumListBegin;
9211 it != mediumListEnd;
9212 ++it)
9213 {
9214 const MediumLock &mediumLock = *it;
9215 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
9216 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
9217
9218 /* sanity check */
9219 if (it == mediumListLast)
9220 Assert(pMedium->m->state == MediumState_LockedWrite);
9221 else
9222 Assert(pMedium->m->state == MediumState_LockedRead);
9223
9224 /* Open all media but last in read-only mode. Do not handle
9225 * shareable media, as compaction and sharing are mutually
9226 * exclusive. */
9227 vrc = VDOpen(hdd,
9228 pMedium->m->strFormat.c_str(),
9229 pMedium->m->strLocationFull.c_str(),
9230 m->uOpenFlagsDef | (it == mediumListLast ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY),
9231 pMedium->m->vdImageIfaces);
9232 if (RT_FAILURE(vrc))
9233 throw setError(VBOX_E_FILE_ERROR,
9234 tr("Could not open the medium storage unit '%s'%s"),
9235 pMedium->m->strLocationFull.c_str(),
9236 i_vdError(vrc).c_str());
9237 }
9238
9239 Assert(m->state == MediumState_LockedWrite);
9240
9241 Utf8Str location(m->strLocationFull);
9242
9243 /* unlock before the potentially lengthy operation */
9244 thisLock.release();
9245
9246 vrc = VDCompact(hdd, VD_LAST_IMAGE, task.mVDOperationIfaces);
9247 if (RT_FAILURE(vrc))
9248 {
9249 if (vrc == VERR_NOT_SUPPORTED)
9250 throw setError(VBOX_E_NOT_SUPPORTED,
9251 tr("Compacting is not yet supported for medium '%s'"),
9252 location.c_str());
9253 else if (vrc == VERR_NOT_IMPLEMENTED)
9254 throw setError(E_NOTIMPL,
9255 tr("Compacting is not implemented, medium '%s'"),
9256 location.c_str());
9257 else
9258 throw setError(VBOX_E_FILE_ERROR,
9259 tr("Could not compact medium '%s'%s"),
9260 location.c_str(),
9261 i_vdError(vrc).c_str());
9262 }
9263 }
9264 catch (HRESULT aRC) { rc = aRC; }
9265
9266 VDDestroy(hdd);
9267 }
9268 catch (HRESULT aRC) { rc = aRC; }
9269
9270 /* Everything is explicitly unlocked when the task exits,
9271 * as the task destruction also destroys the media chain. */
9272
9273 return rc;
9274}
9275
9276/**
9277 * Implementation code for the "resize" task.
9278 *
9279 * @param task
9280 * @return
9281 */
9282HRESULT Medium::i_taskResizeHandler(Medium::ResizeTask &task)
9283{
9284 HRESULT rc = S_OK;
9285
9286 uint64_t size = 0, logicalSize = 0;
9287
9288 try
9289 {
9290 /* The lock is also used as a signal from the task initiator (which
9291 * releases it only after RTThreadCreate()) that we can start the job */
9292 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
9293
9294 PVBOXHDD hdd;
9295 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
9296 ComAssertRCThrow(vrc, E_FAIL);
9297
9298 try
9299 {
9300 /* Open all media in the chain. */
9301 MediumLockList::Base::const_iterator mediumListBegin =
9302 task.mpMediumLockList->GetBegin();
9303 MediumLockList::Base::const_iterator mediumListEnd =
9304 task.mpMediumLockList->GetEnd();
9305 MediumLockList::Base::const_iterator mediumListLast =
9306 mediumListEnd;
9307 --mediumListLast;
9308 for (MediumLockList::Base::const_iterator it = mediumListBegin;
9309 it != mediumListEnd;
9310 ++it)
9311 {
9312 const MediumLock &mediumLock = *it;
9313 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
9314 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
9315
9316 /* sanity check */
9317 if (it == mediumListLast)
9318 Assert(pMedium->m->state == MediumState_LockedWrite);
9319 else
9320 Assert(pMedium->m->state == MediumState_LockedRead);
9321
9322 /* Open all media but last in read-only mode. Do not handle
9323 * shareable media, as compaction and sharing are mutually
9324 * exclusive. */
9325 vrc = VDOpen(hdd,
9326 pMedium->m->strFormat.c_str(),
9327 pMedium->m->strLocationFull.c_str(),
9328 m->uOpenFlagsDef | (it == mediumListLast ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY),
9329 pMedium->m->vdImageIfaces);
9330 if (RT_FAILURE(vrc))
9331 throw setError(VBOX_E_FILE_ERROR,
9332 tr("Could not open the medium storage unit '%s'%s"),
9333 pMedium->m->strLocationFull.c_str(),
9334 i_vdError(vrc).c_str());
9335 }
9336
9337 Assert(m->state == MediumState_LockedWrite);
9338
9339 Utf8Str location(m->strLocationFull);
9340
9341 /* unlock before the potentially lengthy operation */
9342 thisLock.release();
9343
9344 VDGEOMETRY geo = {0, 0, 0}; /* auto */
9345 vrc = VDResize(hdd, task.mSize, &geo, &geo, task.mVDOperationIfaces);
9346 if (RT_FAILURE(vrc))
9347 {
9348 if (vrc == VERR_NOT_SUPPORTED)
9349 throw setError(VBOX_E_NOT_SUPPORTED,
9350 tr("Resizing to new size %llu is not yet supported for medium '%s'"),
9351 task.mSize, location.c_str());
9352 else if (vrc == VERR_NOT_IMPLEMENTED)
9353 throw setError(E_NOTIMPL,
9354 tr("Resiting is not implemented, medium '%s'"),
9355 location.c_str());
9356 else
9357 throw setError(VBOX_E_FILE_ERROR,
9358 tr("Could not resize medium '%s'%s"),
9359 location.c_str(),
9360 i_vdError(vrc).c_str());
9361 }
9362 size = VDGetFileSize(hdd, VD_LAST_IMAGE);
9363 logicalSize = VDGetSize(hdd, VD_LAST_IMAGE);
9364 }
9365 catch (HRESULT aRC) { rc = aRC; }
9366
9367 VDDestroy(hdd);
9368 }
9369 catch (HRESULT aRC) { rc = aRC; }
9370
9371 if (SUCCEEDED(rc))
9372 {
9373 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
9374 m->size = size;
9375 m->logicalSize = logicalSize;
9376 }
9377
9378 /* Everything is explicitly unlocked when the task exits,
9379 * as the task destruction also destroys the media chain. */
9380
9381 return rc;
9382}
9383
9384/**
9385 * Implementation code for the "export" task.
9386 *
9387 * This only gets started from Medium::exportFile() and always runs
9388 * asynchronously. It doesn't touch anything configuration related, so
9389 * we never save the VirtualBox.xml file here.
9390 *
9391 * @param task
9392 * @return
9393 */
9394HRESULT Medium::i_taskExportHandler(Medium::ExportTask &task)
9395{
9396 HRESULT rc = S_OK;
9397
9398 try
9399 {
9400 /* Lock all in {parent,child} order. The lock is also used as a
9401 * signal from the task initiator (which releases it only after
9402 * RTThreadCreate()) that we can start the job. */
9403 ComObjPtr<Medium> pBase = i_getBase();
9404 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
9405
9406 PVBOXHDD hdd;
9407 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
9408 ComAssertRCThrow(vrc, E_FAIL);
9409
9410 try
9411 {
9412 settings::StringsMap::iterator itKeyStore = pBase->m->mapProperties.find("CRYPT/KeyStore");
9413 if (itKeyStore != pBase->m->mapProperties.end())
9414 {
9415 settings::StringsMap::iterator itKeyId = pBase->m->mapProperties.find("CRYPT/KeyId");
9416
9417#ifdef VBOX_WITH_EXTPACK
9418 ExtPackManager *pExtPackManager = m->pVirtualBox->i_getExtPackManager();
9419 if (pExtPackManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
9420 {
9421 /* Load the plugin */
9422 Utf8Str strPlugin;
9423 rc = pExtPackManager->i_getLibraryPathForExtPack(g_szVDPlugin, ORACLE_PUEL_EXTPACK_NAME, &strPlugin);
9424 if (SUCCEEDED(rc))
9425 {
9426 vrc = VDPluginLoadFromFilename(strPlugin.c_str());
9427 if (RT_FAILURE(vrc))
9428 throw setError(VBOX_E_NOT_SUPPORTED,
9429 tr("Retrieving encryption settings of the image failed because the encryption plugin could not be loaded (%s)"),
9430 i_vdError(vrc).c_str());
9431 }
9432 else
9433 throw setError(VBOX_E_NOT_SUPPORTED,
9434 tr("Encryption is not supported because the extension pack '%s' is missing the encryption plugin (old extension pack installed?)"),
9435 ORACLE_PUEL_EXTPACK_NAME);
9436 }
9437 else
9438 throw setError(VBOX_E_NOT_SUPPORTED,
9439 tr("Encryption is not supported because the extension pack '%s' is missing"),
9440 ORACLE_PUEL_EXTPACK_NAME);
9441#else
9442 throw setError(VBOX_E_NOT_SUPPORTED,
9443 tr("Encryption is not supported because extension pack support is not built in"));
9444#endif
9445
9446 if (itKeyId == pBase->m->mapProperties.end())
9447 throw setError(VBOX_E_INVALID_OBJECT_STATE,
9448 tr("Image '%s' is configured for encryption but doesn't has a key identifier set"),
9449 pBase->m->strLocationFull.c_str());
9450
9451 /* Find the proper secret key in the key store. */
9452 if (!task.m_pSecretKeyStore)
9453 throw setError(VBOX_E_INVALID_OBJECT_STATE,
9454 tr("Image '%s' is configured for encryption but there is no key store to retrieve the password from"),
9455 pBase->m->strLocationFull.c_str());
9456
9457 SecretKey *pKey = NULL;
9458 vrc = task.m_pSecretKeyStore->retainSecretKey(itKeyId->second, &pKey);
9459 if (RT_FAILURE(vrc))
9460 throw setError(VBOX_E_INVALID_OBJECT_STATE,
9461 tr("Failed to retrieve the secret key with ID \"%s\" from the store (%Rrc)"),
9462 itKeyId->second.c_str(), vrc);
9463
9464 Medium::CryptoFilterSettings CryptoSettingsRead;
9465 i_taskEncryptSettingsSetup(&CryptoSettingsRead, NULL, itKeyStore->second.c_str(), (const char *)pKey->getKeyBuffer(),
9466 false /* fCreateKeyStore */);
9467 vrc = VDFilterAdd(hdd, "CRYPT", VD_FILTER_FLAGS_READ, CryptoSettingsRead.vdFilterIfaces);
9468 if (vrc == VERR_VD_PASSWORD_INCORRECT)
9469 {
9470 task.m_pSecretKeyStore->releaseSecretKey(itKeyId->second);
9471 throw setError(VBOX_E_PASSWORD_INCORRECT,
9472 tr("The password to decrypt the image is incorrect"));
9473 }
9474 else if (RT_FAILURE(vrc))
9475 {
9476 task.m_pSecretKeyStore->releaseSecretKey(itKeyId->second);
9477 throw setError(VBOX_E_INVALID_OBJECT_STATE,
9478 tr("Failed to load the decryption filter: %s"),
9479 i_vdError(vrc).c_str());
9480 }
9481
9482 task.m_pSecretKeyStore->releaseSecretKey(itKeyId->second);
9483 }
9484
9485 /* Open all media in the source chain. */
9486 MediumLockList::Base::const_iterator sourceListBegin =
9487 task.mpSourceMediumLockList->GetBegin();
9488 MediumLockList::Base::const_iterator sourceListEnd =
9489 task.mpSourceMediumLockList->GetEnd();
9490 for (MediumLockList::Base::const_iterator it = sourceListBegin;
9491 it != sourceListEnd;
9492 ++it)
9493 {
9494 const MediumLock &mediumLock = *it;
9495 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
9496 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
9497
9498 /* sanity check */
9499 Assert(pMedium->m->state == MediumState_LockedRead);
9500
9501 /* Open all media in read-only mode. */
9502 vrc = VDOpen(hdd,
9503 pMedium->m->strFormat.c_str(),
9504 pMedium->m->strLocationFull.c_str(),
9505 VD_OPEN_FLAGS_READONLY | m->uOpenFlagsDef,
9506 pMedium->m->vdImageIfaces);
9507 if (RT_FAILURE(vrc))
9508 throw setError(VBOX_E_FILE_ERROR,
9509 tr("Could not open the medium storage unit '%s'%s"),
9510 pMedium->m->strLocationFull.c_str(),
9511 i_vdError(vrc).c_str());
9512 }
9513
9514 Utf8Str targetFormat(task.mFormat->i_getId());
9515 Utf8Str targetLocation(task.mFilename);
9516 uint64_t capabilities = task.mFormat->i_getCapabilities();
9517
9518 Assert(m->state == MediumState_LockedRead);
9519
9520 /* unlock before the potentially lengthy operation */
9521 thisLock.release();
9522
9523 /* ensure the target directory exists */
9524 if (capabilities & MediumFormatCapabilities_File)
9525 {
9526 rc = VirtualBox::i_ensureFilePathExists(targetLocation,
9527 !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
9528 if (FAILED(rc))
9529 throw rc;
9530 }
9531
9532 PVBOXHDD targetHdd;
9533 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &targetHdd);
9534 ComAssertRCThrow(vrc, E_FAIL);
9535
9536 try
9537 {
9538 vrc = VDCopy(hdd,
9539 VD_LAST_IMAGE,
9540 targetHdd,
9541 targetFormat.c_str(),
9542 targetLocation.c_str(),
9543 false /* fMoveByRename */,
9544 0 /* cbSize */,
9545 task.mVariant & ~MediumVariant_NoCreateDir,
9546 NULL /* pDstUuid */,
9547 VD_OPEN_FLAGS_NORMAL | VD_OPEN_FLAGS_SEQUENTIAL,
9548 NULL /* pVDIfsOperation */,
9549 task.mVDImageIfaces,
9550 task.mVDOperationIfaces);
9551 if (RT_FAILURE(vrc))
9552 throw setError(VBOX_E_FILE_ERROR,
9553 tr("Could not create the exported medium '%s'%s"),
9554 targetLocation.c_str(), i_vdError(vrc).c_str());
9555 }
9556 catch (HRESULT aRC) { rc = aRC; }
9557
9558 VDDestroy(targetHdd);
9559 }
9560 catch (HRESULT aRC) { rc = aRC; }
9561
9562 VDDestroy(hdd);
9563 }
9564 catch (HRESULT aRC) { rc = aRC; }
9565
9566 /* Everything is explicitly unlocked when the task exits,
9567 * as the task destruction also destroys the source chain. */
9568
9569 /* Make sure the source chain is released early, otherwise it can
9570 * lead to deadlocks with concurrent IAppliance activities. */
9571 task.mpSourceMediumLockList->Clear();
9572
9573 return rc;
9574}
9575
9576/**
9577 * Implementation code for the "import" task.
9578 *
9579 * This only gets started from Medium::importFile() and always runs
9580 * asynchronously. It potentially touches the media registry, so we
9581 * always save the VirtualBox.xml file when we're done here.
9582 *
9583 * @param task
9584 * @return
9585 */
9586HRESULT Medium::i_taskImportHandler(Medium::ImportTask &task)
9587{
9588 /** @todo r=klaus The code below needs to be double checked with regard
9589 * to lock order violations, it probably causes lock order issues related
9590 * to the AutoCaller usage. */
9591 HRESULT rcTmp = S_OK;
9592
9593 const ComObjPtr<Medium> &pParent = task.mParent;
9594
9595 bool fCreatingTarget = false;
9596
9597 uint64_t size = 0, logicalSize = 0;
9598 MediumVariant_T variant = MediumVariant_Standard;
9599 bool fGenerateUuid = false;
9600
9601 try
9602 {
9603 if (!pParent.isNull())
9604 if (pParent->i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX)
9605 {
9606 AutoReadLock plock(pParent COMMA_LOCKVAL_SRC_POS);
9607 throw setError(VBOX_E_INVALID_OBJECT_STATE,
9608 tr("Cannot import image for medium '%s', because it exceeds the medium tree depth limit. Please merge some images which you no longer need"),
9609 pParent->m->strLocationFull.c_str());
9610 }
9611
9612 /* Lock all in {parent,child} order. The lock is also used as a
9613 * signal from the task initiator (which releases it only after
9614 * RTThreadCreate()) that we can start the job. */
9615 AutoMultiWriteLock2 thisLock(this, pParent COMMA_LOCKVAL_SRC_POS);
9616
9617 fCreatingTarget = m->state == MediumState_Creating;
9618
9619 /* The object may request a specific UUID (through a special form of
9620 * the setLocation() argument). Otherwise we have to generate it */
9621 Guid targetId = m->id;
9622
9623 fGenerateUuid = targetId.isZero();
9624 if (fGenerateUuid)
9625 {
9626 targetId.create();
9627 /* VirtualBox::i_registerMedium() will need UUID */
9628 unconst(m->id) = targetId;
9629 }
9630
9631
9632 PVBOXHDD hdd;
9633 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
9634 ComAssertRCThrow(vrc, E_FAIL);
9635
9636 try
9637 {
9638 /* Open source medium. */
9639 vrc = VDOpen(hdd,
9640 task.mFormat->i_getId().c_str(),
9641 task.mFilename.c_str(),
9642 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SEQUENTIAL | m->uOpenFlagsDef,
9643 task.mVDImageIfaces);
9644 if (RT_FAILURE(vrc))
9645 throw setError(VBOX_E_FILE_ERROR,
9646 tr("Could not open the medium storage unit '%s'%s"),
9647 task.mFilename.c_str(),
9648 i_vdError(vrc).c_str());
9649
9650 Utf8Str targetFormat(m->strFormat);
9651 Utf8Str targetLocation(m->strLocationFull);
9652 uint64_t capabilities = task.mFormat->i_getCapabilities();
9653
9654 Assert( m->state == MediumState_Creating
9655 || m->state == MediumState_LockedWrite);
9656 Assert( pParent.isNull()
9657 || pParent->m->state == MediumState_LockedRead);
9658
9659 /* unlock before the potentially lengthy operation */
9660 thisLock.release();
9661
9662 /* ensure the target directory exists */
9663 if (capabilities & MediumFormatCapabilities_File)
9664 {
9665 HRESULT rc = VirtualBox::i_ensureFilePathExists(targetLocation,
9666 !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
9667 if (FAILED(rc))
9668 throw rc;
9669 }
9670
9671 PVBOXHDD targetHdd;
9672 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &targetHdd);
9673 ComAssertRCThrow(vrc, E_FAIL);
9674
9675 try
9676 {
9677 /* Open all media in the target chain. */
9678 MediumLockList::Base::const_iterator targetListBegin =
9679 task.mpTargetMediumLockList->GetBegin();
9680 MediumLockList::Base::const_iterator targetListEnd =
9681 task.mpTargetMediumLockList->GetEnd();
9682 for (MediumLockList::Base::const_iterator it = targetListBegin;
9683 it != targetListEnd;
9684 ++it)
9685 {
9686 const MediumLock &mediumLock = *it;
9687 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
9688
9689 /* If the target medium is not created yet there's no
9690 * reason to open it. */
9691 if (pMedium == this && fCreatingTarget)
9692 continue;
9693
9694 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
9695
9696 /* sanity check */
9697 Assert( pMedium->m->state == MediumState_LockedRead
9698 || pMedium->m->state == MediumState_LockedWrite);
9699
9700 unsigned uOpenFlags = VD_OPEN_FLAGS_NORMAL;
9701 if (pMedium->m->state != MediumState_LockedWrite)
9702 uOpenFlags = VD_OPEN_FLAGS_READONLY;
9703 if (pMedium->m->type == MediumType_Shareable)
9704 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
9705
9706 /* Open all media in appropriate mode. */
9707 vrc = VDOpen(targetHdd,
9708 pMedium->m->strFormat.c_str(),
9709 pMedium->m->strLocationFull.c_str(),
9710 uOpenFlags | m->uOpenFlagsDef,
9711 pMedium->m->vdImageIfaces);
9712 if (RT_FAILURE(vrc))
9713 throw setError(VBOX_E_FILE_ERROR,
9714 tr("Could not open the medium storage unit '%s'%s"),
9715 pMedium->m->strLocationFull.c_str(),
9716 i_vdError(vrc).c_str());
9717 }
9718
9719 vrc = VDCopy(hdd,
9720 VD_LAST_IMAGE,
9721 targetHdd,
9722 targetFormat.c_str(),
9723 (fCreatingTarget) ? targetLocation.c_str() : (char *)NULL,
9724 false /* fMoveByRename */,
9725 0 /* cbSize */,
9726 task.mVariant & ~MediumVariant_NoCreateDir,
9727 targetId.raw(),
9728 VD_OPEN_FLAGS_NORMAL,
9729 NULL /* pVDIfsOperation */,
9730 m->vdImageIfaces,
9731 task.mVDOperationIfaces);
9732 if (RT_FAILURE(vrc))
9733 throw setError(VBOX_E_FILE_ERROR,
9734 tr("Could not create the imported medium '%s'%s"),
9735 targetLocation.c_str(), i_vdError(vrc).c_str());
9736
9737 size = VDGetFileSize(targetHdd, VD_LAST_IMAGE);
9738 logicalSize = VDGetSize(targetHdd, VD_LAST_IMAGE);
9739 unsigned uImageFlags;
9740 vrc = VDGetImageFlags(targetHdd, 0, &uImageFlags);
9741 if (RT_SUCCESS(vrc))
9742 variant = (MediumVariant_T)uImageFlags;
9743 }
9744 catch (HRESULT aRC) { rcTmp = aRC; }
9745
9746 VDDestroy(targetHdd);
9747 }
9748 catch (HRESULT aRC) { rcTmp = aRC; }
9749
9750 VDDestroy(hdd);
9751 }
9752 catch (HRESULT aRC) { rcTmp = aRC; }
9753
9754 ErrorInfoKeeper eik;
9755 MultiResult mrc(rcTmp);
9756
9757 /* Only do the parent changes for newly created media. */
9758 if (SUCCEEDED(mrc) && fCreatingTarget)
9759 {
9760 /* we set m->pParent & children() */
9761 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
9762
9763 Assert(m->pParent.isNull());
9764
9765 if (pParent)
9766 {
9767 /* Associate the imported medium with the parent and deassociate
9768 * from VirtualBox. Depth check above. */
9769 i_setParent(pParent);
9770
9771 /* register with mVirtualBox as the last step and move to
9772 * Created state only on success (leaving an orphan file is
9773 * better than breaking media registry consistency) */
9774 eik.restore();
9775 ComObjPtr<Medium> pMedium;
9776 mrc = pParent->m->pVirtualBox->i_registerMedium(this, &pMedium,
9777 treeLock);
9778 Assert(this == pMedium);
9779 eik.fetch();
9780
9781 if (FAILED(mrc))
9782 /* break parent association on failure to register */
9783 this->i_deparent(); // removes target from parent
9784 }
9785 else
9786 {
9787 /* just register */
9788 eik.restore();
9789 ComObjPtr<Medium> pMedium;
9790 mrc = m->pVirtualBox->i_registerMedium(this, &pMedium, treeLock);
9791 Assert(this == pMedium);
9792 eik.fetch();
9793 }
9794 }
9795
9796 if (fCreatingTarget)
9797 {
9798 AutoWriteLock mLock(this COMMA_LOCKVAL_SRC_POS);
9799
9800 if (SUCCEEDED(mrc))
9801 {
9802 m->state = MediumState_Created;
9803
9804 m->size = size;
9805 m->logicalSize = logicalSize;
9806 m->variant = variant;
9807 }
9808 else
9809 {
9810 /* back to NotCreated on failure */
9811 m->state = MediumState_NotCreated;
9812
9813 /* reset UUID to prevent it from being reused next time */
9814 if (fGenerateUuid)
9815 unconst(m->id).clear();
9816 }
9817 }
9818
9819 // now, at the end of this task (always asynchronous), save the settings
9820 {
9821 // save the settings
9822 i_markRegistriesModified();
9823 /* collect multiple errors */
9824 eik.restore();
9825 m->pVirtualBox->i_saveModifiedRegistries();
9826 eik.fetch();
9827 }
9828
9829 /* Everything is explicitly unlocked when the task exits,
9830 * as the task destruction also destroys the target chain. */
9831
9832 /* Make sure the target chain is released early, otherwise it can
9833 * lead to deadlocks with concurrent IAppliance activities. */
9834 task.mpTargetMediumLockList->Clear();
9835
9836 return mrc;
9837}
9838
9839/**
9840 * Sets up the encryption settings for a filter.
9841 */
9842void Medium::i_taskEncryptSettingsSetup(CryptoFilterSettings *pSettings, const char *pszCipher,
9843 const char *pszKeyStore, const char *pszPassword,
9844 bool fCreateKeyStore)
9845{
9846 pSettings->pszCipher = pszCipher;
9847 pSettings->pszPassword = pszPassword;
9848 pSettings->pszKeyStoreLoad = pszKeyStore;
9849 pSettings->fCreateKeyStore = fCreateKeyStore;
9850 pSettings->pbDek = NULL;
9851 pSettings->cbDek = 0;
9852 pSettings->vdFilterIfaces = NULL;
9853
9854 pSettings->vdIfCfg.pfnAreKeysValid = i_vdCryptoConfigAreKeysValid;
9855 pSettings->vdIfCfg.pfnQuerySize = i_vdCryptoConfigQuerySize;
9856 pSettings->vdIfCfg.pfnQuery = i_vdCryptoConfigQuery;
9857 pSettings->vdIfCfg.pfnQueryBytes = NULL;
9858
9859 pSettings->vdIfCrypto.pfnKeyRetain = i_vdCryptoKeyRetain;
9860 pSettings->vdIfCrypto.pfnKeyRelease = i_vdCryptoKeyRelease;
9861 pSettings->vdIfCrypto.pfnKeyStorePasswordRetain = i_vdCryptoKeyStorePasswordRetain;
9862 pSettings->vdIfCrypto.pfnKeyStorePasswordRelease = i_vdCryptoKeyStorePasswordRelease;
9863 pSettings->vdIfCrypto.pfnKeyStoreSave = i_vdCryptoKeyStoreSave;
9864 pSettings->vdIfCrypto.pfnKeyStoreReturnParameters = i_vdCryptoKeyStoreReturnParameters;
9865
9866 int vrc = VDInterfaceAdd(&pSettings->vdIfCfg.Core,
9867 "Medium::vdInterfaceCfgCrypto",
9868 VDINTERFACETYPE_CONFIG, pSettings,
9869 sizeof(VDINTERFACECONFIG), &pSettings->vdFilterIfaces);
9870 AssertRC(vrc);
9871
9872 vrc = VDInterfaceAdd(&pSettings->vdIfCrypto.Core,
9873 "Medium::vdInterfaceCrypto",
9874 VDINTERFACETYPE_CRYPTO, pSettings,
9875 sizeof(VDINTERFACECRYPTO), &pSettings->vdFilterIfaces);
9876 AssertRC(vrc);
9877}
9878
9879/**
9880 * Implementation code for the "encrypt" task.
9881 *
9882 * @param task
9883 * @return
9884 */
9885HRESULT Medium::i_taskEncryptHandler(Medium::EncryptTask &task)
9886{
9887 HRESULT rc = S_OK;
9888
9889 /* Lock all in {parent,child} order. The lock is also used as a
9890 * signal from the task initiator (which releases it only after
9891 * RTThreadCreate()) that we can start the job. */
9892 ComObjPtr<Medium> pBase = i_getBase();
9893 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
9894
9895 try
9896 {
9897# ifdef VBOX_WITH_EXTPACK
9898 ExtPackManager *pExtPackManager = m->pVirtualBox->i_getExtPackManager();
9899 if (pExtPackManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
9900 {
9901 /* Load the plugin */
9902 Utf8Str strPlugin;
9903 rc = pExtPackManager->i_getLibraryPathForExtPack(g_szVDPlugin, ORACLE_PUEL_EXTPACK_NAME, &strPlugin);
9904 if (SUCCEEDED(rc))
9905 {
9906 int vrc = VDPluginLoadFromFilename(strPlugin.c_str());
9907 if (RT_FAILURE(vrc))
9908 throw setError(VBOX_E_NOT_SUPPORTED,
9909 tr("Encrypting the image failed because the encryption plugin could not be loaded (%s)"),
9910 i_vdError(vrc).c_str());
9911 }
9912 else
9913 throw setError(VBOX_E_NOT_SUPPORTED,
9914 tr("Encryption is not supported because the extension pack '%s' is missing the encryption plugin (old extension pack installed?)"),
9915 ORACLE_PUEL_EXTPACK_NAME);
9916 }
9917 else
9918 throw setError(VBOX_E_NOT_SUPPORTED,
9919 tr("Encryption is not supported because the extension pack '%s' is missing"),
9920 ORACLE_PUEL_EXTPACK_NAME);
9921
9922 PVBOXHDD pDisk = NULL;
9923 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &pDisk);
9924 ComAssertRCThrow(vrc, E_FAIL);
9925
9926 Medium::CryptoFilterSettings CryptoSettingsRead;
9927 Medium::CryptoFilterSettings CryptoSettingsWrite;
9928
9929 void *pvBuf = NULL;
9930 const char *pszPasswordNew = NULL;
9931 try
9932 {
9933 /* Set up disk encryption filters. */
9934 if (task.mstrCurrentPassword.isEmpty())
9935 {
9936 /*
9937 * Query whether the medium property indicating that encryption is
9938 * configured is existing.
9939 */
9940 settings::StringsMap::iterator it = pBase->m->mapProperties.find("CRYPT/KeyStore");
9941 if (it != pBase->m->mapProperties.end())
9942 throw setError(VBOX_E_PASSWORD_INCORRECT,
9943 tr("The password given for the encrypted image is incorrect"));
9944 }
9945 else
9946 {
9947 settings::StringsMap::iterator it = pBase->m->mapProperties.find("CRYPT/KeyStore");
9948 if (it == pBase->m->mapProperties.end())
9949 throw setError(VBOX_E_INVALID_OBJECT_STATE,
9950 tr("The image is not configured for encryption"));
9951
9952 i_taskEncryptSettingsSetup(&CryptoSettingsRead, NULL, it->second.c_str(), task.mstrCurrentPassword.c_str(),
9953 false /* fCreateKeyStore */);
9954 vrc = VDFilterAdd(pDisk, "CRYPT", VD_FILTER_FLAGS_READ, CryptoSettingsRead.vdFilterIfaces);
9955 if (vrc == VERR_VD_PASSWORD_INCORRECT)
9956 throw setError(VBOX_E_PASSWORD_INCORRECT,
9957 tr("The password to decrypt the image is incorrect"));
9958 else if (RT_FAILURE(vrc))
9959 throw setError(VBOX_E_INVALID_OBJECT_STATE,
9960 tr("Failed to load the decryption filter: %s"),
9961 i_vdError(vrc).c_str());
9962 }
9963
9964 if (task.mstrCipher.isNotEmpty())
9965 {
9966 if ( task.mstrNewPassword.isEmpty()
9967 && task.mstrNewPasswordId.isEmpty()
9968 && task.mstrCurrentPassword.isNotEmpty())
9969 {
9970 /* An empty password and password ID will default to the current password. */
9971 pszPasswordNew = task.mstrCurrentPassword.c_str();
9972 }
9973 else if (task.mstrNewPassword.isEmpty())
9974 throw setError(VBOX_E_OBJECT_NOT_FOUND,
9975 tr("A password must be given for the image encryption"));
9976 else if (task.mstrNewPasswordId.isEmpty())
9977 throw setError(VBOX_E_INVALID_OBJECT_STATE,
9978 tr("A valid identifier for the password must be given"));
9979 else
9980 pszPasswordNew = task.mstrNewPassword.c_str();
9981
9982 i_taskEncryptSettingsSetup(&CryptoSettingsWrite, task.mstrCipher.c_str(), NULL,
9983 pszPasswordNew, true /* fCreateKeyStore */);
9984 vrc = VDFilterAdd(pDisk, "CRYPT", VD_FILTER_FLAGS_WRITE, CryptoSettingsWrite.vdFilterIfaces);
9985 if (RT_FAILURE(vrc))
9986 throw setError(VBOX_E_INVALID_OBJECT_STATE,
9987 tr("Failed to load the encryption filter: %s"),
9988 i_vdError(vrc).c_str());
9989 }
9990 else if (task.mstrNewPasswordId.isNotEmpty() || task.mstrNewPassword.isNotEmpty())
9991 throw setError(VBOX_E_INVALID_OBJECT_STATE,
9992 tr("The password and password identifier must be empty if the output should be unencrypted"));
9993
9994 /* Open all media in the chain. */
9995 MediumLockList::Base::const_iterator mediumListBegin =
9996 task.mpMediumLockList->GetBegin();
9997 MediumLockList::Base::const_iterator mediumListEnd =
9998 task.mpMediumLockList->GetEnd();
9999 MediumLockList::Base::const_iterator mediumListLast =
10000 mediumListEnd;
10001 --mediumListLast;
10002 for (MediumLockList::Base::const_iterator it = mediumListBegin;
10003 it != mediumListEnd;
10004 ++it)
10005 {
10006 const MediumLock &mediumLock = *it;
10007 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
10008 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
10009
10010 Assert(pMedium->m->state == MediumState_LockedWrite);
10011
10012 /* Open all media but last in read-only mode. Do not handle
10013 * shareable media, as compaction and sharing are mutually
10014 * exclusive. */
10015 vrc = VDOpen(pDisk,
10016 pMedium->m->strFormat.c_str(),
10017 pMedium->m->strLocationFull.c_str(),
10018 m->uOpenFlagsDef | (it == mediumListLast ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY),
10019 pMedium->m->vdImageIfaces);
10020 if (RT_FAILURE(vrc))
10021 throw setError(VBOX_E_FILE_ERROR,
10022 tr("Could not open the medium storage unit '%s'%s"),
10023 pMedium->m->strLocationFull.c_str(),
10024 i_vdError(vrc).c_str());
10025 }
10026
10027 Assert(m->state == MediumState_LockedWrite);
10028
10029 Utf8Str location(m->strLocationFull);
10030
10031 /* unlock before the potentially lengthy operation */
10032 thisLock.release();
10033
10034 vrc = VDPrepareWithFilters(pDisk, task.mVDOperationIfaces);
10035 if (RT_FAILURE(vrc))
10036 throw setError(VBOX_E_FILE_ERROR,
10037 tr("Could not prepare disk images for encryption (%Rrc): %s"),
10038 vrc, i_vdError(vrc).c_str());
10039
10040 thisLock.acquire();
10041 /* If everything went well set the new key store. */
10042 settings::StringsMap::iterator it = pBase->m->mapProperties.find("CRYPT/KeyStore");
10043 if (it != pBase->m->mapProperties.end())
10044 pBase->m->mapProperties.erase(it);
10045
10046 /* Delete KeyId if encryption is removed or the password did change. */
10047 if ( task.mstrNewPasswordId.isNotEmpty()
10048 || task.mstrCipher.isEmpty())
10049 {
10050 it = pBase->m->mapProperties.find("CRYPT/KeyId");
10051 if (it != pBase->m->mapProperties.end())
10052 pBase->m->mapProperties.erase(it);
10053 }
10054
10055 if (CryptoSettingsWrite.pszKeyStore)
10056 {
10057 pBase->m->mapProperties["CRYPT/KeyStore"] = Utf8Str(CryptoSettingsWrite.pszKeyStore);
10058 if (task.mstrNewPasswordId.isNotEmpty())
10059 pBase->m->mapProperties["CRYPT/KeyId"] = task.mstrNewPasswordId;
10060 }
10061
10062 if (CryptoSettingsRead.pszCipherReturned)
10063 RTStrFree(CryptoSettingsRead.pszCipherReturned);
10064
10065 if (CryptoSettingsWrite.pszCipherReturned)
10066 RTStrFree(CryptoSettingsWrite.pszCipherReturned);
10067
10068 thisLock.release();
10069 pBase->i_markRegistriesModified();
10070 m->pVirtualBox->i_saveModifiedRegistries();
10071 }
10072 catch (HRESULT aRC) { rc = aRC; }
10073
10074 if (pvBuf)
10075 RTMemFree(pvBuf);
10076
10077 VDDestroy(pDisk);
10078# else
10079 throw setError(VBOX_E_NOT_SUPPORTED,
10080 tr("Encryption is not supported because extension pack support is not built in"));
10081# endif
10082 }
10083 catch (HRESULT aRC) { rc = aRC; }
10084
10085 /* Everything is explicitly unlocked when the task exits,
10086 * as the task destruction also destroys the media chain. */
10087
10088 return rc;
10089}
10090
10091/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

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