VirtualBox

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

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

Main: Add a method to check for the correct encryption password

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

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