VirtualBox

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

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

Main: Fixes for disk encryption support and make use of the optimized filter preparation when encrypting images for the first time

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

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