VirtualBox

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

Last change on this file since 54971 was 54950, checked in by vboxsync, 10 years ago

Fix unused variables warnings

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

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