VirtualBox

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

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

Main/Medium: Logic change for IMedium::ChangeEncryption, an empty password and password ID will result in the current password being used for encryption if the cipher is not empty. Useful for changing the encryption cipher of an image while still using the same password

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

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