VirtualBox

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

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

Main/Medium: Add basic support to encrypt/decrypt media, work in progress

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

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