VirtualBox

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

最後變更 在這個檔案從62918是 62868,由 vboxsync 提交於 9 年 前

bugref:8482. Shifted the function handler() from descendants to the base class Medium::Task.

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

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