VirtualBox

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

Last change on this file since 61361 was 61174, checked in by vboxsync, 9 years ago

Main/Medium: fix accidentally negated assertion in previous lock order fix

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

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