VirtualBox

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

Last change on this file since 60034 was 59621, checked in by vboxsync, 9 years ago

ApplianceImplImport.cpp,++: Pushed the IPRT VFS stuff futher down, only going to the VD I/O wrappers when start getting down into the media code during import. Reworked the OVA releated import code to skip files it doesn't care about (like message resource xml files which we don't implement) and not be picky about which order files the OVF, MF and CERT files come in (todo: make sure it isn't picky about the order of disks either). Read the manifest and certificate file during the read() method.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 329.8 KB
Line 
1/* $Id: MediumImpl.cpp 59621 2016-02-10 00:51:35Z vboxsync $ */
2/** @file
3 * VirtualBox COM class implementation
4 */
5
6/*
7 * Copyright (C) 2008-2015 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17#include "MediumImpl.h"
18#include "TokenImpl.h"
19#include "ProgressImpl.h"
20#include "SystemPropertiesImpl.h"
21#include "VirtualBoxImpl.h"
22#include "ExtPackManagerImpl.h"
23
24#include "AutoCaller.h"
25#include "Logging.h"
26
27#include "VBox/com/MultiResult.h"
28#include "VBox/com/ErrorInfo.h"
29
30#include <VBox/err.h>
31#include <VBox/settings.h>
32
33#include <iprt/param.h>
34#include <iprt/path.h>
35#include <iprt/file.h>
36#include <iprt/tcp.h>
37#include <iprt/cpp/utils.h>
38#include <iprt/memsafer.h>
39#include <iprt/base64.h>
40
41#include <VBox/vd.h>
42
43#include <algorithm>
44#include <list>
45
46#include <openssl/rand.h>
47
48typedef std::list<Guid> GuidList;
49
50////////////////////////////////////////////////////////////////////////////////
51//
52// Medium data definition
53//
54////////////////////////////////////////////////////////////////////////////////
55
56/** Describes how a machine refers to this medium. */
57struct BackRef
58{
59 /** Equality predicate for stdc++. */
60 struct EqualsTo : public std::unary_function <BackRef, bool>
61 {
62 explicit EqualsTo(const Guid &aMachineId) : machineId(aMachineId) {}
63
64 bool operator()(const argument_type &aThat) const
65 {
66 return aThat.machineId == machineId;
67 }
68
69 const Guid machineId;
70 };
71
72 BackRef(const Guid &aMachineId,
73 const Guid &aSnapshotId = Guid::Empty)
74 : machineId(aMachineId),
75 fInCurState(aSnapshotId.isZero())
76 {
77 if (aSnapshotId.isValid() && !aSnapshotId.isZero())
78 llSnapshotIds.push_back(aSnapshotId);
79 }
80
81 Guid machineId;
82 bool fInCurState : 1;
83 GuidList llSnapshotIds;
84};
85
86typedef std::list<BackRef> BackRefList;
87
88struct Medium::Data
89{
90 Data()
91 : pVirtualBox(NULL),
92 state(MediumState_NotCreated),
93 variant(MediumVariant_Standard),
94 size(0),
95 readers(0),
96 preLockState(MediumState_NotCreated),
97 queryInfoSem(LOCKCLASS_MEDIUMQUERY),
98 queryInfoRunning(false),
99 type(MediumType_Normal),
100 devType(DeviceType_HardDisk),
101 logicalSize(0),
102 hddOpenMode(OpenReadWrite),
103 autoReset(false),
104 hostDrive(false),
105 implicit(false),
106 fClosing(false),
107 uOpenFlagsDef(VD_OPEN_FLAGS_IGNORE_FLUSH),
108 numCreateDiffTasks(0),
109 vdDiskIfaces(NULL),
110 vdImageIfaces(NULL)
111 { }
112
113 /** weak VirtualBox parent */
114 VirtualBox * const pVirtualBox;
115
116 // pParent and llChildren are protected by VirtualBox::i_getMediaTreeLockHandle()
117 ComObjPtr<Medium> pParent;
118 MediaList llChildren; // to add a child, just call push_back; to remove
119 // a child, call child->deparent() which does a lookup
120
121 GuidList llRegistryIDs; // media registries in which this medium is listed
122
123 const Guid id;
124 Utf8Str strDescription;
125 MediumState_T state;
126 MediumVariant_T variant;
127 Utf8Str strLocationFull;
128 uint64_t size;
129 Utf8Str strLastAccessError;
130
131 BackRefList backRefs;
132
133 size_t readers;
134 MediumState_T preLockState;
135
136 /** Special synchronization for operations which must wait for
137 * Medium::i_queryInfo in another thread to complete. Using a SemRW is
138 * not quite ideal, but at least it is subject to the lock validator,
139 * unlike the SemEventMulti which we had here for many years. Catching
140 * possible deadlocks is more important than a tiny bit of efficiency. */
141 RWLockHandle queryInfoSem;
142 bool queryInfoRunning : 1;
143
144 const Utf8Str strFormat;
145 ComObjPtr<MediumFormat> formatObj;
146
147 MediumType_T type;
148 DeviceType_T devType;
149 uint64_t logicalSize;
150
151 HDDOpenMode hddOpenMode;
152
153 bool autoReset : 1;
154
155 /** New UUID to be set on the next Medium::i_queryInfo call. */
156 const Guid uuidImage;
157 /** New parent UUID to be set on the next Medium::i_queryInfo call. */
158 const Guid uuidParentImage;
159
160 bool hostDrive : 1;
161
162 settings::StringsMap mapProperties;
163
164 bool implicit : 1;
165 /** Flag whether the medium is in the process of being closed. */
166 bool fClosing: 1;
167
168 /** Default flags passed to VDOpen(). */
169 unsigned uOpenFlagsDef;
170
171 uint32_t numCreateDiffTasks;
172
173 Utf8Str vdError; /*< Error remembered by the VD error callback. */
174
175 VDINTERFACEERROR vdIfError;
176
177 VDINTERFACECONFIG vdIfConfig;
178
179 VDINTERFACETCPNET vdIfTcpNet;
180
181 PVDINTERFACE vdDiskIfaces;
182 PVDINTERFACE vdImageIfaces;
183};
184
185typedef struct VDSOCKETINT
186{
187 /** Socket handle. */
188 RTSOCKET hSocket;
189} VDSOCKETINT, *PVDSOCKETINT;
190
191////////////////////////////////////////////////////////////////////////////////
192//
193// Globals
194//
195////////////////////////////////////////////////////////////////////////////////
196
197/**
198 * Medium::Task class for asynchronous operations.
199 *
200 * @note Instances of this class must be created using new() because the
201 * task thread function will delete them when the task is complete.
202 *
203 * @note The constructor of this class adds a caller on the managed Medium
204 * object which is automatically released upon destruction.
205 */
206class Medium::Task
207{
208public:
209 Task(Medium *aMedium, Progress *aProgress)
210 : mVDOperationIfaces(NULL),
211 mMedium(aMedium),
212 mMediumCaller(aMedium),
213 mThread(NIL_RTTHREAD),
214 mProgress(aProgress),
215 mVirtualBoxCaller(NULL)
216 {
217 AssertReturnVoidStmt(aMedium, mRC = E_FAIL);
218 mRC = mMediumCaller.rc();
219 if (FAILED(mRC))
220 return;
221
222 /* Get strong VirtualBox reference, see below. */
223 VirtualBox *pVirtualBox = aMedium->m->pVirtualBox;
224 mVirtualBox = pVirtualBox;
225 mVirtualBoxCaller.attach(pVirtualBox);
226 mRC = mVirtualBoxCaller.rc();
227 if (FAILED(mRC))
228 return;
229
230 /* Set up a per-operation progress interface, can be used freely (for
231 * binary operations you can use it either on the source or target). */
232 mVDIfProgress.pfnProgress = vdProgressCall;
233 int vrc = VDInterfaceAdd(&mVDIfProgress.Core,
234 "Medium::Task::vdInterfaceProgress",
235 VDINTERFACETYPE_PROGRESS,
236 mProgress,
237 sizeof(VDINTERFACEPROGRESS),
238 &mVDOperationIfaces);
239 AssertRC(vrc);
240 if (RT_FAILURE(vrc))
241 mRC = E_FAIL;
242 }
243
244 // Make all destructors virtual. Just in case.
245 virtual ~Task()
246 {}
247
248 HRESULT rc() const { return mRC; }
249 bool isOk() const { return SUCCEEDED(rc()); }
250
251 static 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 true /* fMediumLockWrite */,
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 false /* fMediumLockWrite */,
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 true /* fMediumLockWrite */,
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 true /* fMediumLockWrite */,
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 true /* fMediumLockWrite */,
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 true /* fMediumLockWrite */,
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 true /* fMediumLockWrite */,
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 fMediumLockWrite Whether to associate a write lock with this medium.
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 bool fMediumLockWrite,
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 == this)
4347 mediumLockList.Prepend(pMedium, fMediumLockWrite);
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 true /* fMediumLockWrite */,
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 true /* fMediumLockWrite */,
5116 false /* fMediumLockWriteAll */,
5117 NULL,
5118 *aMediumLockList);
5119 else
5120 rc = i_createMediumLockList(true /* fFailIfInaccessible */,
5121 false /* fMediumLockWrite */,
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 }
5542
5543 /* the destructor will do the work */
5544 delete aMediumLockList;
5545
5546 /* unlock the children which had to be reparented, the destructor will do
5547 * the work */
5548 if (aChildrenToReparent)
5549 delete aChildrenToReparent;
5550}
5551
5552/**
5553 * Fix the parent UUID of all children to point to this medium as their
5554 * parent.
5555 */
5556HRESULT Medium::i_fixParentUuidOfChildren(MediumLockList *pChildrenToReparent)
5557{
5558 Assert(!isWriteLockOnCurrentThread());
5559 Assert(!m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
5560 MediumLockList mediumLockList;
5561 HRESULT rc = i_createMediumLockList(true /* fFailIfInaccessible */,
5562 false /* fMediumLockWrite */,
5563 false /* fMediumLockWriteAll */,
5564 this,
5565 mediumLockList);
5566 AssertComRCReturnRC(rc);
5567
5568 try
5569 {
5570 PVBOXHDD hdd;
5571 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
5572 ComAssertRCThrow(vrc, E_FAIL);
5573
5574 try
5575 {
5576 MediumLockList::Base::iterator lockListBegin =
5577 mediumLockList.GetBegin();
5578 MediumLockList::Base::iterator lockListEnd =
5579 mediumLockList.GetEnd();
5580 for (MediumLockList::Base::iterator it = lockListBegin;
5581 it != lockListEnd;
5582 ++it)
5583 {
5584 MediumLock &mediumLock = *it;
5585 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
5586 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
5587
5588 // open the medium
5589 vrc = VDOpen(hdd,
5590 pMedium->m->strFormat.c_str(),
5591 pMedium->m->strLocationFull.c_str(),
5592 VD_OPEN_FLAGS_READONLY | m->uOpenFlagsDef,
5593 pMedium->m->vdImageIfaces);
5594 if (RT_FAILURE(vrc))
5595 throw vrc;
5596 }
5597
5598 MediumLockList::Base::iterator childrenBegin = pChildrenToReparent->GetBegin();
5599 MediumLockList::Base::iterator childrenEnd = pChildrenToReparent->GetEnd();
5600 for (MediumLockList::Base::iterator it = childrenBegin;
5601 it != childrenEnd;
5602 ++it)
5603 {
5604 Medium *pMedium = it->GetMedium();
5605 /* VD_OPEN_FLAGS_INFO since UUID is wrong yet */
5606 vrc = VDOpen(hdd,
5607 pMedium->m->strFormat.c_str(),
5608 pMedium->m->strLocationFull.c_str(),
5609 VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
5610 pMedium->m->vdImageIfaces);
5611 if (RT_FAILURE(vrc))
5612 throw vrc;
5613
5614 vrc = VDSetParentUuid(hdd, VD_LAST_IMAGE, m->id.raw());
5615 if (RT_FAILURE(vrc))
5616 throw vrc;
5617
5618 vrc = VDClose(hdd, false /* fDelete */);
5619 if (RT_FAILURE(vrc))
5620 throw vrc;
5621 }
5622 }
5623 catch (HRESULT aRC) { rc = aRC; }
5624 catch (int aVRC)
5625 {
5626 rc = setError(E_FAIL,
5627 tr("Could not update medium UUID references to parent '%s' (%s)"),
5628 m->strLocationFull.c_str(),
5629 i_vdError(aVRC).c_str());
5630 }
5631
5632 VDDestroy(hdd);
5633 }
5634 catch (HRESULT aRC) { rc = aRC; }
5635
5636 return rc;
5637}
5638
5639/**
5640 * Used by IAppliance to export disk images.
5641 *
5642 * @param aFilename Filename to create (UTF8).
5643 * @param aFormat Medium format for creating @a aFilename.
5644 * @param aVariant Which exact image format variant to use
5645 * for the destination image.
5646 * @param pKeyStore The optional key store for decrypting the data
5647 * for encrypted media during the export.
5648 * @param aVDImageIOCallbacks Pointer to the callback table for a
5649 * VDINTERFACEIO interface. May be NULL.
5650 * @param aVDImageIOUser Opaque data for the callbacks.
5651 * @param aProgress Progress object to use.
5652 * @return
5653 * @note The source format is defined by the Medium instance.
5654 */
5655HRESULT Medium::i_exportFile(const char *aFilename,
5656 const ComObjPtr<MediumFormat> &aFormat,
5657 MediumVariant_T aVariant,
5658 SecretKeyStore *pKeyStore,
5659 PVDINTERFACEIO aVDImageIOIf, void *aVDImageIOUser,
5660 const ComObjPtr<Progress> &aProgress)
5661{
5662 AssertPtrReturn(aFilename, E_INVALIDARG);
5663 AssertReturn(!aFormat.isNull(), E_INVALIDARG);
5664 AssertReturn(!aProgress.isNull(), E_INVALIDARG);
5665
5666 AutoCaller autoCaller(this);
5667 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5668
5669 HRESULT rc = S_OK;
5670 Medium::Task *pTask = NULL;
5671
5672 try
5673 {
5674 // This needs no extra locks besides what is done in the called methods.
5675
5676 /* Build the source lock list. */
5677 MediumLockList *pSourceMediumLockList(new MediumLockList());
5678 rc = i_createMediumLockList(true /* fFailIfInaccessible */,
5679 false /* fMediumLockWrite */,
5680 false /* fMediumLockWriteAll */,
5681 NULL,
5682 *pSourceMediumLockList);
5683 if (FAILED(rc))
5684 {
5685 delete pSourceMediumLockList;
5686 throw rc;
5687 }
5688
5689 rc = pSourceMediumLockList->Lock();
5690 if (FAILED(rc))
5691 {
5692 delete pSourceMediumLockList;
5693 throw setError(rc,
5694 tr("Failed to lock source media '%s'"),
5695 i_getLocationFull().c_str());
5696 }
5697
5698 /* setup task object to carry out the operation asynchronously */
5699 pTask = new Medium::ExportTask(this, aProgress, aFilename, aFormat,
5700 aVariant, pKeyStore, aVDImageIOIf,
5701 aVDImageIOUser, pSourceMediumLockList);
5702 rc = pTask->rc();
5703 AssertComRC(rc);
5704 if (FAILED(rc))
5705 throw rc;
5706 }
5707 catch (HRESULT aRC) { rc = aRC; }
5708
5709 if (SUCCEEDED(rc))
5710 rc = i_startThread(pTask);
5711 else if (pTask != NULL)
5712 delete pTask;
5713
5714 return rc;
5715}
5716
5717/**
5718 * Used by IAppliance to import disk images.
5719 *
5720 * @param aFilename Filename to read (UTF8).
5721 * @param aFormat Medium format for reading @a aFilename.
5722 * @param aVariant Which exact image format variant to use
5723 * for the destination image.
5724 * @param aVfsIosSrc Handle to the source I/O stream.
5725 * @param aParent Parent medium. May be NULL.
5726 * @param aProgress Progress object to use.
5727 * @return
5728 * @note The destination format is defined by the Medium instance.
5729 */
5730HRESULT Medium::i_importFile(const char *aFilename,
5731 const ComObjPtr<MediumFormat> &aFormat,
5732 MediumVariant_T aVariant,
5733 RTVFSIOSTREAM aVfsIosSrc,
5734 const ComObjPtr<Medium> &aParent,
5735 const ComObjPtr<Progress> &aProgress)
5736{
5737 AssertPtrReturn(aFilename, E_INVALIDARG);
5738 AssertReturn(!aFormat.isNull(), E_INVALIDARG);
5739 AssertReturn(!aProgress.isNull(), E_INVALIDARG);
5740
5741 AutoCaller autoCaller(this);
5742 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5743
5744 HRESULT rc = S_OK;
5745 Medium::Task *pTask = NULL;
5746
5747 try
5748 {
5749 // locking: we need the tree lock first because we access parent pointers
5750 // and we need to write-lock the media involved
5751 uint32_t cHandles = 2;
5752 LockHandle* pHandles[3] = { &m->pVirtualBox->i_getMediaTreeLockHandle(),
5753 this->lockHandle() };
5754 /* Only add parent to the lock if it is not null */
5755 if (!aParent.isNull())
5756 pHandles[cHandles++] = aParent->lockHandle();
5757 AutoWriteLock alock(cHandles,
5758 pHandles
5759 COMMA_LOCKVAL_SRC_POS);
5760
5761 if ( m->state != MediumState_NotCreated
5762 && m->state != MediumState_Created)
5763 throw i_setStateError();
5764
5765 /* Build the target lock list. */
5766 MediumLockList *pTargetMediumLockList(new MediumLockList());
5767 alock.release();
5768 rc = i_createMediumLockList(true /* fFailIfInaccessible */,
5769 true /* fMediumLockWrite */,
5770 false /* fMediumLockWriteAll */,
5771 aParent,
5772 *pTargetMediumLockList);
5773 alock.acquire();
5774 if (FAILED(rc))
5775 {
5776 delete pTargetMediumLockList;
5777 throw rc;
5778 }
5779
5780 alock.release();
5781 rc = pTargetMediumLockList->Lock();
5782 alock.acquire();
5783 if (FAILED(rc))
5784 {
5785 delete pTargetMediumLockList;
5786 throw setError(rc,
5787 tr("Failed to lock target media '%s'"),
5788 i_getLocationFull().c_str());
5789 }
5790
5791 /* setup task object to carry out the operation asynchronously */
5792 pTask = new Medium::ImportTask(this, aProgress, aFilename, aFormat, aVariant,
5793 aVfsIosSrc, aParent, pTargetMediumLockList);
5794 rc = pTask->rc();
5795 AssertComRC(rc);
5796 if (FAILED(rc))
5797 throw rc;
5798
5799 if (m->state == MediumState_NotCreated)
5800 m->state = MediumState_Creating;
5801 }
5802 catch (HRESULT aRC) { rc = aRC; }
5803
5804 if (SUCCEEDED(rc))
5805 rc = i_startThread(pTask);
5806 else if (pTask != NULL)
5807 delete pTask;
5808
5809 return rc;
5810}
5811
5812/**
5813 * Internal version of the public CloneTo API which allows to enable certain
5814 * optimizations to improve speed during VM cloning.
5815 *
5816 * @param aTarget Target medium
5817 * @param aVariant Which exact image format variant to use
5818 * for the destination image.
5819 * @param aParent Parent medium. May be NULL.
5820 * @param aProgress Progress object to use.
5821 * @param idxSrcImageSame The last image in the source chain which has the
5822 * same content as the given image in the destination
5823 * chain. Use UINT32_MAX to disable this optimization.
5824 * @param idxDstImageSame The last image in the destination chain which has the
5825 * same content as the given image in the source chain.
5826 * Use UINT32_MAX to disable this optimization.
5827 * @return
5828 */
5829HRESULT Medium::i_cloneToEx(const ComObjPtr<Medium> &aTarget, ULONG aVariant,
5830 const ComObjPtr<Medium> &aParent, IProgress **aProgress,
5831 uint32_t idxSrcImageSame, uint32_t idxDstImageSame)
5832{
5833 CheckComArgNotNull(aTarget);
5834 CheckComArgOutPointerValid(aProgress);
5835 ComAssertRet(aTarget != this, E_INVALIDARG);
5836
5837 AutoCaller autoCaller(this);
5838 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5839
5840 HRESULT rc = S_OK;
5841 ComObjPtr<Progress> pProgress;
5842 Medium::Task *pTask = NULL;
5843
5844 try
5845 {
5846 // locking: we need the tree lock first because we access parent pointers
5847 // and we need to write-lock the media involved
5848 uint32_t cHandles = 3;
5849 LockHandle* pHandles[4] = { &m->pVirtualBox->i_getMediaTreeLockHandle(),
5850 this->lockHandle(),
5851 aTarget->lockHandle() };
5852 /* Only add parent to the lock if it is not null */
5853 if (!aParent.isNull())
5854 pHandles[cHandles++] = aParent->lockHandle();
5855 AutoWriteLock alock(cHandles,
5856 pHandles
5857 COMMA_LOCKVAL_SRC_POS);
5858
5859 if ( aTarget->m->state != MediumState_NotCreated
5860 && aTarget->m->state != MediumState_Created)
5861 throw aTarget->i_setStateError();
5862
5863 /* Build the source lock list. */
5864 MediumLockList *pSourceMediumLockList(new MediumLockList());
5865 alock.release();
5866 rc = i_createMediumLockList(true /* fFailIfInaccessible */,
5867 false /* fMediumLockWrite */,
5868 false /* fMediumLockWriteAll */,
5869 NULL,
5870 *pSourceMediumLockList);
5871 alock.acquire();
5872 if (FAILED(rc))
5873 {
5874 delete pSourceMediumLockList;
5875 throw rc;
5876 }
5877
5878 /* Build the target lock list (including the to-be parent chain). */
5879 MediumLockList *pTargetMediumLockList(new MediumLockList());
5880 alock.release();
5881 rc = aTarget->i_createMediumLockList(true /* fFailIfInaccessible */,
5882 true /* fMediumLockWrite */,
5883 false /* fMediumLockWriteAll */,
5884 aParent,
5885 *pTargetMediumLockList);
5886 alock.acquire();
5887 if (FAILED(rc))
5888 {
5889 delete pSourceMediumLockList;
5890 delete pTargetMediumLockList;
5891 throw rc;
5892 }
5893
5894 alock.release();
5895 rc = pSourceMediumLockList->Lock();
5896 alock.acquire();
5897 if (FAILED(rc))
5898 {
5899 delete pSourceMediumLockList;
5900 delete pTargetMediumLockList;
5901 throw setError(rc,
5902 tr("Failed to lock source media '%s'"),
5903 i_getLocationFull().c_str());
5904 }
5905 alock.release();
5906 rc = pTargetMediumLockList->Lock();
5907 alock.acquire();
5908 if (FAILED(rc))
5909 {
5910 delete pSourceMediumLockList;
5911 delete pTargetMediumLockList;
5912 throw setError(rc,
5913 tr("Failed to lock target media '%s'"),
5914 aTarget->i_getLocationFull().c_str());
5915 }
5916
5917 pProgress.createObject();
5918 rc = pProgress->init(m->pVirtualBox,
5919 static_cast <IMedium *>(this),
5920 BstrFmt(tr("Creating clone medium '%s'"), aTarget->m->strLocationFull.c_str()).raw(),
5921 TRUE /* aCancelable */);
5922 if (FAILED(rc))
5923 {
5924 delete pSourceMediumLockList;
5925 delete pTargetMediumLockList;
5926 throw rc;
5927 }
5928
5929 /* setup task object to carry out the operation asynchronously */
5930 pTask = new Medium::CloneTask(this, pProgress, aTarget,
5931 (MediumVariant_T)aVariant,
5932 aParent, idxSrcImageSame,
5933 idxDstImageSame, pSourceMediumLockList,
5934 pTargetMediumLockList);
5935 rc = pTask->rc();
5936 AssertComRC(rc);
5937 if (FAILED(rc))
5938 throw rc;
5939
5940 if (aTarget->m->state == MediumState_NotCreated)
5941 aTarget->m->state = MediumState_Creating;
5942 }
5943 catch (HRESULT aRC) { rc = aRC; }
5944
5945 if (SUCCEEDED(rc))
5946 {
5947 rc = i_startThread(pTask);
5948
5949 if (SUCCEEDED(rc))
5950 pProgress.queryInterfaceTo(aProgress);
5951 }
5952 else if (pTask != NULL)
5953 delete pTask;
5954
5955 return rc;
5956}
5957
5958/**
5959 * Returns the key identifier for this medium if encryption is configured.
5960 *
5961 * @returns Key identifier or empty string if no encryption is configured.
5962 */
5963const Utf8Str& Medium::i_getKeyId()
5964{
5965 ComObjPtr<Medium> pBase = i_getBase();
5966
5967 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5968
5969 settings::StringsMap::const_iterator it = pBase->m->mapProperties.find("CRYPT/KeyId");
5970 if (it == pBase->m->mapProperties.end())
5971 return Utf8Str::Empty;
5972
5973 return it->second;
5974}
5975
5976/**
5977 * Returns all filter related properties.
5978 *
5979 * @returns COM status code.
5980 * @param aReturnNames Where to store the properties names on success.
5981 * @param aReturnValues Where to store the properties values on success.
5982 */
5983HRESULT Medium::i_getFilterProperties(std::vector<com::Utf8Str> &aReturnNames,
5984 std::vector<com::Utf8Str> &aReturnValues)
5985{
5986 std::vector<com::Utf8Str> aPropNames;
5987 std::vector<com::Utf8Str> aPropValues;
5988 HRESULT hrc = getProperties(Utf8Str(""), aPropNames, aPropValues);
5989
5990 if (SUCCEEDED(hrc))
5991 {
5992 unsigned cReturnSize = 0;
5993 aReturnNames.resize(0);
5994 aReturnValues.resize(0);
5995 for (unsigned idx = 0; idx < aPropNames.size(); idx++)
5996 {
5997 if (i_isPropertyForFilter(aPropNames[idx]))
5998 {
5999 aReturnNames.resize(cReturnSize + 1);
6000 aReturnValues.resize(cReturnSize + 1);
6001 aReturnNames[cReturnSize] = aPropNames[idx];
6002 aReturnValues[cReturnSize] = aPropValues[idx];
6003 cReturnSize++;
6004 }
6005 }
6006 }
6007
6008 return hrc;
6009}
6010
6011////////////////////////////////////////////////////////////////////////////////
6012//
6013// Private methods
6014//
6015////////////////////////////////////////////////////////////////////////////////
6016
6017/**
6018 * Queries information from the medium.
6019 *
6020 * As a result of this call, the accessibility state and data members such as
6021 * size and description will be updated with the current information.
6022 *
6023 * @note This method may block during a system I/O call that checks storage
6024 * accessibility.
6025 *
6026 * @note Caller MUST NOT hold the media tree or medium lock.
6027 *
6028 * @note Locks mParent for reading. Locks this object for writing.
6029 *
6030 * @param fSetImageId Whether to reset the UUID contained in the image file to the UUID in the medium instance data (see SetIDs())
6031 * @param fSetParentId Whether to reset the parent UUID contained in the image file to the parent
6032 * UUID in the medium instance data (see SetIDs())
6033 * @return
6034 */
6035HRESULT Medium::i_queryInfo(bool fSetImageId, bool fSetParentId, AutoCaller &autoCaller)
6036{
6037 Assert(!isWriteLockOnCurrentThread());
6038 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6039
6040 if ( ( m->state != MediumState_Created
6041 && m->state != MediumState_Inaccessible
6042 && m->state != MediumState_LockedRead)
6043 || m->fClosing)
6044 return E_FAIL;
6045
6046 HRESULT rc = S_OK;
6047
6048 int vrc = VINF_SUCCESS;
6049
6050 /* check if a blocking i_queryInfo() call is in progress on some other thread,
6051 * and wait for it to finish if so instead of querying data ourselves */
6052 if (m->queryInfoRunning)
6053 {
6054 Assert( m->state == MediumState_LockedRead
6055 || m->state == MediumState_LockedWrite);
6056
6057 while (m->queryInfoRunning)
6058 {
6059 alock.release();
6060 /* must not hold the object lock now */
6061 Assert(!isWriteLockOnCurrentThread());
6062 {
6063 AutoReadLock qlock(m->queryInfoSem COMMA_LOCKVAL_SRC_POS);
6064 }
6065 alock.acquire();
6066 }
6067
6068 return S_OK;
6069 }
6070
6071 bool success = false;
6072 Utf8Str lastAccessError;
6073
6074 /* are we dealing with a new medium constructed using the existing
6075 * location? */
6076 bool isImport = m->id.isZero();
6077 unsigned uOpenFlags = VD_OPEN_FLAGS_INFO;
6078
6079 /* Note that we don't use VD_OPEN_FLAGS_READONLY when opening new
6080 * media because that would prevent necessary modifications
6081 * when opening media of some third-party formats for the first
6082 * time in VirtualBox (such as VMDK for which VDOpen() needs to
6083 * generate an UUID if it is missing) */
6084 if ( m->hddOpenMode == OpenReadOnly
6085 || m->type == MediumType_Readonly
6086 || (!isImport && !fSetImageId && !fSetParentId)
6087 )
6088 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
6089
6090 /* Open shareable medium with the appropriate flags */
6091 if (m->type == MediumType_Shareable)
6092 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
6093
6094 /* Lock the medium, which makes the behavior much more consistent, must be
6095 * done before dropping the object lock and setting queryInfoRunning. */
6096 ComPtr<IToken> pToken;
6097 if (uOpenFlags & (VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SHAREABLE))
6098 rc = LockRead(pToken.asOutParam());
6099 else
6100 rc = LockWrite(pToken.asOutParam());
6101 if (FAILED(rc)) return rc;
6102
6103 /* Copies of the input state fields which are not read-only,
6104 * as we're dropping the lock. CAUTION: be extremely careful what
6105 * you do with the contents of this medium object, as you will
6106 * create races if there are concurrent changes. */
6107 Utf8Str format(m->strFormat);
6108 Utf8Str location(m->strLocationFull);
6109 ComObjPtr<MediumFormat> formatObj = m->formatObj;
6110
6111 /* "Output" values which can't be set because the lock isn't held
6112 * at the time the values are determined. */
6113 Guid mediumId = m->id;
6114 uint64_t mediumSize = 0;
6115 uint64_t mediumLogicalSize = 0;
6116
6117 /* Flag whether a base image has a non-zero parent UUID and thus
6118 * need repairing after it was closed again. */
6119 bool fRepairImageZeroParentUuid = false;
6120
6121 ComObjPtr<VirtualBox> pVirtualBox = m->pVirtualBox;
6122
6123 /* must be set before leaving the object lock the first time */
6124 m->queryInfoRunning = true;
6125
6126 /* must leave object lock now, because a lock from a higher lock class
6127 * is needed and also a lengthy operation is coming */
6128 alock.release();
6129 autoCaller.release();
6130
6131 /* Note that taking the queryInfoSem after leaving the object lock above
6132 * can lead to short spinning of the loops waiting for i_queryInfo() to
6133 * complete. This is unavoidable since the other order causes a lock order
6134 * violation: here it would be requesting the object lock (at the beginning
6135 * of the method), then queryInfoSem, and below the other way round. */
6136 AutoWriteLock qlock(m->queryInfoSem COMMA_LOCKVAL_SRC_POS);
6137
6138 /* take the opportunity to have a media tree lock, released initially */
6139 Assert(!isWriteLockOnCurrentThread());
6140 Assert(!pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
6141 AutoWriteLock treeLock(pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
6142 treeLock.release();
6143
6144 /* re-take the caller, but not the object lock, to keep uninit away */
6145 autoCaller.add();
6146 if (FAILED(autoCaller.rc()))
6147 {
6148 m->queryInfoRunning = false;
6149 return autoCaller.rc();
6150 }
6151
6152 try
6153 {
6154 /* skip accessibility checks for host drives */
6155 if (m->hostDrive)
6156 {
6157 success = true;
6158 throw S_OK;
6159 }
6160
6161 PVBOXHDD hdd;
6162 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
6163 ComAssertRCThrow(vrc, E_FAIL);
6164
6165 try
6166 {
6167 /** @todo This kind of opening of media is assuming that diff
6168 * media can be opened as base media. Should be documented that
6169 * it must work for all medium format backends. */
6170 vrc = VDOpen(hdd,
6171 format.c_str(),
6172 location.c_str(),
6173 uOpenFlags | m->uOpenFlagsDef,
6174 m->vdImageIfaces);
6175 if (RT_FAILURE(vrc))
6176 {
6177 lastAccessError = Utf8StrFmt(tr("Could not open the medium '%s'%s"),
6178 location.c_str(), i_vdError(vrc).c_str());
6179 throw S_OK;
6180 }
6181
6182 if (formatObj->i_getCapabilities() & MediumFormatCapabilities_Uuid)
6183 {
6184 /* Modify the UUIDs if necessary. The associated fields are
6185 * not modified by other code, so no need to copy. */
6186 if (fSetImageId)
6187 {
6188 alock.acquire();
6189 vrc = VDSetUuid(hdd, 0, m->uuidImage.raw());
6190 alock.release();
6191 if (RT_FAILURE(vrc))
6192 {
6193 lastAccessError = Utf8StrFmt(tr("Could not update the UUID of medium '%s'%s"),
6194 location.c_str(), i_vdError(vrc).c_str());
6195 throw S_OK;
6196 }
6197 mediumId = m->uuidImage;
6198 }
6199 if (fSetParentId)
6200 {
6201 alock.acquire();
6202 vrc = VDSetParentUuid(hdd, 0, m->uuidParentImage.raw());
6203 alock.release();
6204 if (RT_FAILURE(vrc))
6205 {
6206 lastAccessError = Utf8StrFmt(tr("Could not update the parent UUID of medium '%s'%s"),
6207 location.c_str(), i_vdError(vrc).c_str());
6208 throw S_OK;
6209 }
6210 }
6211 /* zap the information, these are no long-term members */
6212 alock.acquire();
6213 unconst(m->uuidImage).clear();
6214 unconst(m->uuidParentImage).clear();
6215 alock.release();
6216
6217 /* check the UUID */
6218 RTUUID uuid;
6219 vrc = VDGetUuid(hdd, 0, &uuid);
6220 ComAssertRCThrow(vrc, E_FAIL);
6221
6222 if (isImport)
6223 {
6224 mediumId = uuid;
6225
6226 if (mediumId.isZero() && (m->hddOpenMode == OpenReadOnly))
6227 // only when importing a VDMK that has no UUID, create one in memory
6228 mediumId.create();
6229 }
6230 else
6231 {
6232 Assert(!mediumId.isZero());
6233
6234 if (mediumId != uuid)
6235 {
6236 /** @todo r=klaus this always refers to VirtualBox.xml as the medium registry, even for new VMs */
6237 lastAccessError = Utf8StrFmt(
6238 tr("UUID {%RTuuid} of the medium '%s' does not match the value {%RTuuid} stored in the media registry ('%s')"),
6239 &uuid,
6240 location.c_str(),
6241 mediumId.raw(),
6242 pVirtualBox->i_settingsFilePath().c_str());
6243 throw S_OK;
6244 }
6245 }
6246 }
6247 else
6248 {
6249 /* the backend does not support storing UUIDs within the
6250 * underlying storage so use what we store in XML */
6251
6252 if (fSetImageId)
6253 {
6254 /* set the UUID if an API client wants to change it */
6255 alock.acquire();
6256 mediumId = m->uuidImage;
6257 alock.release();
6258 }
6259 else if (isImport)
6260 {
6261 /* generate an UUID for an imported UUID-less medium */
6262 mediumId.create();
6263 }
6264 }
6265
6266 /* set the image uuid before the below parent uuid handling code
6267 * might place it somewhere in the media tree, so that the medium
6268 * UUID is valid at this point */
6269 alock.acquire();
6270 if (isImport || fSetImageId)
6271 unconst(m->id) = mediumId;
6272 alock.release();
6273
6274 /* get the medium variant */
6275 unsigned uImageFlags;
6276 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
6277 ComAssertRCThrow(vrc, E_FAIL);
6278 alock.acquire();
6279 m->variant = (MediumVariant_T)uImageFlags;
6280 alock.release();
6281
6282 /* check/get the parent uuid and update corresponding state */
6283 if (uImageFlags & VD_IMAGE_FLAGS_DIFF)
6284 {
6285 RTUUID parentId;
6286 vrc = VDGetParentUuid(hdd, 0, &parentId);
6287 ComAssertRCThrow(vrc, E_FAIL);
6288
6289 /* streamOptimized VMDK images are only accepted as base
6290 * images, as this allows automatic repair of OVF appliances.
6291 * Since such images don't support random writes they will not
6292 * be created for diff images. Only an overly smart user might
6293 * manually create this case. Too bad for him. */
6294 if ( (isImport || fSetParentId)
6295 && !(uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED))
6296 {
6297 /* the parent must be known to us. Note that we freely
6298 * call locking methods of mVirtualBox and parent, as all
6299 * relevant locks must be already held. There may be no
6300 * concurrent access to the just opened medium on other
6301 * threads yet (and init() will fail if this method reports
6302 * MediumState_Inaccessible) */
6303
6304 ComObjPtr<Medium> pParent;
6305 if (RTUuidIsNull(&parentId))
6306 rc = VBOX_E_OBJECT_NOT_FOUND;
6307 else
6308 rc = pVirtualBox->i_findHardDiskById(Guid(parentId), false /* aSetError */, &pParent);
6309 if (FAILED(rc))
6310 {
6311 if (fSetImageId && !fSetParentId)
6312 {
6313 /* If the image UUID gets changed for an existing
6314 * image then the parent UUID can be stale. In such
6315 * cases clear the parent information. The parent
6316 * information may/will be re-set later if the
6317 * API client wants to adjust a complete medium
6318 * hierarchy one by one. */
6319 rc = S_OK;
6320 alock.acquire();
6321 RTUuidClear(&parentId);
6322 vrc = VDSetParentUuid(hdd, 0, &parentId);
6323 alock.release();
6324 ComAssertRCThrow(vrc, E_FAIL);
6325 }
6326 else
6327 {
6328 lastAccessError = Utf8StrFmt(tr("Parent medium with UUID {%RTuuid} of the medium '%s' is not found in the media registry ('%s')"),
6329 &parentId, location.c_str(),
6330 pVirtualBox->i_settingsFilePath().c_str());
6331 throw S_OK;
6332 }
6333 }
6334
6335 /* must drop the caller before taking the tree lock */
6336 autoCaller.release();
6337 /* we set mParent & children() */
6338 treeLock.acquire();
6339 autoCaller.add();
6340 if (FAILED(autoCaller.rc()))
6341 throw autoCaller.rc();
6342
6343 if (m->pParent)
6344 i_deparent();
6345
6346 if (!pParent.isNull())
6347 if (pParent->i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX)
6348 {
6349 AutoReadLock plock(pParent COMMA_LOCKVAL_SRC_POS);
6350 throw setError(VBOX_E_INVALID_OBJECT_STATE,
6351 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"),
6352 pParent->m->strLocationFull.c_str());
6353 }
6354 i_setParent(pParent);
6355
6356 treeLock.release();
6357 }
6358 else
6359 {
6360 /* must drop the caller before taking the tree lock */
6361 autoCaller.release();
6362 /* we access mParent */
6363 treeLock.acquire();
6364 autoCaller.add();
6365 if (FAILED(autoCaller.rc()))
6366 throw autoCaller.rc();
6367
6368 /* check that parent UUIDs match. Note that there's no need
6369 * for the parent's AutoCaller (our lifetime is bound to
6370 * it) */
6371
6372 if (m->pParent.isNull())
6373 {
6374 /* Due to a bug in VDCopy() in VirtualBox 3.0.0-3.0.14
6375 * and 3.1.0-3.1.8 there are base images out there
6376 * which have a non-zero parent UUID. No point in
6377 * complaining about them, instead automatically
6378 * repair the problem. Later we can bring back the
6379 * error message, but we should wait until really
6380 * most users have repaired their images, either with
6381 * VBoxFixHdd or this way. */
6382#if 1
6383 fRepairImageZeroParentUuid = true;
6384#else /* 0 */
6385 lastAccessError = Utf8StrFmt(
6386 tr("Medium type of '%s' is differencing but it is not associated with any parent medium in the media registry ('%s')"),
6387 location.c_str(),
6388 pVirtualBox->settingsFilePath().c_str());
6389 treeLock.release();
6390 throw S_OK;
6391#endif /* 0 */
6392 }
6393
6394 {
6395 autoCaller.release();
6396 AutoReadLock parentLock(m->pParent COMMA_LOCKVAL_SRC_POS);
6397 autoCaller.add();
6398 if (FAILED(autoCaller.rc()))
6399 throw autoCaller.rc();
6400
6401 if ( !fRepairImageZeroParentUuid
6402 && m->pParent->i_getState() != MediumState_Inaccessible
6403 && m->pParent->i_getId() != parentId)
6404 {
6405 /** @todo r=klaus this always refers to VirtualBox.xml as the medium registry, even for new VMs */
6406 lastAccessError = Utf8StrFmt(
6407 tr("Parent UUID {%RTuuid} of the medium '%s' does not match UUID {%RTuuid} of its parent medium stored in the media registry ('%s')"),
6408 &parentId, location.c_str(),
6409 m->pParent->i_getId().raw(),
6410 pVirtualBox->i_settingsFilePath().c_str());
6411 parentLock.release();
6412 treeLock.release();
6413 throw S_OK;
6414 }
6415 }
6416
6417 /// @todo NEWMEDIA what to do if the parent is not
6418 /// accessible while the diff is? Probably nothing. The
6419 /// real code will detect the mismatch anyway.
6420
6421 treeLock.release();
6422 }
6423 }
6424
6425 mediumSize = VDGetFileSize(hdd, 0);
6426 mediumLogicalSize = VDGetSize(hdd, 0);
6427
6428 success = true;
6429 }
6430 catch (HRESULT aRC)
6431 {
6432 rc = aRC;
6433 }
6434
6435 vrc = VDDestroy(hdd);
6436 if (RT_FAILURE(vrc))
6437 {
6438 lastAccessError = Utf8StrFmt(tr("Could not update and close the medium '%s'%s"),
6439 location.c_str(), i_vdError(vrc).c_str());
6440 success = false;
6441 throw S_OK;
6442 }
6443 }
6444 catch (HRESULT aRC)
6445 {
6446 rc = aRC;
6447 }
6448
6449 autoCaller.release();
6450 treeLock.acquire();
6451 autoCaller.add();
6452 if (FAILED(autoCaller.rc()))
6453 {
6454 m->queryInfoRunning = false;
6455 return autoCaller.rc();
6456 }
6457 alock.acquire();
6458
6459 if (success)
6460 {
6461 m->size = mediumSize;
6462 m->logicalSize = mediumLogicalSize;
6463 m->strLastAccessError.setNull();
6464 }
6465 else
6466 {
6467 m->strLastAccessError = lastAccessError;
6468 Log1WarningFunc(("'%s' is not accessible (error='%s', rc=%Rhrc, vrc=%Rrc)\n",
6469 location.c_str(), m->strLastAccessError.c_str(), rc, vrc));
6470 }
6471
6472 /* Set the proper state according to the result of the check */
6473 if (success)
6474 m->preLockState = MediumState_Created;
6475 else
6476 m->preLockState = MediumState_Inaccessible;
6477
6478 /* unblock anyone waiting for the i_queryInfo results */
6479 qlock.release();
6480 m->queryInfoRunning = false;
6481
6482 pToken->Abandon();
6483 pToken.setNull();
6484
6485 if (FAILED(rc)) return rc;
6486
6487 /* If this is a base image which incorrectly has a parent UUID set,
6488 * repair the image now by zeroing the parent UUID. This is only done
6489 * when we have structural information from a config file, on import
6490 * this is not possible. If someone would accidentally call openMedium
6491 * with a diff image before the base is registered this would destroy
6492 * the diff. Not acceptable. */
6493 if (fRepairImageZeroParentUuid)
6494 {
6495 rc = LockWrite(pToken.asOutParam());
6496 if (FAILED(rc)) return rc;
6497
6498 alock.release();
6499
6500 try
6501 {
6502 PVBOXHDD hdd;
6503 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
6504 ComAssertRCThrow(vrc, E_FAIL);
6505
6506 try
6507 {
6508 vrc = VDOpen(hdd,
6509 format.c_str(),
6510 location.c_str(),
6511 (uOpenFlags & ~VD_OPEN_FLAGS_READONLY) | m->uOpenFlagsDef,
6512 m->vdImageIfaces);
6513 if (RT_FAILURE(vrc))
6514 throw S_OK;
6515
6516 RTUUID zeroParentUuid;
6517 RTUuidClear(&zeroParentUuid);
6518 vrc = VDSetParentUuid(hdd, 0, &zeroParentUuid);
6519 ComAssertRCThrow(vrc, E_FAIL);
6520 }
6521 catch (HRESULT aRC)
6522 {
6523 rc = aRC;
6524 }
6525
6526 VDDestroy(hdd);
6527 }
6528 catch (HRESULT aRC)
6529 {
6530 rc = aRC;
6531 }
6532
6533 pToken->Abandon();
6534 pToken.setNull();
6535 if (FAILED(rc)) return rc;
6536 }
6537
6538 return rc;
6539}
6540
6541/**
6542 * Performs extra checks if the medium can be closed and returns S_OK in
6543 * this case. Otherwise, returns a respective error message. Called by
6544 * Close() under the medium tree lock and the medium lock.
6545 *
6546 * @note Also reused by Medium::Reset().
6547 *
6548 * @note Caller must hold the media tree write lock!
6549 */
6550HRESULT Medium::i_canClose()
6551{
6552 Assert(m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
6553
6554 if (i_getChildren().size() != 0)
6555 return setError(VBOX_E_OBJECT_IN_USE,
6556 tr("Cannot close medium '%s' because it has %d child media"),
6557 m->strLocationFull.c_str(), i_getChildren().size());
6558
6559 return S_OK;
6560}
6561
6562/**
6563 * Unregisters this medium with mVirtualBox. Called by close() under the medium tree lock.
6564 *
6565 * @note Caller must have locked the media tree lock for writing!
6566 */
6567HRESULT Medium::i_unregisterWithVirtualBox()
6568{
6569 /* Note that we need to de-associate ourselves from the parent to let
6570 * VirtualBox::i_unregisterMedium() properly save the registry */
6571
6572 /* we modify mParent and access children */
6573 Assert(m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
6574
6575 Medium *pParentBackup = m->pParent;
6576 AssertReturn(i_getChildren().size() == 0, E_FAIL);
6577 if (m->pParent)
6578 i_deparent();
6579
6580 HRESULT rc = m->pVirtualBox->i_unregisterMedium(this);
6581 if (FAILED(rc))
6582 {
6583 if (pParentBackup)
6584 {
6585 // re-associate with the parent as we are still relatives in the registry
6586 i_setParent(pParentBackup);
6587 }
6588 }
6589
6590 return rc;
6591}
6592
6593/**
6594 * Like SetProperty but do not trigger a settings store. Only for internal use!
6595 */
6596HRESULT Medium::i_setPropertyDirect(const Utf8Str &aName, const Utf8Str &aValue)
6597{
6598 AutoCaller autoCaller(this);
6599 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6600
6601 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
6602
6603 switch (m->state)
6604 {
6605 case MediumState_Created:
6606 case MediumState_Inaccessible:
6607 break;
6608 default:
6609 return i_setStateError();
6610 }
6611
6612 m->mapProperties[aName] = aValue;
6613
6614 return S_OK;
6615}
6616
6617/**
6618 * Sets the extended error info according to the current media state.
6619 *
6620 * @note Must be called from under this object's write or read lock.
6621 */
6622HRESULT Medium::i_setStateError()
6623{
6624 HRESULT rc = E_FAIL;
6625
6626 switch (m->state)
6627 {
6628 case MediumState_NotCreated:
6629 {
6630 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
6631 tr("Storage for the medium '%s' is not created"),
6632 m->strLocationFull.c_str());
6633 break;
6634 }
6635 case MediumState_Created:
6636 {
6637 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
6638 tr("Storage for the medium '%s' is already created"),
6639 m->strLocationFull.c_str());
6640 break;
6641 }
6642 case MediumState_LockedRead:
6643 {
6644 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
6645 tr("Medium '%s' is locked for reading by another task"),
6646 m->strLocationFull.c_str());
6647 break;
6648 }
6649 case MediumState_LockedWrite:
6650 {
6651 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
6652 tr("Medium '%s' is locked for writing by another task"),
6653 m->strLocationFull.c_str());
6654 break;
6655 }
6656 case MediumState_Inaccessible:
6657 {
6658 /* be in sync with Console::powerUpThread() */
6659 if (!m->strLastAccessError.isEmpty())
6660 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
6661 tr("Medium '%s' is not accessible. %s"),
6662 m->strLocationFull.c_str(), m->strLastAccessError.c_str());
6663 else
6664 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
6665 tr("Medium '%s' is not accessible"),
6666 m->strLocationFull.c_str());
6667 break;
6668 }
6669 case MediumState_Creating:
6670 {
6671 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
6672 tr("Storage for the medium '%s' is being created"),
6673 m->strLocationFull.c_str());
6674 break;
6675 }
6676 case MediumState_Deleting:
6677 {
6678 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
6679 tr("Storage for the medium '%s' is being deleted"),
6680 m->strLocationFull.c_str());
6681 break;
6682 }
6683 default:
6684 {
6685 AssertFailed();
6686 break;
6687 }
6688 }
6689
6690 return rc;
6691}
6692
6693/**
6694 * Sets the value of m->strLocationFull. The given location must be a fully
6695 * qualified path; relative paths are not supported here.
6696 *
6697 * As a special exception, if the specified location is a file path that ends with '/'
6698 * then the file name part will be generated by this method automatically in the format
6699 * '{<uuid>}.<ext>' where <uuid> is a fresh UUID that this method will generate
6700 * and assign to this medium, and <ext> is the default extension for this
6701 * medium's storage format. Note that this procedure requires the media state to
6702 * be NotCreated and will return a failure otherwise.
6703 *
6704 * @param aLocation Location of the storage unit. If the location is a FS-path,
6705 * then it can be relative to the VirtualBox home directory.
6706 * @param aFormat Optional fallback format if it is an import and the format
6707 * cannot be determined.
6708 *
6709 * @note Must be called from under this object's write lock.
6710 */
6711HRESULT Medium::i_setLocation(const Utf8Str &aLocation,
6712 const Utf8Str &aFormat /* = Utf8Str::Empty */)
6713{
6714 AssertReturn(!aLocation.isEmpty(), E_FAIL);
6715
6716 AutoCaller autoCaller(this);
6717 AssertComRCReturnRC(autoCaller.rc());
6718
6719 /* formatObj may be null only when initializing from an existing path and
6720 * no format is known yet */
6721 AssertReturn( (!m->strFormat.isEmpty() && !m->formatObj.isNull())
6722 || ( getObjectState().getState() == ObjectState::InInit
6723 && m->state != MediumState_NotCreated
6724 && m->id.isZero()
6725 && m->strFormat.isEmpty()
6726 && m->formatObj.isNull()),
6727 E_FAIL);
6728
6729 /* are we dealing with a new medium constructed using the existing
6730 * location? */
6731 bool isImport = m->strFormat.isEmpty();
6732
6733 if ( isImport
6734 || ( (m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File)
6735 && !m->hostDrive))
6736 {
6737 Guid id;
6738
6739 Utf8Str locationFull(aLocation);
6740
6741 if (m->state == MediumState_NotCreated)
6742 {
6743 /* must be a file (formatObj must be already known) */
6744 Assert(m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File);
6745
6746 if (RTPathFilename(aLocation.c_str()) == NULL)
6747 {
6748 /* no file name is given (either an empty string or ends with a
6749 * slash), generate a new UUID + file name if the state allows
6750 * this */
6751
6752 ComAssertMsgRet(!m->formatObj->i_getFileExtensions().empty(),
6753 ("Must be at least one extension if it is MediumFormatCapabilities_File\n"),
6754 E_FAIL);
6755
6756 Utf8Str strExt = m->formatObj->i_getFileExtensions().front();
6757 ComAssertMsgRet(!strExt.isEmpty(),
6758 ("Default extension must not be empty\n"),
6759 E_FAIL);
6760
6761 id.create();
6762
6763 locationFull = Utf8StrFmt("%s{%RTuuid}.%s",
6764 aLocation.c_str(), id.raw(), strExt.c_str());
6765 }
6766 }
6767
6768 // we must always have full paths now (if it refers to a file)
6769 if ( ( m->formatObj.isNull()
6770 || m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File)
6771 && !RTPathStartsWithRoot(locationFull.c_str()))
6772 return setError(VBOX_E_FILE_ERROR,
6773 tr("The given path '%s' is not fully qualified"),
6774 locationFull.c_str());
6775
6776 /* detect the backend from the storage unit if importing */
6777 if (isImport)
6778 {
6779 VDTYPE enmType = VDTYPE_INVALID;
6780 char *backendName = NULL;
6781
6782 int vrc = VINF_SUCCESS;
6783
6784 /* is it a file? */
6785 {
6786 RTFILE file;
6787 vrc = RTFileOpen(&file, locationFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
6788 if (RT_SUCCESS(vrc))
6789 RTFileClose(file);
6790 }
6791 if (RT_SUCCESS(vrc))
6792 {
6793 vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
6794 locationFull.c_str(), &backendName, &enmType);
6795 }
6796 else if ( vrc != VERR_FILE_NOT_FOUND
6797 && vrc != VERR_PATH_NOT_FOUND
6798 && vrc != VERR_ACCESS_DENIED
6799 && locationFull != aLocation)
6800 {
6801 /* assume it's not a file, restore the original location */
6802 locationFull = aLocation;
6803 vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
6804 locationFull.c_str(), &backendName, &enmType);
6805 }
6806
6807 if (RT_FAILURE(vrc))
6808 {
6809 if (vrc == VERR_ACCESS_DENIED)
6810 return setError(VBOX_E_FILE_ERROR,
6811 tr("Permission problem accessing the file for the medium '%s' (%Rrc)"),
6812 locationFull.c_str(), vrc);
6813 else if (vrc == VERR_FILE_NOT_FOUND || vrc == VERR_PATH_NOT_FOUND)
6814 return setError(VBOX_E_FILE_ERROR,
6815 tr("Could not find file for the medium '%s' (%Rrc)"),
6816 locationFull.c_str(), vrc);
6817 else if (aFormat.isEmpty())
6818 return setError(VBOX_E_IPRT_ERROR,
6819 tr("Could not get the storage format of the medium '%s' (%Rrc)"),
6820 locationFull.c_str(), vrc);
6821 else
6822 {
6823 HRESULT rc = i_setFormat(aFormat);
6824 /* setFormat() must not fail since we've just used the backend so
6825 * the format object must be there */
6826 AssertComRCReturnRC(rc);
6827 }
6828 }
6829 else if ( enmType == VDTYPE_INVALID
6830 || m->devType != i_convertToDeviceType(enmType))
6831 {
6832 /*
6833 * The user tried to use a image as a device which is not supported
6834 * by the backend.
6835 */
6836 return setError(E_FAIL,
6837 tr("The medium '%s' can't be used as the requested device type"),
6838 locationFull.c_str());
6839 }
6840 else
6841 {
6842 ComAssertRet(backendName != NULL && *backendName != '\0', E_FAIL);
6843
6844 HRESULT rc = i_setFormat(backendName);
6845 RTStrFree(backendName);
6846
6847 /* setFormat() must not fail since we've just used the backend so
6848 * the format object must be there */
6849 AssertComRCReturnRC(rc);
6850 }
6851 }
6852
6853 m->strLocationFull = locationFull;
6854
6855 /* is it still a file? */
6856 if ( (m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File)
6857 && (m->state == MediumState_NotCreated)
6858 )
6859 /* assign a new UUID (this UUID will be used when calling
6860 * VDCreateBase/VDCreateDiff as a wanted UUID). Note that we
6861 * also do that if we didn't generate it to make sure it is
6862 * either generated by us or reset to null */
6863 unconst(m->id) = id;
6864 }
6865 else
6866 m->strLocationFull = aLocation;
6867
6868 return S_OK;
6869}
6870
6871/**
6872 * Checks that the format ID is valid and sets it on success.
6873 *
6874 * Note that this method will caller-reference the format object on success!
6875 * This reference must be released somewhere to let the MediumFormat object be
6876 * uninitialized.
6877 *
6878 * @note Must be called from under this object's write lock.
6879 */
6880HRESULT Medium::i_setFormat(const Utf8Str &aFormat)
6881{
6882 /* get the format object first */
6883 {
6884 SystemProperties *pSysProps = m->pVirtualBox->i_getSystemProperties();
6885 AutoReadLock propsLock(pSysProps COMMA_LOCKVAL_SRC_POS);
6886
6887 unconst(m->formatObj) = pSysProps->i_mediumFormat(aFormat);
6888 if (m->formatObj.isNull())
6889 return setError(E_INVALIDARG,
6890 tr("Invalid medium storage format '%s'"),
6891 aFormat.c_str());
6892
6893 /* get properties (preinsert them as keys in the map). Note that the
6894 * map doesn't grow over the object life time since the set of
6895 * properties is meant to be constant. */
6896
6897 Assert(m->mapProperties.empty());
6898
6899 for (MediumFormat::PropertyArray::const_iterator it = m->formatObj->i_getProperties().begin();
6900 it != m->formatObj->i_getProperties().end();
6901 ++it)
6902 {
6903 m->mapProperties.insert(std::make_pair(it->strName, Utf8Str::Empty));
6904 }
6905 }
6906
6907 unconst(m->strFormat) = aFormat;
6908
6909 return S_OK;
6910}
6911
6912/**
6913 * Converts the Medium device type to the VD type.
6914 */
6915VDTYPE Medium::i_convertDeviceType()
6916{
6917 VDTYPE enmType;
6918
6919 switch (m->devType)
6920 {
6921 case DeviceType_HardDisk:
6922 enmType = VDTYPE_HDD;
6923 break;
6924 case DeviceType_DVD:
6925 enmType = VDTYPE_DVD;
6926 break;
6927 case DeviceType_Floppy:
6928 enmType = VDTYPE_FLOPPY;
6929 break;
6930 default:
6931 ComAssertFailedRet(VDTYPE_INVALID);
6932 }
6933
6934 return enmType;
6935}
6936
6937/**
6938 * Converts from the VD type to the medium type.
6939 */
6940DeviceType_T Medium::i_convertToDeviceType(VDTYPE enmType)
6941{
6942 DeviceType_T devType;
6943
6944 switch (enmType)
6945 {
6946 case VDTYPE_HDD:
6947 devType = DeviceType_HardDisk;
6948 break;
6949 case VDTYPE_DVD:
6950 devType = DeviceType_DVD;
6951 break;
6952 case VDTYPE_FLOPPY:
6953 devType = DeviceType_Floppy;
6954 break;
6955 default:
6956 ComAssertFailedRet(DeviceType_Null);
6957 }
6958
6959 return devType;
6960}
6961
6962/**
6963 * Internal method which checks whether a property name is for a filter plugin.
6964 */
6965bool Medium::i_isPropertyForFilter(const com::Utf8Str &aName)
6966{
6967 /* If the name contains "/" use the part before as a filter name and lookup the filter. */
6968 size_t offSlash;
6969 if ((offSlash = aName.find("/", 0)) != aName.npos)
6970 {
6971 com::Utf8Str strFilter;
6972 com::Utf8Str strKey;
6973
6974 HRESULT rc = strFilter.assignEx(aName, 0, offSlash);
6975 if (FAILED(rc))
6976 return false;
6977
6978 rc = strKey.assignEx(aName, offSlash + 1, aName.length() - offSlash - 1); /* Skip slash */
6979 if (FAILED(rc))
6980 return false;
6981
6982 VDFILTERINFO FilterInfo;
6983 int vrc = VDFilterInfoOne(strFilter.c_str(), &FilterInfo);
6984 if (RT_SUCCESS(vrc))
6985 {
6986 /* Check that the property exists. */
6987 PCVDCONFIGINFO paConfig = FilterInfo.paConfigInfo;
6988 while (paConfig->pszKey)
6989 {
6990 if (strKey.equals(paConfig->pszKey))
6991 return true;
6992 paConfig++;
6993 }
6994 }
6995 }
6996
6997 return false;
6998}
6999
7000/**
7001 * Returns the last error message collected by the i_vdErrorCall callback and
7002 * resets it.
7003 *
7004 * The error message is returned prepended with a dot and a space, like this:
7005 * <code>
7006 * ". <error_text> (%Rrc)"
7007 * </code>
7008 * to make it easily appendable to a more general error message. The @c %Rrc
7009 * format string is given @a aVRC as an argument.
7010 *
7011 * If there is no last error message collected by i_vdErrorCall or if it is a
7012 * null or empty string, then this function returns the following text:
7013 * <code>
7014 * " (%Rrc)"
7015 * </code>
7016 *
7017 * @note Doesn't do any object locking; it is assumed that the caller makes sure
7018 * the callback isn't called by more than one thread at a time.
7019 *
7020 * @param aVRC VBox error code to use when no error message is provided.
7021 */
7022Utf8Str Medium::i_vdError(int aVRC)
7023{
7024 Utf8Str error;
7025
7026 if (m->vdError.isEmpty())
7027 error = Utf8StrFmt(" (%Rrc)", aVRC);
7028 else
7029 error = Utf8StrFmt(".\n%s", m->vdError.c_str());
7030
7031 m->vdError.setNull();
7032
7033 return error;
7034}
7035
7036/**
7037 * Error message callback.
7038 *
7039 * Puts the reported error message to the m->vdError field.
7040 *
7041 * @note Doesn't do any object locking; it is assumed that the caller makes sure
7042 * the callback isn't called by more than one thread at a time.
7043 *
7044 * @param pvUser The opaque data passed on container creation.
7045 * @param rc The VBox error code.
7046 * @param RT_SRC_POS_DECL Use RT_SRC_POS.
7047 * @param pszFormat Error message format string.
7048 * @param va Error message arguments.
7049 */
7050/*static*/
7051DECLCALLBACK(void) Medium::i_vdErrorCall(void *pvUser, int rc, RT_SRC_POS_DECL,
7052 const char *pszFormat, va_list va)
7053{
7054 NOREF(pszFile); NOREF(iLine); NOREF(pszFunction); /* RT_SRC_POS_DECL */
7055
7056 Medium *that = static_cast<Medium*>(pvUser);
7057 AssertReturnVoid(that != NULL);
7058
7059 if (that->m->vdError.isEmpty())
7060 that->m->vdError =
7061 Utf8StrFmt("%s (%Rrc)", Utf8Str(pszFormat, va).c_str(), rc);
7062 else
7063 that->m->vdError =
7064 Utf8StrFmt("%s.\n%s (%Rrc)", that->m->vdError.c_str(),
7065 Utf8Str(pszFormat, va).c_str(), rc);
7066}
7067
7068/* static */
7069DECLCALLBACK(bool) Medium::i_vdConfigAreKeysValid(void *pvUser,
7070 const char * /* pszzValid */)
7071{
7072 Medium *that = static_cast<Medium*>(pvUser);
7073 AssertReturn(that != NULL, false);
7074
7075 /* we always return true since the only keys we have are those found in
7076 * VDBACKENDINFO */
7077 return true;
7078}
7079
7080/* static */
7081DECLCALLBACK(int) Medium::i_vdConfigQuerySize(void *pvUser,
7082 const char *pszName,
7083 size_t *pcbValue)
7084{
7085 AssertReturn(VALID_PTR(pcbValue), VERR_INVALID_POINTER);
7086
7087 Medium *that = static_cast<Medium*>(pvUser);
7088 AssertReturn(that != NULL, VERR_GENERAL_FAILURE);
7089
7090 settings::StringsMap::const_iterator it = that->m->mapProperties.find(Utf8Str(pszName));
7091 if (it == that->m->mapProperties.end())
7092 return VERR_CFGM_VALUE_NOT_FOUND;
7093
7094 /* we interpret null values as "no value" in Medium */
7095 if (it->second.isEmpty())
7096 return VERR_CFGM_VALUE_NOT_FOUND;
7097
7098 *pcbValue = it->second.length() + 1 /* include terminator */;
7099
7100 return VINF_SUCCESS;
7101}
7102
7103/* static */
7104DECLCALLBACK(int) Medium::i_vdConfigQuery(void *pvUser,
7105 const char *pszName,
7106 char *pszValue,
7107 size_t cchValue)
7108{
7109 AssertReturn(VALID_PTR(pszValue), VERR_INVALID_POINTER);
7110
7111 Medium *that = static_cast<Medium*>(pvUser);
7112 AssertReturn(that != NULL, VERR_GENERAL_FAILURE);
7113
7114 settings::StringsMap::const_iterator it = that->m->mapProperties.find(Utf8Str(pszName));
7115 if (it == that->m->mapProperties.end())
7116 return VERR_CFGM_VALUE_NOT_FOUND;
7117
7118 /* we interpret null values as "no value" in Medium */
7119 if (it->second.isEmpty())
7120 return VERR_CFGM_VALUE_NOT_FOUND;
7121
7122 const Utf8Str &value = it->second;
7123 if (value.length() >= cchValue)
7124 return VERR_CFGM_NOT_ENOUGH_SPACE;
7125
7126 memcpy(pszValue, value.c_str(), value.length() + 1);
7127
7128 return VINF_SUCCESS;
7129}
7130
7131DECLCALLBACK(int) Medium::i_vdTcpSocketCreate(uint32_t fFlags, PVDSOCKET pSock)
7132{
7133 PVDSOCKETINT pSocketInt = NULL;
7134
7135 if ((fFlags & VD_INTERFACETCPNET_CONNECT_EXTENDED_SELECT) != 0)
7136 return VERR_NOT_SUPPORTED;
7137
7138 pSocketInt = (PVDSOCKETINT)RTMemAllocZ(sizeof(VDSOCKETINT));
7139 if (!pSocketInt)
7140 return VERR_NO_MEMORY;
7141
7142 pSocketInt->hSocket = NIL_RTSOCKET;
7143 *pSock = pSocketInt;
7144 return VINF_SUCCESS;
7145}
7146
7147DECLCALLBACK(int) Medium::i_vdTcpSocketDestroy(VDSOCKET Sock)
7148{
7149 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
7150
7151 if (pSocketInt->hSocket != NIL_RTSOCKET)
7152 RTTcpClientCloseEx(pSocketInt->hSocket, false /*fGracefulShutdown*/);
7153
7154 RTMemFree(pSocketInt);
7155
7156 return VINF_SUCCESS;
7157}
7158
7159DECLCALLBACK(int) Medium::i_vdTcpClientConnect(VDSOCKET Sock, const char *pszAddress, uint32_t uPort,
7160 RTMSINTERVAL cMillies)
7161{
7162 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
7163
7164 return RTTcpClientConnectEx(pszAddress, uPort, &pSocketInt->hSocket, cMillies, NULL);
7165}
7166
7167DECLCALLBACK(int) Medium::i_vdTcpClientClose(VDSOCKET Sock)
7168{
7169 int rc = VINF_SUCCESS;
7170 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
7171
7172 rc = RTTcpClientCloseEx(pSocketInt->hSocket, false /*fGracefulShutdown*/);
7173 pSocketInt->hSocket = NIL_RTSOCKET;
7174 return rc;
7175}
7176
7177DECLCALLBACK(bool) Medium::i_vdTcpIsClientConnected(VDSOCKET Sock)
7178{
7179 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
7180 return pSocketInt->hSocket != NIL_RTSOCKET;
7181}
7182
7183DECLCALLBACK(int) Medium::i_vdTcpSelectOne(VDSOCKET Sock, RTMSINTERVAL cMillies)
7184{
7185 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
7186 return RTTcpSelectOne(pSocketInt->hSocket, cMillies);
7187}
7188
7189DECLCALLBACK(int) Medium::i_vdTcpRead(VDSOCKET Sock, void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
7190{
7191 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
7192 return RTTcpRead(pSocketInt->hSocket, pvBuffer, cbBuffer, pcbRead);
7193}
7194
7195DECLCALLBACK(int) Medium::i_vdTcpWrite(VDSOCKET Sock, const void *pvBuffer, size_t cbBuffer)
7196{
7197 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
7198 return RTTcpWrite(pSocketInt->hSocket, pvBuffer, cbBuffer);
7199}
7200
7201DECLCALLBACK(int) Medium::i_vdTcpSgWrite(VDSOCKET Sock, PCRTSGBUF pSgBuf)
7202{
7203 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
7204 return RTTcpSgWrite(pSocketInt->hSocket, pSgBuf);
7205}
7206
7207DECLCALLBACK(int) Medium::i_vdTcpFlush(VDSOCKET Sock)
7208{
7209 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
7210 return RTTcpFlush(pSocketInt->hSocket);
7211}
7212
7213DECLCALLBACK(int) Medium::i_vdTcpSetSendCoalescing(VDSOCKET Sock, bool fEnable)
7214{
7215 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
7216 return RTTcpSetSendCoalescing(pSocketInt->hSocket, fEnable);
7217}
7218
7219DECLCALLBACK(int) Medium::i_vdTcpGetLocalAddress(VDSOCKET Sock, PRTNETADDR pAddr)
7220{
7221 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
7222 return RTTcpGetLocalAddress(pSocketInt->hSocket, pAddr);
7223}
7224
7225DECLCALLBACK(int) Medium::i_vdTcpGetPeerAddress(VDSOCKET Sock, PRTNETADDR pAddr)
7226{
7227 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
7228 return RTTcpGetPeerAddress(pSocketInt->hSocket, pAddr);
7229}
7230
7231DECLCALLBACK(bool) Medium::i_vdCryptoConfigAreKeysValid(void *pvUser, const char *pszzValid)
7232{
7233 /* Just return always true here. */
7234 NOREF(pvUser);
7235 NOREF(pszzValid);
7236 return true;
7237}
7238
7239DECLCALLBACK(int) Medium::i_vdCryptoConfigQuerySize(void *pvUser, const char *pszName, size_t *pcbValue)
7240{
7241 Medium::CryptoFilterSettings *pSettings = (Medium::CryptoFilterSettings *)pvUser;
7242 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
7243 AssertReturn(VALID_PTR(pcbValue), VERR_INVALID_POINTER);
7244
7245 size_t cbValue = 0;
7246 if (!strcmp(pszName, "Algorithm"))
7247 cbValue = strlen(pSettings->pszCipher) + 1;
7248 else if (!strcmp(pszName, "KeyId"))
7249 cbValue = sizeof("irrelevant");
7250 else if (!strcmp(pszName, "KeyStore"))
7251 {
7252 if (!pSettings->pszKeyStoreLoad)
7253 return VERR_CFGM_VALUE_NOT_FOUND;
7254 cbValue = strlen(pSettings->pszKeyStoreLoad) + 1;
7255 }
7256 else if (!strcmp(pszName, "CreateKeyStore"))
7257 cbValue = 2; /* Single digit + terminator. */
7258 else
7259 return VERR_CFGM_VALUE_NOT_FOUND;
7260
7261 *pcbValue = cbValue + 1 /* include terminator */;
7262
7263 return VINF_SUCCESS;
7264}
7265
7266DECLCALLBACK(int) Medium::i_vdCryptoConfigQuery(void *pvUser, const char *pszName,
7267 char *pszValue, size_t cchValue)
7268{
7269 Medium::CryptoFilterSettings *pSettings = (Medium::CryptoFilterSettings *)pvUser;
7270 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
7271 AssertReturn(VALID_PTR(pszValue), VERR_INVALID_POINTER);
7272
7273 const char *psz = NULL;
7274 if (!strcmp(pszName, "Algorithm"))
7275 psz = pSettings->pszCipher;
7276 else if (!strcmp(pszName, "KeyId"))
7277 psz = "irrelevant";
7278 else if (!strcmp(pszName, "KeyStore"))
7279 psz = pSettings->pszKeyStoreLoad;
7280 else if (!strcmp(pszName, "CreateKeyStore"))
7281 {
7282 if (pSettings->fCreateKeyStore)
7283 psz = "1";
7284 else
7285 psz = "0";
7286 }
7287 else
7288 return VERR_CFGM_VALUE_NOT_FOUND;
7289
7290 size_t cch = strlen(psz);
7291 if (cch >= cchValue)
7292 return VERR_CFGM_NOT_ENOUGH_SPACE;
7293
7294 memcpy(pszValue, psz, cch + 1);
7295 return VINF_SUCCESS;
7296}
7297
7298DECLCALLBACK(int) Medium::i_vdCryptoKeyRetain(void *pvUser, const char *pszId,
7299 const uint8_t **ppbKey, size_t *pcbKey)
7300{
7301 Medium::CryptoFilterSettings *pSettings = (Medium::CryptoFilterSettings *)pvUser;
7302 NOREF(pszId);
7303 NOREF(ppbKey);
7304 NOREF(pcbKey);
7305 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
7306 AssertMsgFailedReturn(("This method should not be called here!\n"), VERR_INVALID_STATE);
7307}
7308
7309DECLCALLBACK(int) Medium::i_vdCryptoKeyRelease(void *pvUser, const char *pszId)
7310{
7311 Medium::CryptoFilterSettings *pSettings = (Medium::CryptoFilterSettings *)pvUser;
7312 NOREF(pszId);
7313 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
7314 AssertMsgFailedReturn(("This method should not be called here!\n"), VERR_INVALID_STATE);
7315}
7316
7317DECLCALLBACK(int) Medium::i_vdCryptoKeyStorePasswordRetain(void *pvUser, const char *pszId, const char **ppszPassword)
7318{
7319 Medium::CryptoFilterSettings *pSettings = (Medium::CryptoFilterSettings *)pvUser;
7320 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
7321
7322 NOREF(pszId);
7323 *ppszPassword = pSettings->pszPassword;
7324 return VINF_SUCCESS;
7325}
7326
7327DECLCALLBACK(int) Medium::i_vdCryptoKeyStorePasswordRelease(void *pvUser, const char *pszId)
7328{
7329 Medium::CryptoFilterSettings *pSettings = (Medium::CryptoFilterSettings *)pvUser;
7330 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
7331 NOREF(pszId);
7332 return VINF_SUCCESS;
7333}
7334
7335DECLCALLBACK(int) Medium::i_vdCryptoKeyStoreSave(void *pvUser, const void *pvKeyStore, size_t cbKeyStore)
7336{
7337 Medium::CryptoFilterSettings *pSettings = (Medium::CryptoFilterSettings *)pvUser;
7338 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
7339
7340 pSettings->pszKeyStore = (char *)RTMemAllocZ(cbKeyStore);
7341 if (!pSettings->pszKeyStore)
7342 return VERR_NO_MEMORY;
7343
7344 memcpy(pSettings->pszKeyStore, pvKeyStore, cbKeyStore);
7345 return VINF_SUCCESS;
7346}
7347
7348DECLCALLBACK(int) Medium::i_vdCryptoKeyStoreReturnParameters(void *pvUser, const char *pszCipher,
7349 const uint8_t *pbDek, size_t cbDek)
7350{
7351 Medium::CryptoFilterSettings *pSettings = (Medium::CryptoFilterSettings *)pvUser;
7352 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
7353
7354 pSettings->pszCipherReturned = RTStrDup(pszCipher);
7355 pSettings->pbDek = pbDek;
7356 pSettings->cbDek = cbDek;
7357
7358 return pSettings->pszCipherReturned ? VINF_SUCCESS : VERR_NO_MEMORY;
7359}
7360
7361/**
7362 * Starts a new thread driven by the appropriate Medium::Task::handler() method.
7363 *
7364 * @note When the task is executed by this method, IProgress::notifyComplete()
7365 * is automatically called for the progress object associated with this
7366 * task when the task is finished to signal the operation completion for
7367 * other threads asynchronously waiting for it.
7368 */
7369HRESULT Medium::i_startThread(Medium::Task *pTask)
7370{
7371#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
7372 /* Extreme paranoia: The calling thread should not hold the medium
7373 * tree lock or any medium lock. Since there is no separate lock class
7374 * for medium objects be even more strict: no other object locks. */
7375 Assert(!AutoLockHoldsLocksInClass(LOCKCLASS_LISTOFMEDIA));
7376 Assert(!AutoLockHoldsLocksInClass(getLockingClass()));
7377#endif
7378
7379 /// @todo use a more descriptive task name
7380 int vrc = RTThreadCreate(NULL, Medium::Task::fntMediumTask, pTask,
7381 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
7382 "Medium::Task");
7383 if (RT_FAILURE(vrc))
7384 {
7385 delete pTask;
7386 return setError(E_FAIL, "Could not create Medium::Task thread (%Rrc)\n", vrc);
7387 }
7388
7389 return S_OK;
7390}
7391
7392/**
7393 * Runs Medium::Task::handler() on the current thread instead of creating
7394 * a new one.
7395 *
7396 * This call implies that it is made on another temporary thread created for
7397 * some asynchronous task. Avoid calling it from a normal thread since the task
7398 * operations are potentially lengthy and will block the calling thread in this
7399 * case.
7400 *
7401 * @note When the task is executed by this method, IProgress::notifyComplete()
7402 * is not called for the progress object associated with this task when
7403 * the task is finished. Instead, the result of the operation is returned
7404 * by this method directly and it's the caller's responsibility to
7405 * complete the progress object in this case.
7406 */
7407HRESULT Medium::i_runNow(Medium::Task *pTask)
7408{
7409#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
7410 /* Extreme paranoia: The calling thread should not hold the medium
7411 * tree lock or any medium lock. Since there is no separate lock class
7412 * for medium objects be even more strict: no other object locks. */
7413 Assert(!AutoLockHoldsLocksInClass(LOCKCLASS_LISTOFMEDIA));
7414 Assert(!AutoLockHoldsLocksInClass(getLockingClass()));
7415#endif
7416
7417 /* NIL_RTTHREAD indicates synchronous call. */
7418 return (HRESULT)Medium::Task::fntMediumTask(NIL_RTTHREAD, pTask);
7419}
7420
7421/**
7422 * Implementation code for the "create base" task.
7423 *
7424 * This only gets started from Medium::CreateBaseStorage() and always runs
7425 * asynchronously. As a result, we always save the VirtualBox.xml file when
7426 * we're done here.
7427 *
7428 * @param task
7429 * @return
7430 */
7431HRESULT Medium::i_taskCreateBaseHandler(Medium::CreateBaseTask &task)
7432{
7433 HRESULT rc = S_OK;
7434
7435 /* these parameters we need after creation */
7436 uint64_t size = 0, logicalSize = 0;
7437 MediumVariant_T variant = MediumVariant_Standard;
7438 bool fGenerateUuid = false;
7439
7440 try
7441 {
7442 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
7443
7444 /* The object may request a specific UUID (through a special form of
7445 * the setLocation() argument). Otherwise we have to generate it */
7446 Guid id = m->id;
7447
7448 fGenerateUuid = id.isZero();
7449 if (fGenerateUuid)
7450 {
7451 id.create();
7452 /* VirtualBox::i_registerMedium() will need UUID */
7453 unconst(m->id) = id;
7454 }
7455
7456 Utf8Str format(m->strFormat);
7457 Utf8Str location(m->strLocationFull);
7458 uint64_t capabilities = m->formatObj->i_getCapabilities();
7459 ComAssertThrow(capabilities & ( MediumFormatCapabilities_CreateFixed
7460 | MediumFormatCapabilities_CreateDynamic), E_FAIL);
7461 Assert(m->state == MediumState_Creating);
7462
7463 PVBOXHDD hdd;
7464 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
7465 ComAssertRCThrow(vrc, E_FAIL);
7466
7467 /* unlock before the potentially lengthy operation */
7468 thisLock.release();
7469
7470 try
7471 {
7472 /* ensure the directory exists */
7473 if (capabilities & MediumFormatCapabilities_File)
7474 {
7475 rc = VirtualBox::i_ensureFilePathExists(location, !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
7476 if (FAILED(rc))
7477 throw rc;
7478 }
7479
7480 VDGEOMETRY geo = { 0, 0, 0 }; /* auto-detect */
7481
7482 vrc = VDCreateBase(hdd,
7483 format.c_str(),
7484 location.c_str(),
7485 task.mSize,
7486 task.mVariant & ~MediumVariant_NoCreateDir,
7487 NULL,
7488 &geo,
7489 &geo,
7490 id.raw(),
7491 VD_OPEN_FLAGS_NORMAL | m->uOpenFlagsDef,
7492 m->vdImageIfaces,
7493 task.mVDOperationIfaces);
7494 if (RT_FAILURE(vrc))
7495 {
7496 if (vrc == VERR_VD_INVALID_TYPE)
7497 throw setError(VBOX_E_FILE_ERROR,
7498 tr("Parameters for creating the medium storage unit '%s' are invalid%s"),
7499 location.c_str(), i_vdError(vrc).c_str());
7500 else
7501 throw setError(VBOX_E_FILE_ERROR,
7502 tr("Could not create the medium storage unit '%s'%s"),
7503 location.c_str(), i_vdError(vrc).c_str());
7504 }
7505
7506 size = VDGetFileSize(hdd, 0);
7507 logicalSize = VDGetSize(hdd, 0);
7508 unsigned uImageFlags;
7509 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
7510 if (RT_SUCCESS(vrc))
7511 variant = (MediumVariant_T)uImageFlags;
7512 }
7513 catch (HRESULT aRC) { rc = aRC; }
7514
7515 VDDestroy(hdd);
7516 }
7517 catch (HRESULT aRC) { rc = aRC; }
7518
7519 if (SUCCEEDED(rc))
7520 {
7521 /* register with mVirtualBox as the last step and move to
7522 * Created state only on success (leaving an orphan file is
7523 * better than breaking media registry consistency) */
7524 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
7525 ComObjPtr<Medium> pMedium;
7526 rc = m->pVirtualBox->i_registerMedium(this, &pMedium, treeLock);
7527 Assert(this == pMedium);
7528 }
7529
7530 // re-acquire the lock before changing state
7531 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
7532
7533 if (SUCCEEDED(rc))
7534 {
7535 m->state = MediumState_Created;
7536
7537 m->size = size;
7538 m->logicalSize = logicalSize;
7539 m->variant = variant;
7540
7541 thisLock.release();
7542 i_markRegistriesModified();
7543 if (task.isAsync())
7544 {
7545 // in asynchronous mode, save settings now
7546 m->pVirtualBox->i_saveModifiedRegistries();
7547 }
7548 }
7549 else
7550 {
7551 /* back to NotCreated on failure */
7552 m->state = MediumState_NotCreated;
7553
7554 /* reset UUID to prevent it from being reused next time */
7555 if (fGenerateUuid)
7556 unconst(m->id).clear();
7557 }
7558
7559 return rc;
7560}
7561
7562/**
7563 * Implementation code for the "create diff" task.
7564 *
7565 * This task always gets started from Medium::createDiffStorage() and can run
7566 * synchronously or asynchronously depending on the "wait" parameter passed to
7567 * that function. If we run synchronously, the caller expects the medium
7568 * registry modification to be set before returning; otherwise (in asynchronous
7569 * mode), we save the settings ourselves.
7570 *
7571 * @param task
7572 * @return
7573 */
7574HRESULT Medium::i_taskCreateDiffHandler(Medium::CreateDiffTask &task)
7575{
7576 HRESULT rcTmp = S_OK;
7577
7578 const ComObjPtr<Medium> &pTarget = task.mTarget;
7579
7580 uint64_t size = 0, logicalSize = 0;
7581 MediumVariant_T variant = MediumVariant_Standard;
7582 bool fGenerateUuid = false;
7583
7584 try
7585 {
7586 if (i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX)
7587 {
7588 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7589 throw setError(VBOX_E_INVALID_OBJECT_STATE,
7590 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"),
7591 m->strLocationFull.c_str());
7592 }
7593
7594 /* Lock both in {parent,child} order. */
7595 AutoMultiWriteLock2 mediaLock(this, pTarget COMMA_LOCKVAL_SRC_POS);
7596
7597 /* The object may request a specific UUID (through a special form of
7598 * the setLocation() argument). Otherwise we have to generate it */
7599 Guid targetId = pTarget->m->id;
7600
7601 fGenerateUuid = targetId.isZero();
7602 if (fGenerateUuid)
7603 {
7604 targetId.create();
7605 /* VirtualBox::i_registerMedium() will need UUID */
7606 unconst(pTarget->m->id) = targetId;
7607 }
7608
7609 Guid id = m->id;
7610
7611 Utf8Str targetFormat(pTarget->m->strFormat);
7612 Utf8Str targetLocation(pTarget->m->strLocationFull);
7613 uint64_t capabilities = pTarget->m->formatObj->i_getCapabilities();
7614 ComAssertThrow(capabilities & MediumFormatCapabilities_CreateDynamic, E_FAIL);
7615
7616 Assert(pTarget->m->state == MediumState_Creating);
7617 Assert(m->state == MediumState_LockedRead);
7618
7619 PVBOXHDD hdd;
7620 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
7621 ComAssertRCThrow(vrc, E_FAIL);
7622
7623 /* the two media are now protected by their non-default states;
7624 * unlock the media before the potentially lengthy operation */
7625 mediaLock.release();
7626
7627 try
7628 {
7629 /* Open all media in the target chain but the last. */
7630 MediumLockList::Base::const_iterator targetListBegin =
7631 task.mpMediumLockList->GetBegin();
7632 MediumLockList::Base::const_iterator targetListEnd =
7633 task.mpMediumLockList->GetEnd();
7634 for (MediumLockList::Base::const_iterator it = targetListBegin;
7635 it != targetListEnd;
7636 ++it)
7637 {
7638 const MediumLock &mediumLock = *it;
7639 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
7640
7641 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
7642
7643 /* Skip over the target diff medium */
7644 if (pMedium->m->state == MediumState_Creating)
7645 continue;
7646
7647 /* sanity check */
7648 Assert(pMedium->m->state == MediumState_LockedRead);
7649
7650 /* Open all media in appropriate mode. */
7651 vrc = VDOpen(hdd,
7652 pMedium->m->strFormat.c_str(),
7653 pMedium->m->strLocationFull.c_str(),
7654 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
7655 pMedium->m->vdImageIfaces);
7656 if (RT_FAILURE(vrc))
7657 throw setError(VBOX_E_FILE_ERROR,
7658 tr("Could not open the medium storage unit '%s'%s"),
7659 pMedium->m->strLocationFull.c_str(),
7660 i_vdError(vrc).c_str());
7661 }
7662
7663 /* ensure the target directory exists */
7664 if (capabilities & MediumFormatCapabilities_File)
7665 {
7666 HRESULT rc = VirtualBox::i_ensureFilePathExists(targetLocation,
7667 !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
7668 if (FAILED(rc))
7669 throw rc;
7670 }
7671
7672 vrc = VDCreateDiff(hdd,
7673 targetFormat.c_str(),
7674 targetLocation.c_str(),
7675 (task.mVariant & ~MediumVariant_NoCreateDir) | VD_IMAGE_FLAGS_DIFF,
7676 NULL,
7677 targetId.raw(),
7678 id.raw(),
7679 VD_OPEN_FLAGS_NORMAL | m->uOpenFlagsDef,
7680 pTarget->m->vdImageIfaces,
7681 task.mVDOperationIfaces);
7682 if (RT_FAILURE(vrc))
7683 {
7684 if (vrc == VERR_VD_INVALID_TYPE)
7685 throw setError(VBOX_E_FILE_ERROR,
7686 tr("Parameters for creating the differencing medium storage unit '%s' are invalid%s"),
7687 targetLocation.c_str(), i_vdError(vrc).c_str());
7688 else
7689 throw setError(VBOX_E_FILE_ERROR,
7690 tr("Could not create the differencing medium storage unit '%s'%s"),
7691 targetLocation.c_str(), i_vdError(vrc).c_str());
7692 }
7693
7694 size = VDGetFileSize(hdd, VD_LAST_IMAGE);
7695 logicalSize = VDGetSize(hdd, VD_LAST_IMAGE);
7696 unsigned uImageFlags;
7697 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
7698 if (RT_SUCCESS(vrc))
7699 variant = (MediumVariant_T)uImageFlags;
7700 }
7701 catch (HRESULT aRC) { rcTmp = aRC; }
7702
7703 VDDestroy(hdd);
7704 }
7705 catch (HRESULT aRC) { rcTmp = aRC; }
7706
7707 MultiResult mrc(rcTmp);
7708
7709 if (SUCCEEDED(mrc))
7710 {
7711 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
7712
7713 Assert(pTarget->m->pParent.isNull());
7714
7715 /* associate child with the parent, maximum depth was checked above */
7716 pTarget->i_setParent(this);
7717
7718 /* diffs for immutable media are auto-reset by default */
7719 bool fAutoReset;
7720 {
7721 ComObjPtr<Medium> pBase = i_getBase();
7722 AutoReadLock block(pBase COMMA_LOCKVAL_SRC_POS);
7723 fAutoReset = (pBase->m->type == MediumType_Immutable);
7724 }
7725 {
7726 AutoWriteLock tlock(pTarget COMMA_LOCKVAL_SRC_POS);
7727 pTarget->m->autoReset = fAutoReset;
7728 }
7729
7730 /* register with mVirtualBox as the last step and move to
7731 * Created state only on success (leaving an orphan file is
7732 * better than breaking media registry consistency) */
7733 ComObjPtr<Medium> pMedium;
7734 mrc = m->pVirtualBox->i_registerMedium(pTarget, &pMedium, treeLock);
7735 Assert(pTarget == pMedium);
7736
7737 if (FAILED(mrc))
7738 /* break the parent association on failure to register */
7739 i_deparent();
7740 }
7741
7742 AutoMultiWriteLock2 mediaLock(this, pTarget COMMA_LOCKVAL_SRC_POS);
7743
7744 if (SUCCEEDED(mrc))
7745 {
7746 pTarget->m->state = MediumState_Created;
7747
7748 pTarget->m->size = size;
7749 pTarget->m->logicalSize = logicalSize;
7750 pTarget->m->variant = variant;
7751 }
7752 else
7753 {
7754 /* back to NotCreated on failure */
7755 pTarget->m->state = MediumState_NotCreated;
7756
7757 pTarget->m->autoReset = false;
7758
7759 /* reset UUID to prevent it from being reused next time */
7760 if (fGenerateUuid)
7761 unconst(pTarget->m->id).clear();
7762 }
7763
7764 // deregister the task registered in createDiffStorage()
7765 Assert(m->numCreateDiffTasks != 0);
7766 --m->numCreateDiffTasks;
7767
7768 mediaLock.release();
7769 i_markRegistriesModified();
7770 if (task.isAsync())
7771 {
7772 // in asynchronous mode, save settings now
7773 m->pVirtualBox->i_saveModifiedRegistries();
7774 }
7775
7776 /* Note that in sync mode, it's the caller's responsibility to
7777 * unlock the medium. */
7778
7779 return mrc;
7780}
7781
7782/**
7783 * Implementation code for the "merge" task.
7784 *
7785 * This task always gets started from Medium::mergeTo() and can run
7786 * synchronously or asynchronously depending on the "wait" parameter passed to
7787 * that function. If we run synchronously, the caller expects the medium
7788 * registry modification to be set before returning; otherwise (in asynchronous
7789 * mode), we save the settings ourselves.
7790 *
7791 * @param task
7792 * @return
7793 */
7794HRESULT Medium::i_taskMergeHandler(Medium::MergeTask &task)
7795{
7796 HRESULT rcTmp = S_OK;
7797
7798 const ComObjPtr<Medium> &pTarget = task.mTarget;
7799
7800 try
7801 {
7802 if (!task.mParentForTarget.isNull())
7803 if (task.mParentForTarget->i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX)
7804 {
7805 AutoReadLock plock(task.mParentForTarget COMMA_LOCKVAL_SRC_POS);
7806 throw setError(VBOX_E_INVALID_OBJECT_STATE,
7807 tr("Cannot merge image for medium '%s', because it exceeds the medium tree depth limit. Please merge some images which you no longer need"),
7808 task.mParentForTarget->m->strLocationFull.c_str());
7809 }
7810
7811 PVBOXHDD hdd;
7812 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
7813 ComAssertRCThrow(vrc, E_FAIL);
7814
7815 try
7816 {
7817 // Similar code appears in SessionMachine::onlineMergeMedium, so
7818 // if you make any changes below check whether they are applicable
7819 // in that context as well.
7820
7821 unsigned uTargetIdx = VD_LAST_IMAGE;
7822 unsigned uSourceIdx = VD_LAST_IMAGE;
7823 /* Open all media in the chain. */
7824 MediumLockList::Base::iterator lockListBegin =
7825 task.mpMediumLockList->GetBegin();
7826 MediumLockList::Base::iterator lockListEnd =
7827 task.mpMediumLockList->GetEnd();
7828 unsigned i = 0;
7829 for (MediumLockList::Base::iterator it = lockListBegin;
7830 it != lockListEnd;
7831 ++it)
7832 {
7833 MediumLock &mediumLock = *it;
7834 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
7835
7836 if (pMedium == this)
7837 uSourceIdx = i;
7838 else if (pMedium == pTarget)
7839 uTargetIdx = i;
7840
7841 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
7842
7843 /*
7844 * complex sanity (sane complexity)
7845 *
7846 * The current medium must be in the Deleting (medium is merged)
7847 * or LockedRead (parent medium) state if it is not the target.
7848 * If it is the target it must be in the LockedWrite state.
7849 */
7850#ifndef DEBUG_andy /* See see @bugref{8172}. */
7851 Assert( ( pMedium != pTarget
7852 && ( pMedium->m->state == MediumState_Deleting
7853 || pMedium->m->state == MediumState_LockedRead))
7854 || ( pMedium == pTarget
7855 && pMedium->m->state == MediumState_LockedWrite));
7856#endif
7857 /*
7858 * Medium must be the target, in the LockedRead state
7859 * or Deleting state where it is not allowed to be attached
7860 * to a virtual machine.
7861 */
7862 Assert( pMedium == pTarget
7863 || pMedium->m->state == MediumState_LockedRead
7864 || ( pMedium->m->backRefs.size() == 0
7865 && pMedium->m->state == MediumState_Deleting));
7866 /* The source medium must be in Deleting state. */
7867 Assert( pMedium != this
7868 || pMedium->m->state == MediumState_Deleting);
7869
7870 unsigned uOpenFlags = VD_OPEN_FLAGS_NORMAL;
7871
7872 if ( pMedium->m->state == MediumState_LockedRead
7873 || pMedium->m->state == MediumState_Deleting)
7874 uOpenFlags = VD_OPEN_FLAGS_READONLY;
7875 if (pMedium->m->type == MediumType_Shareable)
7876 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
7877
7878 /* Open the medium */
7879 vrc = VDOpen(hdd,
7880 pMedium->m->strFormat.c_str(),
7881 pMedium->m->strLocationFull.c_str(),
7882 uOpenFlags | m->uOpenFlagsDef,
7883 pMedium->m->vdImageIfaces);
7884 if (RT_FAILURE(vrc))
7885 throw vrc;
7886
7887 i++;
7888 }
7889
7890 ComAssertThrow( uSourceIdx != VD_LAST_IMAGE
7891 && uTargetIdx != VD_LAST_IMAGE, E_FAIL);
7892
7893 vrc = VDMerge(hdd, uSourceIdx, uTargetIdx,
7894 task.mVDOperationIfaces);
7895 if (RT_FAILURE(vrc))
7896 throw vrc;
7897
7898 /* update parent UUIDs */
7899 if (!task.mfMergeForward)
7900 {
7901 /* we need to update UUIDs of all source's children
7902 * which cannot be part of the container at once so
7903 * add each one in there individually */
7904 if (task.mpChildrenToReparent)
7905 {
7906 MediumLockList::Base::iterator childrenBegin = task.mpChildrenToReparent->GetBegin();
7907 MediumLockList::Base::iterator childrenEnd = task.mpChildrenToReparent->GetEnd();
7908 for (MediumLockList::Base::iterator it = childrenBegin;
7909 it != childrenEnd;
7910 ++it)
7911 {
7912 Medium *pMedium = it->GetMedium();
7913 /* VD_OPEN_FLAGS_INFO since UUID is wrong yet */
7914 vrc = VDOpen(hdd,
7915 pMedium->m->strFormat.c_str(),
7916 pMedium->m->strLocationFull.c_str(),
7917 VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
7918 pMedium->m->vdImageIfaces);
7919 if (RT_FAILURE(vrc))
7920 throw vrc;
7921
7922 vrc = VDSetParentUuid(hdd, VD_LAST_IMAGE,
7923 pTarget->m->id.raw());
7924 if (RT_FAILURE(vrc))
7925 throw vrc;
7926
7927 vrc = VDClose(hdd, false /* fDelete */);
7928 if (RT_FAILURE(vrc))
7929 throw vrc;
7930 }
7931 }
7932 }
7933 }
7934 catch (HRESULT aRC) { rcTmp = aRC; }
7935 catch (int aVRC)
7936 {
7937 rcTmp = setError(VBOX_E_FILE_ERROR,
7938 tr("Could not merge the medium '%s' to '%s'%s"),
7939 m->strLocationFull.c_str(),
7940 pTarget->m->strLocationFull.c_str(),
7941 i_vdError(aVRC).c_str());
7942 }
7943
7944 VDDestroy(hdd);
7945 }
7946 catch (HRESULT aRC) { rcTmp = aRC; }
7947
7948 ErrorInfoKeeper eik;
7949 MultiResult mrc(rcTmp);
7950 HRESULT rc2;
7951
7952 if (SUCCEEDED(mrc))
7953 {
7954 /* all media but the target were successfully deleted by
7955 * VDMerge; reparent the last one and uninitialize deleted media. */
7956
7957 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
7958
7959 if (task.mfMergeForward)
7960 {
7961 /* first, unregister the target since it may become a base
7962 * medium which needs re-registration */
7963 rc2 = m->pVirtualBox->i_unregisterMedium(pTarget);
7964 AssertComRC(rc2);
7965
7966 /* then, reparent it and disconnect the deleted branch at both ends
7967 * (chain->parent() is source's parent). Depth check above. */
7968 pTarget->i_deparent();
7969 pTarget->i_setParent(task.mParentForTarget);
7970 if (task.mParentForTarget)
7971 i_deparent();
7972
7973 /* then, register again */
7974 ComObjPtr<Medium> pMedium;
7975 rc2 = m->pVirtualBox->i_registerMedium(pTarget, &pMedium,
7976 treeLock);
7977 AssertComRC(rc2);
7978 }
7979 else
7980 {
7981 Assert(pTarget->i_getChildren().size() == 1);
7982 Medium *targetChild = pTarget->i_getChildren().front();
7983
7984 /* disconnect the deleted branch at the elder end */
7985 targetChild->i_deparent();
7986
7987 /* reparent source's children and disconnect the deleted
7988 * branch at the younger end */
7989 if (task.mpChildrenToReparent)
7990 {
7991 /* obey {parent,child} lock order */
7992 AutoWriteLock sourceLock(this COMMA_LOCKVAL_SRC_POS);
7993
7994 MediumLockList::Base::iterator childrenBegin = task.mpChildrenToReparent->GetBegin();
7995 MediumLockList::Base::iterator childrenEnd = task.mpChildrenToReparent->GetEnd();
7996 for (MediumLockList::Base::iterator it = childrenBegin;
7997 it != childrenEnd;
7998 ++it)
7999 {
8000 Medium *pMedium = it->GetMedium();
8001 AutoWriteLock childLock(pMedium COMMA_LOCKVAL_SRC_POS);
8002
8003 pMedium->i_deparent(); // removes pMedium from source
8004 // no depth check, reduces depth
8005 pMedium->i_setParent(pTarget);
8006 }
8007 }
8008 }
8009
8010 /* unregister and uninitialize all media removed by the merge */
8011 MediumLockList::Base::iterator lockListBegin =
8012 task.mpMediumLockList->GetBegin();
8013 MediumLockList::Base::iterator lockListEnd =
8014 task.mpMediumLockList->GetEnd();
8015 for (MediumLockList::Base::iterator it = lockListBegin;
8016 it != lockListEnd;
8017 )
8018 {
8019 MediumLock &mediumLock = *it;
8020 /* Create a real copy of the medium pointer, as the medium
8021 * lock deletion below would invalidate the referenced object. */
8022 const ComObjPtr<Medium> pMedium = mediumLock.GetMedium();
8023
8024 /* The target and all media not merged (readonly) are skipped */
8025 if ( pMedium == pTarget
8026 || pMedium->m->state == MediumState_LockedRead)
8027 {
8028 ++it;
8029 continue;
8030 }
8031
8032 rc2 = pMedium->m->pVirtualBox->i_unregisterMedium(pMedium);
8033 AssertComRC(rc2);
8034
8035 /* now, uninitialize the deleted medium (note that
8036 * due to the Deleting state, uninit() will not touch
8037 * the parent-child relationship so we need to
8038 * uninitialize each disk individually) */
8039
8040 /* note that the operation initiator medium (which is
8041 * normally also the source medium) is a special case
8042 * -- there is one more caller added by Task to it which
8043 * we must release. Also, if we are in sync mode, the
8044 * caller may still hold an AutoCaller instance for it
8045 * and therefore we cannot uninit() it (it's therefore
8046 * the caller's responsibility) */
8047 if (pMedium == this)
8048 {
8049 Assert(i_getChildren().size() == 0);
8050 Assert(m->backRefs.size() == 0);
8051 task.mMediumCaller.release();
8052 }
8053
8054 /* Delete the medium lock list entry, which also releases the
8055 * caller added by MergeChain before uninit() and updates the
8056 * iterator to point to the right place. */
8057 rc2 = task.mpMediumLockList->RemoveByIterator(it);
8058 AssertComRC(rc2);
8059
8060 if (task.isAsync() || pMedium != this)
8061 {
8062 treeLock.release();
8063 pMedium->uninit();
8064 treeLock.acquire();
8065 }
8066 }
8067 }
8068
8069 i_markRegistriesModified();
8070 if (task.isAsync())
8071 {
8072 // in asynchronous mode, save settings now
8073 eik.restore();
8074 m->pVirtualBox->i_saveModifiedRegistries();
8075 eik.fetch();
8076 }
8077
8078 if (FAILED(mrc))
8079 {
8080 /* Here we come if either VDMerge() failed (in which case we
8081 * assume that it tried to do everything to make a further
8082 * retry possible -- e.g. not deleted intermediate media
8083 * and so on) or VirtualBox::saveRegistries() failed (where we
8084 * should have the original tree but with intermediate storage
8085 * units deleted by VDMerge()). We have to only restore states
8086 * (through the MergeChain dtor) unless we are run synchronously
8087 * in which case it's the responsibility of the caller as stated
8088 * in the mergeTo() docs. The latter also implies that we
8089 * don't own the merge chain, so release it in this case. */
8090 if (task.isAsync())
8091 i_cancelMergeTo(task.mpChildrenToReparent, task.mpMediumLockList);
8092 }
8093
8094 return mrc;
8095}
8096
8097/**
8098 * Implementation code for the "clone" task.
8099 *
8100 * This only gets started from Medium::CloneTo() and always runs asynchronously.
8101 * As a result, we always save the VirtualBox.xml file when we're done here.
8102 *
8103 * @param task
8104 * @return
8105 */
8106HRESULT Medium::i_taskCloneHandler(Medium::CloneTask &task)
8107{
8108 HRESULT rcTmp = S_OK;
8109
8110 const ComObjPtr<Medium> &pTarget = task.mTarget;
8111 const ComObjPtr<Medium> &pParent = task.mParent;
8112
8113 bool fCreatingTarget = false;
8114
8115 uint64_t size = 0, logicalSize = 0;
8116 MediumVariant_T variant = MediumVariant_Standard;
8117 bool fGenerateUuid = false;
8118
8119 try
8120 {
8121 if (!pParent.isNull())
8122 if (pParent->i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX)
8123 {
8124 AutoReadLock plock(pParent COMMA_LOCKVAL_SRC_POS);
8125 throw setError(VBOX_E_INVALID_OBJECT_STATE,
8126 tr("Cannot clone image for medium '%s', because it exceeds the medium tree depth limit. Please merge some images which you no longer need"),
8127 pParent->m->strLocationFull.c_str());
8128 }
8129
8130 /* Lock all in {parent,child} order. The lock is also used as a
8131 * signal from the task initiator (which releases it only after
8132 * RTThreadCreate()) that we can start the job. */
8133 AutoMultiWriteLock3 thisLock(this, pTarget, pParent COMMA_LOCKVAL_SRC_POS);
8134
8135 fCreatingTarget = pTarget->m->state == MediumState_Creating;
8136
8137 /* The object may request a specific UUID (through a special form of
8138 * the setLocation() argument). Otherwise we have to generate it */
8139 Guid targetId = pTarget->m->id;
8140
8141 fGenerateUuid = targetId.isZero();
8142 if (fGenerateUuid)
8143 {
8144 targetId.create();
8145 /* VirtualBox::registerMedium() will need UUID */
8146 unconst(pTarget->m->id) = targetId;
8147 }
8148
8149 PVBOXHDD hdd;
8150 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
8151 ComAssertRCThrow(vrc, E_FAIL);
8152
8153 try
8154 {
8155 /* Open all media in the source chain. */
8156 MediumLockList::Base::const_iterator sourceListBegin =
8157 task.mpSourceMediumLockList->GetBegin();
8158 MediumLockList::Base::const_iterator sourceListEnd =
8159 task.mpSourceMediumLockList->GetEnd();
8160 for (MediumLockList::Base::const_iterator it = sourceListBegin;
8161 it != sourceListEnd;
8162 ++it)
8163 {
8164 const MediumLock &mediumLock = *it;
8165 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
8166 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
8167
8168 /* sanity check */
8169 Assert(pMedium->m->state == MediumState_LockedRead);
8170
8171 /** Open all media in read-only mode. */
8172 vrc = VDOpen(hdd,
8173 pMedium->m->strFormat.c_str(),
8174 pMedium->m->strLocationFull.c_str(),
8175 VD_OPEN_FLAGS_READONLY | m->uOpenFlagsDef,
8176 pMedium->m->vdImageIfaces);
8177 if (RT_FAILURE(vrc))
8178 throw setError(VBOX_E_FILE_ERROR,
8179 tr("Could not open the medium storage unit '%s'%s"),
8180 pMedium->m->strLocationFull.c_str(),
8181 i_vdError(vrc).c_str());
8182 }
8183
8184 Utf8Str targetFormat(pTarget->m->strFormat);
8185 Utf8Str targetLocation(pTarget->m->strLocationFull);
8186 uint64_t capabilities = pTarget->m->formatObj->i_getCapabilities();
8187
8188 Assert( pTarget->m->state == MediumState_Creating
8189 || pTarget->m->state == MediumState_LockedWrite);
8190 Assert(m->state == MediumState_LockedRead);
8191 Assert( pParent.isNull()
8192 || pParent->m->state == MediumState_LockedRead);
8193
8194 /* unlock before the potentially lengthy operation */
8195 thisLock.release();
8196
8197 /* ensure the target directory exists */
8198 if (capabilities & MediumFormatCapabilities_File)
8199 {
8200 HRESULT rc = VirtualBox::i_ensureFilePathExists(targetLocation,
8201 !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
8202 if (FAILED(rc))
8203 throw rc;
8204 }
8205
8206 PVBOXHDD targetHdd;
8207 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &targetHdd);
8208 ComAssertRCThrow(vrc, E_FAIL);
8209
8210 try
8211 {
8212 /* Open all media in the target chain. */
8213 MediumLockList::Base::const_iterator targetListBegin =
8214 task.mpTargetMediumLockList->GetBegin();
8215 MediumLockList::Base::const_iterator targetListEnd =
8216 task.mpTargetMediumLockList->GetEnd();
8217 for (MediumLockList::Base::const_iterator it = targetListBegin;
8218 it != targetListEnd;
8219 ++it)
8220 {
8221 const MediumLock &mediumLock = *it;
8222 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
8223
8224 /* If the target medium is not created yet there's no
8225 * reason to open it. */
8226 if (pMedium == pTarget && fCreatingTarget)
8227 continue;
8228
8229 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
8230
8231 /* sanity check */
8232 Assert( pMedium->m->state == MediumState_LockedRead
8233 || pMedium->m->state == MediumState_LockedWrite);
8234
8235 unsigned uOpenFlags = VD_OPEN_FLAGS_NORMAL;
8236 if (pMedium->m->state != MediumState_LockedWrite)
8237 uOpenFlags = VD_OPEN_FLAGS_READONLY;
8238 if (pMedium->m->type == MediumType_Shareable)
8239 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
8240
8241 /* Open all media in appropriate mode. */
8242 vrc = VDOpen(targetHdd,
8243 pMedium->m->strFormat.c_str(),
8244 pMedium->m->strLocationFull.c_str(),
8245 uOpenFlags | m->uOpenFlagsDef,
8246 pMedium->m->vdImageIfaces);
8247 if (RT_FAILURE(vrc))
8248 throw setError(VBOX_E_FILE_ERROR,
8249 tr("Could not open the medium storage unit '%s'%s"),
8250 pMedium->m->strLocationFull.c_str(),
8251 i_vdError(vrc).c_str());
8252 }
8253
8254 /* target isn't locked, but no changing data is accessed */
8255 if (task.midxSrcImageSame == UINT32_MAX)
8256 {
8257 vrc = VDCopy(hdd,
8258 VD_LAST_IMAGE,
8259 targetHdd,
8260 targetFormat.c_str(),
8261 (fCreatingTarget) ? targetLocation.c_str() : (char *)NULL,
8262 false /* fMoveByRename */,
8263 0 /* cbSize */,
8264 task.mVariant & ~MediumVariant_NoCreateDir,
8265 targetId.raw(),
8266 VD_OPEN_FLAGS_NORMAL | m->uOpenFlagsDef,
8267 NULL /* pVDIfsOperation */,
8268 pTarget->m->vdImageIfaces,
8269 task.mVDOperationIfaces);
8270 }
8271 else
8272 {
8273 vrc = VDCopyEx(hdd,
8274 VD_LAST_IMAGE,
8275 targetHdd,
8276 targetFormat.c_str(),
8277 (fCreatingTarget) ? targetLocation.c_str() : (char *)NULL,
8278 false /* fMoveByRename */,
8279 0 /* cbSize */,
8280 task.midxSrcImageSame,
8281 task.midxDstImageSame,
8282 task.mVariant & ~MediumVariant_NoCreateDir,
8283 targetId.raw(),
8284 VD_OPEN_FLAGS_NORMAL | m->uOpenFlagsDef,
8285 NULL /* pVDIfsOperation */,
8286 pTarget->m->vdImageIfaces,
8287 task.mVDOperationIfaces);
8288 }
8289 if (RT_FAILURE(vrc))
8290 throw setError(VBOX_E_FILE_ERROR,
8291 tr("Could not create the clone medium '%s'%s"),
8292 targetLocation.c_str(), i_vdError(vrc).c_str());
8293
8294 size = VDGetFileSize(targetHdd, VD_LAST_IMAGE);
8295 logicalSize = VDGetSize(targetHdd, VD_LAST_IMAGE);
8296 unsigned uImageFlags;
8297 vrc = VDGetImageFlags(targetHdd, 0, &uImageFlags);
8298 if (RT_SUCCESS(vrc))
8299 variant = (MediumVariant_T)uImageFlags;
8300 }
8301 catch (HRESULT aRC) { rcTmp = aRC; }
8302
8303 VDDestroy(targetHdd);
8304 }
8305 catch (HRESULT aRC) { rcTmp = aRC; }
8306
8307 VDDestroy(hdd);
8308 }
8309 catch (HRESULT aRC) { rcTmp = aRC; }
8310
8311 ErrorInfoKeeper eik;
8312 MultiResult mrc(rcTmp);
8313
8314 /* Only do the parent changes for newly created media. */
8315 if (SUCCEEDED(mrc) && fCreatingTarget)
8316 {
8317 /* we set mParent & children() */
8318 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
8319
8320 Assert(pTarget->m->pParent.isNull());
8321
8322 if (pParent)
8323 {
8324 /* Associate the clone with the parent and deassociate
8325 * from VirtualBox. Depth check above. */
8326 pTarget->i_setParent(pParent);
8327
8328 /* register with mVirtualBox as the last step and move to
8329 * Created state only on success (leaving an orphan file is
8330 * better than breaking media registry consistency) */
8331 eik.restore();
8332 ComObjPtr<Medium> pMedium;
8333 mrc = pParent->m->pVirtualBox->i_registerMedium(pTarget, &pMedium,
8334 treeLock);
8335 Assert( FAILED(mrc)
8336 || pTarget == pMedium);
8337 eik.fetch();
8338
8339 if (FAILED(mrc))
8340 /* break parent association on failure to register */
8341 pTarget->i_deparent(); // removes target from parent
8342 }
8343 else
8344 {
8345 /* just register */
8346 eik.restore();
8347 ComObjPtr<Medium> pMedium;
8348 mrc = m->pVirtualBox->i_registerMedium(pTarget, &pMedium,
8349 treeLock);
8350 Assert( FAILED(mrc)
8351 || pTarget == pMedium);
8352 eik.fetch();
8353 }
8354 }
8355
8356 if (fCreatingTarget)
8357 {
8358 AutoWriteLock mLock(pTarget COMMA_LOCKVAL_SRC_POS);
8359
8360 if (SUCCEEDED(mrc))
8361 {
8362 pTarget->m->state = MediumState_Created;
8363
8364 pTarget->m->size = size;
8365 pTarget->m->logicalSize = logicalSize;
8366 pTarget->m->variant = variant;
8367 }
8368 else
8369 {
8370 /* back to NotCreated on failure */
8371 pTarget->m->state = MediumState_NotCreated;
8372
8373 /* reset UUID to prevent it from being reused next time */
8374 if (fGenerateUuid)
8375 unconst(pTarget->m->id).clear();
8376 }
8377 }
8378
8379 /* Copy any filter related settings over to the target. */
8380 if (SUCCEEDED(mrc))
8381 {
8382 /* Copy any filter related settings over. */
8383 ComObjPtr<Medium> pBase = i_getBase();
8384 ComObjPtr<Medium> pTargetBase = pTarget->i_getBase();
8385 std::vector<com::Utf8Str> aFilterPropNames;
8386 std::vector<com::Utf8Str> aFilterPropValues;
8387 mrc = pBase->i_getFilterProperties(aFilterPropNames, aFilterPropValues);
8388 if (SUCCEEDED(mrc))
8389 {
8390 /* Go through the properties and add them to the target medium. */
8391 for (unsigned idx = 0; idx < aFilterPropNames.size(); idx++)
8392 {
8393 mrc = pTargetBase->i_setPropertyDirect(aFilterPropNames[idx], aFilterPropValues[idx]);
8394 if (FAILED(mrc)) break;
8395 }
8396
8397 // now, at the end of this task (always asynchronous), save the settings
8398 if (SUCCEEDED(mrc))
8399 {
8400 // save the settings
8401 i_markRegistriesModified();
8402 /* collect multiple errors */
8403 eik.restore();
8404 m->pVirtualBox->i_saveModifiedRegistries();
8405 eik.fetch();
8406 }
8407 }
8408 }
8409
8410 /* Everything is explicitly unlocked when the task exits,
8411 * as the task destruction also destroys the source chain. */
8412
8413 /* Make sure the source chain is released early. It could happen
8414 * that we get a deadlock in Appliance::Import when Medium::Close
8415 * is called & the source chain is released at the same time. */
8416 task.mpSourceMediumLockList->Clear();
8417
8418 return mrc;
8419}
8420
8421/**
8422 * Implementation code for the "delete" task.
8423 *
8424 * This task always gets started from Medium::deleteStorage() and can run
8425 * synchronously or asynchronously depending on the "wait" parameter passed to
8426 * that function.
8427 *
8428 * @param task
8429 * @return
8430 */
8431HRESULT Medium::i_taskDeleteHandler(Medium::DeleteTask &task)
8432{
8433 NOREF(task);
8434 HRESULT rc = S_OK;
8435
8436 try
8437 {
8438 /* The lock is also used as a signal from the task initiator (which
8439 * releases it only after RTThreadCreate()) that we can start the job */
8440 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
8441
8442 PVBOXHDD hdd;
8443 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
8444 ComAssertRCThrow(vrc, E_FAIL);
8445
8446 Utf8Str format(m->strFormat);
8447 Utf8Str location(m->strLocationFull);
8448
8449 /* unlock before the potentially lengthy operation */
8450 Assert(m->state == MediumState_Deleting);
8451 thisLock.release();
8452
8453 try
8454 {
8455 vrc = VDOpen(hdd,
8456 format.c_str(),
8457 location.c_str(),
8458 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
8459 m->vdImageIfaces);
8460 if (RT_SUCCESS(vrc))
8461 vrc = VDClose(hdd, true /* fDelete */);
8462
8463 if (RT_FAILURE(vrc))
8464 throw setError(VBOX_E_FILE_ERROR,
8465 tr("Could not delete the medium storage unit '%s'%s"),
8466 location.c_str(), i_vdError(vrc).c_str());
8467
8468 }
8469 catch (HRESULT aRC) { rc = aRC; }
8470
8471 VDDestroy(hdd);
8472 }
8473 catch (HRESULT aRC) { rc = aRC; }
8474
8475 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
8476
8477 /* go to the NotCreated state even on failure since the storage
8478 * may have been already partially deleted and cannot be used any
8479 * more. One will be able to manually re-open the storage if really
8480 * needed to re-register it. */
8481 m->state = MediumState_NotCreated;
8482
8483 /* Reset UUID to prevent Create* from reusing it again */
8484 unconst(m->id).clear();
8485
8486 return rc;
8487}
8488
8489/**
8490 * Implementation code for the "reset" task.
8491 *
8492 * This always gets started asynchronously from Medium::Reset().
8493 *
8494 * @param task
8495 * @return
8496 */
8497HRESULT Medium::i_taskResetHandler(Medium::ResetTask &task)
8498{
8499 HRESULT rc = S_OK;
8500
8501 uint64_t size = 0, logicalSize = 0;
8502 MediumVariant_T variant = MediumVariant_Standard;
8503
8504 try
8505 {
8506 /* The lock is also used as a signal from the task initiator (which
8507 * releases it only after RTThreadCreate()) that we can start the job */
8508 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
8509
8510 /// @todo Below we use a pair of delete/create operations to reset
8511 /// the diff contents but the most efficient way will of course be
8512 /// to add a VDResetDiff() API call
8513
8514 PVBOXHDD hdd;
8515 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
8516 ComAssertRCThrow(vrc, E_FAIL);
8517
8518 Guid id = m->id;
8519 Utf8Str format(m->strFormat);
8520 Utf8Str location(m->strLocationFull);
8521
8522 Medium *pParent = m->pParent;
8523 Guid parentId = pParent->m->id;
8524 Utf8Str parentFormat(pParent->m->strFormat);
8525 Utf8Str parentLocation(pParent->m->strLocationFull);
8526
8527 Assert(m->state == MediumState_LockedWrite);
8528
8529 /* unlock before the potentially lengthy operation */
8530 thisLock.release();
8531
8532 try
8533 {
8534 /* Open all media in the target chain but the last. */
8535 MediumLockList::Base::const_iterator targetListBegin =
8536 task.mpMediumLockList->GetBegin();
8537 MediumLockList::Base::const_iterator targetListEnd =
8538 task.mpMediumLockList->GetEnd();
8539 for (MediumLockList::Base::const_iterator it = targetListBegin;
8540 it != targetListEnd;
8541 ++it)
8542 {
8543 const MediumLock &mediumLock = *it;
8544 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
8545
8546 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
8547
8548 /* sanity check, "this" is checked above */
8549 Assert( pMedium == this
8550 || pMedium->m->state == MediumState_LockedRead);
8551
8552 /* Open all media in appropriate mode. */
8553 vrc = VDOpen(hdd,
8554 pMedium->m->strFormat.c_str(),
8555 pMedium->m->strLocationFull.c_str(),
8556 VD_OPEN_FLAGS_READONLY | m->uOpenFlagsDef,
8557 pMedium->m->vdImageIfaces);
8558 if (RT_FAILURE(vrc))
8559 throw setError(VBOX_E_FILE_ERROR,
8560 tr("Could not open the medium storage unit '%s'%s"),
8561 pMedium->m->strLocationFull.c_str(),
8562 i_vdError(vrc).c_str());
8563
8564 /* Done when we hit the media which should be reset */
8565 if (pMedium == this)
8566 break;
8567 }
8568
8569 /* first, delete the storage unit */
8570 vrc = VDClose(hdd, true /* fDelete */);
8571 if (RT_FAILURE(vrc))
8572 throw setError(VBOX_E_FILE_ERROR,
8573 tr("Could not delete the medium storage unit '%s'%s"),
8574 location.c_str(), i_vdError(vrc).c_str());
8575
8576 /* next, create it again */
8577 vrc = VDOpen(hdd,
8578 parentFormat.c_str(),
8579 parentLocation.c_str(),
8580 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
8581 m->vdImageIfaces);
8582 if (RT_FAILURE(vrc))
8583 throw setError(VBOX_E_FILE_ERROR,
8584 tr("Could not open the medium storage unit '%s'%s"),
8585 parentLocation.c_str(), i_vdError(vrc).c_str());
8586
8587 vrc = VDCreateDiff(hdd,
8588 format.c_str(),
8589 location.c_str(),
8590 /// @todo use the same medium variant as before
8591 VD_IMAGE_FLAGS_NONE,
8592 NULL,
8593 id.raw(),
8594 parentId.raw(),
8595 VD_OPEN_FLAGS_NORMAL,
8596 m->vdImageIfaces,
8597 task.mVDOperationIfaces);
8598 if (RT_FAILURE(vrc))
8599 {
8600 if (vrc == VERR_VD_INVALID_TYPE)
8601 throw setError(VBOX_E_FILE_ERROR,
8602 tr("Parameters for creating the differencing medium storage unit '%s' are invalid%s"),
8603 location.c_str(), i_vdError(vrc).c_str());
8604 else
8605 throw setError(VBOX_E_FILE_ERROR,
8606 tr("Could not create the differencing medium storage unit '%s'%s"),
8607 location.c_str(), i_vdError(vrc).c_str());
8608 }
8609
8610 size = VDGetFileSize(hdd, VD_LAST_IMAGE);
8611 logicalSize = VDGetSize(hdd, VD_LAST_IMAGE);
8612 unsigned uImageFlags;
8613 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
8614 if (RT_SUCCESS(vrc))
8615 variant = (MediumVariant_T)uImageFlags;
8616 }
8617 catch (HRESULT aRC) { rc = aRC; }
8618
8619 VDDestroy(hdd);
8620 }
8621 catch (HRESULT aRC) { rc = aRC; }
8622
8623 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
8624
8625 m->size = size;
8626 m->logicalSize = logicalSize;
8627 m->variant = variant;
8628
8629 /* Everything is explicitly unlocked when the task exits,
8630 * as the task destruction also destroys the media chain. */
8631
8632 return rc;
8633}
8634
8635/**
8636 * Implementation code for the "compact" task.
8637 *
8638 * @param task
8639 * @return
8640 */
8641HRESULT Medium::i_taskCompactHandler(Medium::CompactTask &task)
8642{
8643 HRESULT rc = S_OK;
8644
8645 /* Lock all in {parent,child} order. The lock is also used as a
8646 * signal from the task initiator (which releases it only after
8647 * RTThreadCreate()) that we can start the job. */
8648 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
8649
8650 try
8651 {
8652 PVBOXHDD hdd;
8653 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
8654 ComAssertRCThrow(vrc, E_FAIL);
8655
8656 try
8657 {
8658 /* Open all media in the chain. */
8659 MediumLockList::Base::const_iterator mediumListBegin =
8660 task.mpMediumLockList->GetBegin();
8661 MediumLockList::Base::const_iterator mediumListEnd =
8662 task.mpMediumLockList->GetEnd();
8663 MediumLockList::Base::const_iterator mediumListLast =
8664 mediumListEnd;
8665 --mediumListLast;
8666 for (MediumLockList::Base::const_iterator it = mediumListBegin;
8667 it != mediumListEnd;
8668 ++it)
8669 {
8670 const MediumLock &mediumLock = *it;
8671 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
8672 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
8673
8674 /* sanity check */
8675 if (it == mediumListLast)
8676 Assert(pMedium->m->state == MediumState_LockedWrite);
8677 else
8678 Assert(pMedium->m->state == MediumState_LockedRead);
8679
8680 /* Open all media but last in read-only mode. Do not handle
8681 * shareable media, as compaction and sharing are mutually
8682 * exclusive. */
8683 vrc = VDOpen(hdd,
8684 pMedium->m->strFormat.c_str(),
8685 pMedium->m->strLocationFull.c_str(),
8686 m->uOpenFlagsDef | (it == mediumListLast ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY),
8687 pMedium->m->vdImageIfaces);
8688 if (RT_FAILURE(vrc))
8689 throw setError(VBOX_E_FILE_ERROR,
8690 tr("Could not open the medium storage unit '%s'%s"),
8691 pMedium->m->strLocationFull.c_str(),
8692 i_vdError(vrc).c_str());
8693 }
8694
8695 Assert(m->state == MediumState_LockedWrite);
8696
8697 Utf8Str location(m->strLocationFull);
8698
8699 /* unlock before the potentially lengthy operation */
8700 thisLock.release();
8701
8702 vrc = VDCompact(hdd, VD_LAST_IMAGE, task.mVDOperationIfaces);
8703 if (RT_FAILURE(vrc))
8704 {
8705 if (vrc == VERR_NOT_SUPPORTED)
8706 throw setError(VBOX_E_NOT_SUPPORTED,
8707 tr("Compacting is not yet supported for medium '%s'"),
8708 location.c_str());
8709 else if (vrc == VERR_NOT_IMPLEMENTED)
8710 throw setError(E_NOTIMPL,
8711 tr("Compacting is not implemented, medium '%s'"),
8712 location.c_str());
8713 else
8714 throw setError(VBOX_E_FILE_ERROR,
8715 tr("Could not compact medium '%s'%s"),
8716 location.c_str(),
8717 i_vdError(vrc).c_str());
8718 }
8719 }
8720 catch (HRESULT aRC) { rc = aRC; }
8721
8722 VDDestroy(hdd);
8723 }
8724 catch (HRESULT aRC) { rc = aRC; }
8725
8726 /* Everything is explicitly unlocked when the task exits,
8727 * as the task destruction also destroys the media chain. */
8728
8729 return rc;
8730}
8731
8732/**
8733 * Implementation code for the "resize" task.
8734 *
8735 * @param task
8736 * @return
8737 */
8738HRESULT Medium::i_taskResizeHandler(Medium::ResizeTask &task)
8739{
8740 HRESULT rc = S_OK;
8741
8742 uint64_t size = 0, logicalSize = 0;
8743
8744 try
8745 {
8746 /* The lock is also used as a signal from the task initiator (which
8747 * releases it only after RTThreadCreate()) that we can start the job */
8748 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
8749
8750 PVBOXHDD hdd;
8751 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
8752 ComAssertRCThrow(vrc, E_FAIL);
8753
8754 try
8755 {
8756 /* Open all media in the chain. */
8757 MediumLockList::Base::const_iterator mediumListBegin =
8758 task.mpMediumLockList->GetBegin();
8759 MediumLockList::Base::const_iterator mediumListEnd =
8760 task.mpMediumLockList->GetEnd();
8761 MediumLockList::Base::const_iterator mediumListLast =
8762 mediumListEnd;
8763 --mediumListLast;
8764 for (MediumLockList::Base::const_iterator it = mediumListBegin;
8765 it != mediumListEnd;
8766 ++it)
8767 {
8768 const MediumLock &mediumLock = *it;
8769 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
8770 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
8771
8772 /* sanity check */
8773 if (it == mediumListLast)
8774 Assert(pMedium->m->state == MediumState_LockedWrite);
8775 else
8776 Assert(pMedium->m->state == MediumState_LockedRead);
8777
8778 /* Open all media but last in read-only mode. Do not handle
8779 * shareable media, as compaction and sharing are mutually
8780 * exclusive. */
8781 vrc = VDOpen(hdd,
8782 pMedium->m->strFormat.c_str(),
8783 pMedium->m->strLocationFull.c_str(),
8784 m->uOpenFlagsDef | (it == mediumListLast ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY),
8785 pMedium->m->vdImageIfaces);
8786 if (RT_FAILURE(vrc))
8787 throw setError(VBOX_E_FILE_ERROR,
8788 tr("Could not open the medium storage unit '%s'%s"),
8789 pMedium->m->strLocationFull.c_str(),
8790 i_vdError(vrc).c_str());
8791 }
8792
8793 Assert(m->state == MediumState_LockedWrite);
8794
8795 Utf8Str location(m->strLocationFull);
8796
8797 /* unlock before the potentially lengthy operation */
8798 thisLock.release();
8799
8800 VDGEOMETRY geo = {0, 0, 0}; /* auto */
8801 vrc = VDResize(hdd, task.mSize, &geo, &geo, task.mVDOperationIfaces);
8802 if (RT_FAILURE(vrc))
8803 {
8804 if (vrc == VERR_NOT_SUPPORTED)
8805 throw setError(VBOX_E_NOT_SUPPORTED,
8806 tr("Resizing to new size %llu is not yet supported for medium '%s'"),
8807 task.mSize, location.c_str());
8808 else if (vrc == VERR_NOT_IMPLEMENTED)
8809 throw setError(E_NOTIMPL,
8810 tr("Resiting is not implemented, medium '%s'"),
8811 location.c_str());
8812 else
8813 throw setError(VBOX_E_FILE_ERROR,
8814 tr("Could not resize medium '%s'%s"),
8815 location.c_str(),
8816 i_vdError(vrc).c_str());
8817 }
8818 size = VDGetFileSize(hdd, VD_LAST_IMAGE);
8819 logicalSize = VDGetSize(hdd, VD_LAST_IMAGE);
8820 }
8821 catch (HRESULT aRC) { rc = aRC; }
8822
8823 VDDestroy(hdd);
8824 }
8825 catch (HRESULT aRC) { rc = aRC; }
8826
8827 if (SUCCEEDED(rc))
8828 {
8829 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
8830 m->size = size;
8831 m->logicalSize = logicalSize;
8832 }
8833
8834 /* Everything is explicitly unlocked when the task exits,
8835 * as the task destruction also destroys the media chain. */
8836
8837 return rc;
8838}
8839
8840/**
8841 * Implementation code for the "export" task.
8842 *
8843 * This only gets started from Medium::exportFile() and always runs
8844 * asynchronously. It doesn't touch anything configuration related, so
8845 * we never save the VirtualBox.xml file here.
8846 *
8847 * @param task
8848 * @return
8849 */
8850HRESULT Medium::i_taskExportHandler(Medium::ExportTask &task)
8851{
8852 HRESULT rc = S_OK;
8853
8854 try
8855 {
8856 /* Lock all in {parent,child} order. The lock is also used as a
8857 * signal from the task initiator (which releases it only after
8858 * RTThreadCreate()) that we can start the job. */
8859 ComObjPtr<Medium> pBase = i_getBase();
8860 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
8861
8862 PVBOXHDD hdd;
8863 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
8864 ComAssertRCThrow(vrc, E_FAIL);
8865
8866 try
8867 {
8868 settings::StringsMap::iterator itKeyStore = pBase->m->mapProperties.find("CRYPT/KeyStore");
8869 if (itKeyStore != pBase->m->mapProperties.end())
8870 {
8871 settings::StringsMap::iterator itKeyId = pBase->m->mapProperties.find("CRYPT/KeyId");
8872
8873#ifdef VBOX_WITH_EXTPACK
8874 static const Utf8Str strExtPackPuel("Oracle VM VirtualBox Extension Pack");
8875 static const char *s_pszVDPlugin = "VDPluginCrypt";
8876 ExtPackManager *pExtPackManager = m->pVirtualBox->i_getExtPackManager();
8877 if (pExtPackManager->i_isExtPackUsable(strExtPackPuel.c_str()))
8878 {
8879 /* Load the plugin */
8880 Utf8Str strPlugin;
8881 rc = pExtPackManager->i_getLibraryPathForExtPack(s_pszVDPlugin, &strExtPackPuel, &strPlugin);
8882 if (SUCCEEDED(rc))
8883 {
8884 vrc = VDPluginLoadFromFilename(strPlugin.c_str());
8885 if (RT_FAILURE(vrc))
8886 throw setError(VBOX_E_NOT_SUPPORTED,
8887 tr("Retrieving encryption settings of the image failed because the encryption plugin could not be loaded (%s)"),
8888 i_vdError(vrc).c_str());
8889 }
8890 else
8891 throw setError(VBOX_E_NOT_SUPPORTED,
8892 tr("Encryption is not supported because the extension pack '%s' is missing the encryption plugin (old extension pack installed?)"),
8893 strExtPackPuel.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"),
8898 strExtPackPuel.c_str());
8899#else
8900 throw setError(VBOX_E_NOT_SUPPORTED,
8901 tr("Encryption is not supported because extension pack support is not built in"));
8902#endif
8903
8904 if (itKeyId == pBase->m->mapProperties.end())
8905 throw setError(VBOX_E_INVALID_OBJECT_STATE,
8906 tr("Image '%s' is configured for encryption but doesn't has a key identifier set"),
8907 pBase->m->strLocationFull.c_str());
8908
8909 /* Find the proper secret key in the key store. */
8910 if (!task.m_pSecretKeyStore)
8911 throw setError(VBOX_E_INVALID_OBJECT_STATE,
8912 tr("Image '%s' is configured for encryption but there is no key store to retrieve the password from"),
8913 pBase->m->strLocationFull.c_str());
8914
8915 SecretKey *pKey = NULL;
8916 vrc = task.m_pSecretKeyStore->retainSecretKey(itKeyId->second, &pKey);
8917 if (RT_FAILURE(vrc))
8918 throw setError(VBOX_E_INVALID_OBJECT_STATE,
8919 tr("Failed to retrieve the secret key with ID \"%s\" from the store (%Rrc)"),
8920 itKeyId->second.c_str(), vrc);
8921
8922 Medium::CryptoFilterSettings CryptoSettingsRead;
8923 i_taskEncryptSettingsSetup(&CryptoSettingsRead, NULL, itKeyStore->second.c_str(), (const char *)pKey->getKeyBuffer(),
8924 false /* fCreateKeyStore */);
8925 vrc = VDFilterAdd(hdd, "CRYPT", VD_FILTER_FLAGS_READ, CryptoSettingsRead.vdFilterIfaces);
8926 if (vrc == VERR_VD_PASSWORD_INCORRECT)
8927 {
8928 task.m_pSecretKeyStore->releaseSecretKey(itKeyId->second);
8929 throw setError(VBOX_E_PASSWORD_INCORRECT,
8930 tr("The password to decrypt the image is incorrect"));
8931 }
8932 else if (RT_FAILURE(vrc))
8933 {
8934 task.m_pSecretKeyStore->releaseSecretKey(itKeyId->second);
8935 throw setError(VBOX_E_INVALID_OBJECT_STATE,
8936 tr("Failed to load the decryption filter: %s"),
8937 i_vdError(vrc).c_str());
8938 }
8939
8940 task.m_pSecretKeyStore->releaseSecretKey(itKeyId->second);
8941 }
8942
8943 /* Open all media in the source chain. */
8944 MediumLockList::Base::const_iterator sourceListBegin =
8945 task.mpSourceMediumLockList->GetBegin();
8946 MediumLockList::Base::const_iterator sourceListEnd =
8947 task.mpSourceMediumLockList->GetEnd();
8948 for (MediumLockList::Base::const_iterator it = sourceListBegin;
8949 it != sourceListEnd;
8950 ++it)
8951 {
8952 const MediumLock &mediumLock = *it;
8953 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
8954 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
8955
8956 /* sanity check */
8957 Assert(pMedium->m->state == MediumState_LockedRead);
8958
8959 /* Open all media in read-only mode. */
8960 vrc = VDOpen(hdd,
8961 pMedium->m->strFormat.c_str(),
8962 pMedium->m->strLocationFull.c_str(),
8963 VD_OPEN_FLAGS_READONLY | m->uOpenFlagsDef,
8964 pMedium->m->vdImageIfaces);
8965 if (RT_FAILURE(vrc))
8966 throw setError(VBOX_E_FILE_ERROR,
8967 tr("Could not open the medium storage unit '%s'%s"),
8968 pMedium->m->strLocationFull.c_str(),
8969 i_vdError(vrc).c_str());
8970 }
8971
8972 Utf8Str targetFormat(task.mFormat->i_getId());
8973 Utf8Str targetLocation(task.mFilename);
8974 uint64_t capabilities = task.mFormat->i_getCapabilities();
8975
8976 Assert(m->state == MediumState_LockedRead);
8977
8978 /* unlock before the potentially lengthy operation */
8979 thisLock.release();
8980
8981 /* ensure the target directory exists */
8982 if (capabilities & MediumFormatCapabilities_File)
8983 {
8984 rc = VirtualBox::i_ensureFilePathExists(targetLocation,
8985 !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
8986 if (FAILED(rc))
8987 throw rc;
8988 }
8989
8990 PVBOXHDD targetHdd;
8991 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &targetHdd);
8992 ComAssertRCThrow(vrc, E_FAIL);
8993
8994 try
8995 {
8996 vrc = VDCopy(hdd,
8997 VD_LAST_IMAGE,
8998 targetHdd,
8999 targetFormat.c_str(),
9000 targetLocation.c_str(),
9001 false /* fMoveByRename */,
9002 0 /* cbSize */,
9003 task.mVariant & ~MediumVariant_NoCreateDir,
9004 NULL /* pDstUuid */,
9005 VD_OPEN_FLAGS_NORMAL | VD_OPEN_FLAGS_SEQUENTIAL,
9006 NULL /* pVDIfsOperation */,
9007 task.mVDImageIfaces,
9008 task.mVDOperationIfaces);
9009 if (RT_FAILURE(vrc))
9010 throw setError(VBOX_E_FILE_ERROR,
9011 tr("Could not create the exported medium '%s'%s"),
9012 targetLocation.c_str(), i_vdError(vrc).c_str());
9013 }
9014 catch (HRESULT aRC) { rc = aRC; }
9015
9016 VDDestroy(targetHdd);
9017 }
9018 catch (HRESULT aRC) { rc = aRC; }
9019
9020 VDDestroy(hdd);
9021 }
9022 catch (HRESULT aRC) { rc = aRC; }
9023
9024 /* Everything is explicitly unlocked when the task exits,
9025 * as the task destruction also destroys the source chain. */
9026
9027 /* Make sure the source chain is released early, otherwise it can
9028 * lead to deadlocks with concurrent IAppliance activities. */
9029 task.mpSourceMediumLockList->Clear();
9030
9031 return rc;
9032}
9033
9034/**
9035 * Implementation code for the "import" task.
9036 *
9037 * This only gets started from Medium::importFile() and always runs
9038 * asynchronously. It potentially touches the media registry, so we
9039 * always save the VirtualBox.xml file when we're done here.
9040 *
9041 * @param task
9042 * @return
9043 */
9044HRESULT Medium::i_taskImportHandler(Medium::ImportTask &task)
9045{
9046 HRESULT rcTmp = S_OK;
9047
9048 const ComObjPtr<Medium> &pParent = task.mParent;
9049
9050 bool fCreatingTarget = false;
9051
9052 uint64_t size = 0, logicalSize = 0;
9053 MediumVariant_T variant = MediumVariant_Standard;
9054 bool fGenerateUuid = false;
9055
9056 try
9057 {
9058 if (!pParent.isNull())
9059 if (pParent->i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX)
9060 {
9061 AutoReadLock plock(pParent COMMA_LOCKVAL_SRC_POS);
9062 throw setError(VBOX_E_INVALID_OBJECT_STATE,
9063 tr("Cannot import image for medium '%s', because it exceeds the medium tree depth limit. Please merge some images which you no longer need"),
9064 pParent->m->strLocationFull.c_str());
9065 }
9066
9067 /* Lock all in {parent,child} order. The lock is also used as a
9068 * signal from the task initiator (which releases it only after
9069 * RTThreadCreate()) that we can start the job. */
9070 AutoMultiWriteLock2 thisLock(this, pParent COMMA_LOCKVAL_SRC_POS);
9071
9072 fCreatingTarget = m->state == MediumState_Creating;
9073
9074 /* The object may request a specific UUID (through a special form of
9075 * the setLocation() argument). Otherwise we have to generate it */
9076 Guid targetId = m->id;
9077
9078 fGenerateUuid = targetId.isZero();
9079 if (fGenerateUuid)
9080 {
9081 targetId.create();
9082 /* VirtualBox::i_registerMedium() will need UUID */
9083 unconst(m->id) = targetId;
9084 }
9085
9086
9087 PVBOXHDD hdd;
9088 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
9089 ComAssertRCThrow(vrc, E_FAIL);
9090
9091 try
9092 {
9093 /* Open source medium. */
9094 vrc = VDOpen(hdd,
9095 task.mFormat->i_getId().c_str(),
9096 task.mFilename.c_str(),
9097 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SEQUENTIAL | m->uOpenFlagsDef,
9098 task.mVDImageIfaces);
9099 if (RT_FAILURE(vrc))
9100 throw setError(VBOX_E_FILE_ERROR,
9101 tr("Could not open the medium storage unit '%s'%s"),
9102 task.mFilename.c_str(),
9103 i_vdError(vrc).c_str());
9104
9105 Utf8Str targetFormat(m->strFormat);
9106 Utf8Str targetLocation(m->strLocationFull);
9107 uint64_t capabilities = task.mFormat->i_getCapabilities();
9108
9109 Assert( m->state == MediumState_Creating
9110 || m->state == MediumState_LockedWrite);
9111 Assert( pParent.isNull()
9112 || pParent->m->state == MediumState_LockedRead);
9113
9114 /* unlock before the potentially lengthy operation */
9115 thisLock.release();
9116
9117 /* ensure the target directory exists */
9118 if (capabilities & MediumFormatCapabilities_File)
9119 {
9120 HRESULT rc = VirtualBox::i_ensureFilePathExists(targetLocation,
9121 !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
9122 if (FAILED(rc))
9123 throw rc;
9124 }
9125
9126 PVBOXHDD targetHdd;
9127 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &targetHdd);
9128 ComAssertRCThrow(vrc, E_FAIL);
9129
9130 try
9131 {
9132 /* Open all media in the target chain. */
9133 MediumLockList::Base::const_iterator targetListBegin =
9134 task.mpTargetMediumLockList->GetBegin();
9135 MediumLockList::Base::const_iterator targetListEnd =
9136 task.mpTargetMediumLockList->GetEnd();
9137 for (MediumLockList::Base::const_iterator it = targetListBegin;
9138 it != targetListEnd;
9139 ++it)
9140 {
9141 const MediumLock &mediumLock = *it;
9142 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
9143
9144 /* If the target medium is not created yet there's no
9145 * reason to open it. */
9146 if (pMedium == this && fCreatingTarget)
9147 continue;
9148
9149 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
9150
9151 /* sanity check */
9152 Assert( pMedium->m->state == MediumState_LockedRead
9153 || pMedium->m->state == MediumState_LockedWrite);
9154
9155 unsigned uOpenFlags = VD_OPEN_FLAGS_NORMAL;
9156 if (pMedium->m->state != MediumState_LockedWrite)
9157 uOpenFlags = VD_OPEN_FLAGS_READONLY;
9158 if (pMedium->m->type == MediumType_Shareable)
9159 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
9160
9161 /* Open all media in appropriate mode. */
9162 vrc = VDOpen(targetHdd,
9163 pMedium->m->strFormat.c_str(),
9164 pMedium->m->strLocationFull.c_str(),
9165 uOpenFlags | m->uOpenFlagsDef,
9166 pMedium->m->vdImageIfaces);
9167 if (RT_FAILURE(vrc))
9168 throw setError(VBOX_E_FILE_ERROR,
9169 tr("Could not open the medium storage unit '%s'%s"),
9170 pMedium->m->strLocationFull.c_str(),
9171 i_vdError(vrc).c_str());
9172 }
9173
9174 vrc = VDCopy(hdd,
9175 VD_LAST_IMAGE,
9176 targetHdd,
9177 targetFormat.c_str(),
9178 (fCreatingTarget) ? targetLocation.c_str() : (char *)NULL,
9179 false /* fMoveByRename */,
9180 0 /* cbSize */,
9181 task.mVariant & ~MediumVariant_NoCreateDir,
9182 targetId.raw(),
9183 VD_OPEN_FLAGS_NORMAL,
9184 NULL /* pVDIfsOperation */,
9185 m->vdImageIfaces,
9186 task.mVDOperationIfaces);
9187 if (RT_FAILURE(vrc))
9188 throw setError(VBOX_E_FILE_ERROR,
9189 tr("Could not create the imported medium '%s'%s"),
9190 targetLocation.c_str(), i_vdError(vrc).c_str());
9191
9192 size = VDGetFileSize(targetHdd, VD_LAST_IMAGE);
9193 logicalSize = VDGetSize(targetHdd, VD_LAST_IMAGE);
9194 unsigned uImageFlags;
9195 vrc = VDGetImageFlags(targetHdd, 0, &uImageFlags);
9196 if (RT_SUCCESS(vrc))
9197 variant = (MediumVariant_T)uImageFlags;
9198 }
9199 catch (HRESULT aRC) { rcTmp = aRC; }
9200
9201 VDDestroy(targetHdd);
9202 }
9203 catch (HRESULT aRC) { rcTmp = aRC; }
9204
9205 VDDestroy(hdd);
9206 }
9207 catch (HRESULT aRC) { rcTmp = aRC; }
9208
9209 ErrorInfoKeeper eik;
9210 MultiResult mrc(rcTmp);
9211
9212 /* Only do the parent changes for newly created media. */
9213 if (SUCCEEDED(mrc) && fCreatingTarget)
9214 {
9215 /* we set mParent & children() */
9216 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
9217
9218 Assert(m->pParent.isNull());
9219
9220 if (pParent)
9221 {
9222 /* Associate the imported medium with the parent and deassociate
9223 * from VirtualBox. Depth check above. */
9224 i_setParent(pParent);
9225
9226 /* register with mVirtualBox as the last step and move to
9227 * Created state only on success (leaving an orphan file is
9228 * better than breaking media registry consistency) */
9229 eik.restore();
9230 ComObjPtr<Medium> pMedium;
9231 mrc = pParent->m->pVirtualBox->i_registerMedium(this, &pMedium,
9232 treeLock);
9233 Assert(this == pMedium);
9234 eik.fetch();
9235
9236 if (FAILED(mrc))
9237 /* break parent association on failure to register */
9238 this->i_deparent(); // removes target from parent
9239 }
9240 else
9241 {
9242 /* just register */
9243 eik.restore();
9244 ComObjPtr<Medium> pMedium;
9245 mrc = m->pVirtualBox->i_registerMedium(this, &pMedium, treeLock);
9246 Assert(this == pMedium);
9247 eik.fetch();
9248 }
9249 }
9250
9251 if (fCreatingTarget)
9252 {
9253 AutoWriteLock mLock(this COMMA_LOCKVAL_SRC_POS);
9254
9255 if (SUCCEEDED(mrc))
9256 {
9257 m->state = MediumState_Created;
9258
9259 m->size = size;
9260 m->logicalSize = logicalSize;
9261 m->variant = variant;
9262 }
9263 else
9264 {
9265 /* back to NotCreated on failure */
9266 m->state = MediumState_NotCreated;
9267
9268 /* reset UUID to prevent it from being reused next time */
9269 if (fGenerateUuid)
9270 unconst(m->id).clear();
9271 }
9272 }
9273
9274 // now, at the end of this task (always asynchronous), save the settings
9275 {
9276 // save the settings
9277 i_markRegistriesModified();
9278 /* collect multiple errors */
9279 eik.restore();
9280 m->pVirtualBox->i_saveModifiedRegistries();
9281 eik.fetch();
9282 }
9283
9284 /* Everything is explicitly unlocked when the task exits,
9285 * as the task destruction also destroys the target chain. */
9286
9287 /* Make sure the target chain is released early, otherwise it can
9288 * lead to deadlocks with concurrent IAppliance activities. */
9289 task.mpTargetMediumLockList->Clear();
9290
9291 return mrc;
9292}
9293
9294/**
9295 * Sets up the encryption settings for a filter.
9296 */
9297void Medium::i_taskEncryptSettingsSetup(CryptoFilterSettings *pSettings, const char *pszCipher,
9298 const char *pszKeyStore, const char *pszPassword,
9299 bool fCreateKeyStore)
9300{
9301 pSettings->pszCipher = pszCipher;
9302 pSettings->pszPassword = pszPassword;
9303 pSettings->pszKeyStoreLoad = pszKeyStore;
9304 pSettings->fCreateKeyStore = fCreateKeyStore;
9305 pSettings->pbDek = NULL;
9306 pSettings->cbDek = 0;
9307 pSettings->vdFilterIfaces = NULL;
9308
9309 pSettings->vdIfCfg.pfnAreKeysValid = i_vdCryptoConfigAreKeysValid;
9310 pSettings->vdIfCfg.pfnQuerySize = i_vdCryptoConfigQuerySize;
9311 pSettings->vdIfCfg.pfnQuery = i_vdCryptoConfigQuery;
9312 pSettings->vdIfCfg.pfnQueryBytes = NULL;
9313
9314 pSettings->vdIfCrypto.pfnKeyRetain = i_vdCryptoKeyRetain;
9315 pSettings->vdIfCrypto.pfnKeyRelease = i_vdCryptoKeyRelease;
9316 pSettings->vdIfCrypto.pfnKeyStorePasswordRetain = i_vdCryptoKeyStorePasswordRetain;
9317 pSettings->vdIfCrypto.pfnKeyStorePasswordRelease = i_vdCryptoKeyStorePasswordRelease;
9318 pSettings->vdIfCrypto.pfnKeyStoreSave = i_vdCryptoKeyStoreSave;
9319 pSettings->vdIfCrypto.pfnKeyStoreReturnParameters = i_vdCryptoKeyStoreReturnParameters;
9320
9321 int vrc = VDInterfaceAdd(&pSettings->vdIfCfg.Core,
9322 "Medium::vdInterfaceCfgCrypto",
9323 VDINTERFACETYPE_CONFIG, pSettings,
9324 sizeof(VDINTERFACECONFIG), &pSettings->vdFilterIfaces);
9325 AssertRC(vrc);
9326
9327 vrc = VDInterfaceAdd(&pSettings->vdIfCrypto.Core,
9328 "Medium::vdInterfaceCrypto",
9329 VDINTERFACETYPE_CRYPTO, pSettings,
9330 sizeof(VDINTERFACECRYPTO), &pSettings->vdFilterIfaces);
9331 AssertRC(vrc);
9332}
9333
9334/**
9335 * Implementation code for the "encrypt" task.
9336 *
9337 * @param task
9338 * @return
9339 */
9340HRESULT Medium::i_taskEncryptHandler(Medium::EncryptTask &task)
9341{
9342 HRESULT rc = S_OK;
9343
9344 /* Lock all in {parent,child} order. The lock is also used as a
9345 * signal from the task initiator (which releases it only after
9346 * RTThreadCreate()) that we can start the job. */
9347 ComObjPtr<Medium> pBase = i_getBase();
9348 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
9349
9350 try
9351 {
9352# ifdef VBOX_WITH_EXTPACK
9353 static const Utf8Str strExtPackPuel("Oracle VM VirtualBox Extension Pack");
9354 static const char *s_pszVDPlugin = "VDPluginCrypt";
9355 ExtPackManager *pExtPackManager = m->pVirtualBox->i_getExtPackManager();
9356 if (pExtPackManager->i_isExtPackUsable(strExtPackPuel.c_str()))
9357 {
9358 /* Load the plugin */
9359 Utf8Str strPlugin;
9360 rc = pExtPackManager->i_getLibraryPathForExtPack(s_pszVDPlugin, &strExtPackPuel, &strPlugin);
9361 if (SUCCEEDED(rc))
9362 {
9363 int vrc = VDPluginLoadFromFilename(strPlugin.c_str());
9364 if (RT_FAILURE(vrc))
9365 throw setError(VBOX_E_NOT_SUPPORTED,
9366 tr("Encrypting the image failed because the encryption plugin could not be loaded (%s)"),
9367 i_vdError(vrc).c_str());
9368 }
9369 else
9370 throw setError(VBOX_E_NOT_SUPPORTED,
9371 tr("Encryption is not supported because the extension pack '%s' is missing the encryption plugin (old extension pack installed?)"),
9372 strExtPackPuel.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"),
9377 strExtPackPuel.c_str());
9378
9379 PVBOXHDD pDisk = NULL;
9380 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &pDisk);
9381 ComAssertRCThrow(vrc, E_FAIL);
9382
9383 Medium::CryptoFilterSettings CryptoSettingsRead;
9384 Medium::CryptoFilterSettings CryptoSettingsWrite;
9385
9386 void *pvBuf = NULL;
9387 const char *pszPasswordNew = NULL;
9388 try
9389 {
9390 /* Set up disk encryption filters. */
9391 if (task.mstrCurrentPassword.isEmpty())
9392 {
9393 /*
9394 * Query whether the medium property indicating that encryption is
9395 * configured is existing.
9396 */
9397 settings::StringsMap::iterator it = pBase->m->mapProperties.find("CRYPT/KeyStore");
9398 if (it != pBase->m->mapProperties.end())
9399 throw setError(VBOX_E_PASSWORD_INCORRECT,
9400 tr("The password given for the encrypted image is incorrect"));
9401 }
9402 else
9403 {
9404 settings::StringsMap::iterator it = pBase->m->mapProperties.find("CRYPT/KeyStore");
9405 if (it == pBase->m->mapProperties.end())
9406 throw setError(VBOX_E_INVALID_OBJECT_STATE,
9407 tr("The image is not configured for encryption"));
9408
9409 i_taskEncryptSettingsSetup(&CryptoSettingsRead, NULL, it->second.c_str(), task.mstrCurrentPassword.c_str(),
9410 false /* fCreateKeyStore */);
9411 vrc = VDFilterAdd(pDisk, "CRYPT", VD_FILTER_FLAGS_READ, CryptoSettingsRead.vdFilterIfaces);
9412 if (vrc == VERR_VD_PASSWORD_INCORRECT)
9413 throw setError(VBOX_E_PASSWORD_INCORRECT,
9414 tr("The password to decrypt the image is incorrect"));
9415 else if (RT_FAILURE(vrc))
9416 throw setError(VBOX_E_INVALID_OBJECT_STATE,
9417 tr("Failed to load the decryption filter: %s"),
9418 i_vdError(vrc).c_str());
9419 }
9420
9421 if (task.mstrCipher.isNotEmpty())
9422 {
9423 if ( task.mstrNewPassword.isEmpty()
9424 && task.mstrNewPasswordId.isEmpty()
9425 && task.mstrCurrentPassword.isNotEmpty())
9426 {
9427 /* An empty password and password ID will default to the current password. */
9428 pszPasswordNew = task.mstrCurrentPassword.c_str();
9429 }
9430 else if (task.mstrNewPassword.isEmpty())
9431 throw setError(VBOX_E_OBJECT_NOT_FOUND,
9432 tr("A password must be given for the image encryption"));
9433 else if (task.mstrNewPasswordId.isEmpty())
9434 throw setError(VBOX_E_INVALID_OBJECT_STATE,
9435 tr("A valid identifier for the password must be given"));
9436 else
9437 pszPasswordNew = task.mstrNewPassword.c_str();
9438
9439 i_taskEncryptSettingsSetup(&CryptoSettingsWrite, task.mstrCipher.c_str(), NULL,
9440 pszPasswordNew, true /* fCreateKeyStore */);
9441 vrc = VDFilterAdd(pDisk, "CRYPT", VD_FILTER_FLAGS_WRITE, CryptoSettingsWrite.vdFilterIfaces);
9442 if (RT_FAILURE(vrc))
9443 throw setError(VBOX_E_INVALID_OBJECT_STATE,
9444 tr("Failed to load the encryption filter: %s"),
9445 i_vdError(vrc).c_str());
9446 }
9447 else if (task.mstrNewPasswordId.isNotEmpty() || task.mstrNewPassword.isNotEmpty())
9448 throw setError(VBOX_E_INVALID_OBJECT_STATE,
9449 tr("The password and password identifier must be empty if the output should be unencrypted"));
9450
9451 /* Open all media in the chain. */
9452 MediumLockList::Base::const_iterator mediumListBegin =
9453 task.mpMediumLockList->GetBegin();
9454 MediumLockList::Base::const_iterator mediumListEnd =
9455 task.mpMediumLockList->GetEnd();
9456 MediumLockList::Base::const_iterator mediumListLast =
9457 mediumListEnd;
9458 --mediumListLast;
9459 for (MediumLockList::Base::const_iterator it = mediumListBegin;
9460 it != mediumListEnd;
9461 ++it)
9462 {
9463 const MediumLock &mediumLock = *it;
9464 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
9465 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
9466
9467 Assert(pMedium->m->state == MediumState_LockedWrite);
9468
9469 /* Open all media but last in read-only mode. Do not handle
9470 * shareable media, as compaction and sharing are mutually
9471 * exclusive. */
9472 vrc = VDOpen(pDisk,
9473 pMedium->m->strFormat.c_str(),
9474 pMedium->m->strLocationFull.c_str(),
9475 m->uOpenFlagsDef | (it == mediumListLast ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY),
9476 pMedium->m->vdImageIfaces);
9477 if (RT_FAILURE(vrc))
9478 throw setError(VBOX_E_FILE_ERROR,
9479 tr("Could not open the medium storage unit '%s'%s"),
9480 pMedium->m->strLocationFull.c_str(),
9481 i_vdError(vrc).c_str());
9482 }
9483
9484 Assert(m->state == MediumState_LockedWrite);
9485
9486 Utf8Str location(m->strLocationFull);
9487
9488 /* unlock before the potentially lengthy operation */
9489 thisLock.release();
9490
9491 vrc = VDPrepareWithFilters(pDisk, task.mVDOperationIfaces);
9492 if (RT_FAILURE(vrc))
9493 throw setError(VBOX_E_FILE_ERROR,
9494 tr("Could not prepare disk images for encryption (%Rrc): %s"),
9495 vrc, i_vdError(vrc).c_str());
9496
9497 thisLock.acquire();
9498 /* If everything went well set the new key store. */
9499 settings::StringsMap::iterator it = pBase->m->mapProperties.find("CRYPT/KeyStore");
9500 if (it != pBase->m->mapProperties.end())
9501 pBase->m->mapProperties.erase(it);
9502
9503 /* Delete KeyId if encryption is removed or the password did change. */
9504 if ( task.mstrNewPasswordId.isNotEmpty()
9505 || task.mstrCipher.isEmpty())
9506 {
9507 it = pBase->m->mapProperties.find("CRYPT/KeyId");
9508 if (it != pBase->m->mapProperties.end())
9509 pBase->m->mapProperties.erase(it);
9510 }
9511
9512 if (CryptoSettingsWrite.pszKeyStore)
9513 {
9514 pBase->m->mapProperties["CRYPT/KeyStore"] = Utf8Str(CryptoSettingsWrite.pszKeyStore);
9515 if (task.mstrNewPasswordId.isNotEmpty())
9516 pBase->m->mapProperties["CRYPT/KeyId"] = task.mstrNewPasswordId;
9517 }
9518
9519 if (CryptoSettingsRead.pszCipherReturned)
9520 RTStrFree(CryptoSettingsRead.pszCipherReturned);
9521
9522 if (CryptoSettingsWrite.pszCipherReturned)
9523 RTStrFree(CryptoSettingsWrite.pszCipherReturned);
9524
9525 thisLock.release();
9526 pBase->i_markRegistriesModified();
9527 m->pVirtualBox->i_saveModifiedRegistries();
9528 }
9529 catch (HRESULT aRC) { rc = aRC; }
9530
9531 if (pvBuf)
9532 RTMemFree(pvBuf);
9533
9534 VDDestroy(pDisk);
9535# else
9536 throw setError(VBOX_E_NOT_SUPPORTED,
9537 tr("Encryption is not supported because extension pack support is not built in"));
9538# endif
9539 }
9540 catch (HRESULT aRC) { rc = aRC; }
9541
9542 /* Everything is explicitly unlocked when the task exits,
9543 * as the task destruction also destroys the media chain. */
9544
9545 return rc;
9546}
9547
9548/* 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