VirtualBox

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

Last change on this file since 60952 was 60628, checked in by vboxsync, 9 years ago

Main/Medium: don't skip this important assertion doing sanity checking

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

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