VirtualBox

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

Last change on this file since 61917 was 61910, checked in by vboxsync, 9 years ago

Main/src-server/MediumImpl.cpp: Warning.

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