VirtualBox

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

Last change on this file since 61639 was 61604, checked in by vboxsync, 9 years ago

bugref:8344. using RTPATH_SLASH.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 350.5 KB
Line 
1/* $Id: MediumImpl.cpp 61604 2016-06-09 08:02:03Z 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.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17#include "MediumImpl.h"
18#include "TokenImpl.h"
19#include "ProgressImpl.h"
20#include "SystemPropertiesImpl.h"
21#include "VirtualBoxImpl.h"
22#include "ExtPackManagerImpl.h"
23
24#include "AutoCaller.h"
25#include "Logging.h"
26
27#include "VBox/com/MultiResult.h"
28#include "VBox/com/ErrorInfo.h"
29
30#include <VBox/err.h>
31#include <VBox/settings.h>
32
33#include <iprt/param.h>
34#include <iprt/path.h>
35#include <iprt/file.h>
36#include <iprt/tcp.h>
37#include <iprt/cpp/utils.h>
38#include <iprt/memsafer.h>
39#include <iprt/base64.h>
40
41#include <VBox/vd.h>
42
43#include <algorithm>
44#include <list>
45
46#include <openssl/rand.h>
47
48typedef std::list<Guid> GuidList;
49
50////////////////////////////////////////////////////////////////////////////////
51//
52// Medium data definition
53//
54////////////////////////////////////////////////////////////////////////////////
55
56/** Describes how a machine refers to this medium. */
57struct BackRef
58{
59 /** Equality predicate for stdc++. */
60 struct EqualsTo : public std::unary_function <BackRef, bool>
61 {
62 explicit EqualsTo(const Guid &aMachineId) : machineId(aMachineId) {}
63
64 bool operator()(const argument_type &aThat) const
65 {
66 return aThat.machineId == machineId;
67 }
68
69 const Guid machineId;
70 };
71
72 BackRef(const Guid &aMachineId,
73 const Guid &aSnapshotId = Guid::Empty)
74 : machineId(aMachineId),
75 fInCurState(aSnapshotId.isZero())
76 {
77 if (aSnapshotId.isValid() && !aSnapshotId.isZero())
78 llSnapshotIds.push_back(aSnapshotId);
79 }
80
81 Guid machineId;
82 bool fInCurState : 1;
83 GuidList llSnapshotIds;
84};
85
86typedef std::list<BackRef> BackRefList;
87
88struct Medium::Data
89{
90 Data()
91 : pVirtualBox(NULL),
92 state(MediumState_NotCreated),
93 variant(MediumVariant_Standard),
94 size(0),
95 readers(0),
96 preLockState(MediumState_NotCreated),
97 queryInfoSem(LOCKCLASS_MEDIUMQUERY),
98 queryInfoRunning(false),
99 type(MediumType_Normal),
100 devType(DeviceType_HardDisk),
101 logicalSize(0),
102 hddOpenMode(OpenReadWrite),
103 autoReset(false),
104 hostDrive(false),
105 implicit(false),
106 fClosing(false),
107 uOpenFlagsDef(VD_OPEN_FLAGS_IGNORE_FLUSH),
108 numCreateDiffTasks(0),
109 vdDiskIfaces(NULL),
110 vdImageIfaces(NULL),
111 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"
3001 * there is no backslash in the end
3002 * and there is no filename with extension(suffix) in the end
3003 * In this case just set destMediumFileName to NULL and
3004 * and add '/' in the end of path.destMediumPath
3005 */
3006 destMediumFileName.setNull();
3007 destMediumPath.append(RTPATH_SLASH);
3008 }
3009
3010 if (destMediumFileName.isEmpty())
3011 {
3012 /* case when a target name is absent */
3013 destMediumPath.append(sourceMediumFileName);
3014 }
3015 else
3016 {
3017 if (destMediumPath.equals(destMediumFileName))
3018 {
3019 /* the passed target path consist of only a filename without directory
3020 * next move medium within the source directory with the passed new name
3021 */
3022 destMediumPath = sourceMediumPath.stripFilename().append(RTPATH_SLASH).append(destMediumFileName);
3023 }
3024 else
3025 {
3026 /* set the target extension like on the source. Permission to convert is prohibited */
3027 suffix = i_getFormat();
3028
3029 if (suffix.compare("RAW", Utf8Str::CaseInsensitive) == 0)
3030 {
3031 if(i_getDeviceType() == DeviceType_DVD)
3032 {
3033 suffix = "iso";
3034 }
3035 else
3036 {
3037 rc = setError(VERR_NOT_A_FILE,
3038 tr("Medium '%s' has RAW type. \"Move\" operation isn't supported for this type."),
3039 i_getLocationFull().c_str());
3040 throw rc;
3041 }
3042 }
3043
3044 suffix.toLower();
3045 destMediumPath.stripSuffix().append('.').append(suffix);
3046 }
3047 }
3048
3049 if (i_isMediumFormatFile())
3050 {
3051 /* Check path for a new file object */
3052 rc = VirtualBox::i_ensureFilePathExists(destMediumPath, true);
3053 if (FAILED(rc))
3054 throw rc;
3055 }
3056 else
3057 {
3058 rc = setError(VERR_NOT_A_FILE,
3059 tr("Medium '%s' isn't a file object. \"Move\" operation isn't supported."),
3060 i_getLocationFull().c_str());
3061 throw rc;
3062 }
3063
3064 /* Set needed variables for "moving" procedure. It'll be used later in separate thread task */
3065 rc = i_preparationForMoving(destMediumPath);
3066 if (FAILED(rc))
3067 {
3068 rc = setError(VERR_NO_CHANGE,
3069 tr("Medium '%s' is already in the correct location"),
3070 i_getLocationFull().c_str());
3071 throw rc;
3072 }
3073 }
3074
3075 /* Check VMs which have this medium attached to*/
3076 std::vector<com::Guid> aMachineIds;
3077 rc = getMachineIds(aMachineIds);
3078 std::vector<com::Guid>::const_iterator currMachineID = aMachineIds.begin();
3079 std::vector<com::Guid>::const_iterator lastMachineID = aMachineIds.end();
3080
3081 while (currMachineID != lastMachineID)
3082 {
3083 Guid id(*currMachineID);
3084 ComObjPtr<Machine> aMachine;
3085
3086 alock.release();
3087 rc = m->pVirtualBox->i_findMachine(id, false, true, &aMachine);
3088 alock.acquire();
3089
3090 if (SUCCEEDED(rc))
3091 {
3092 MachineState_T aState;
3093 ComObjPtr<SessionMachine> sm;
3094 ComPtr<IInternalSessionControl> ctl;
3095
3096 alock.release();
3097 bool ses = aMachine->i_isSessionOpenVM(sm, &ctl);
3098 alock.acquire();
3099
3100 if (ses)
3101 {
3102 rc = setError(VERR_VM_UNEXPECTED_VM_STATE,
3103 tr("At least VM '%s' to whom this medium '%s' attached has the opened session now. "
3104 "Stop all needed VM before set a new location."),
3105 id.toString().c_str(),
3106 i_getLocationFull().c_str());
3107 throw rc;
3108 }
3109 }
3110 ++currMachineID;
3111 }
3112
3113 /* Build the source lock list. */
3114 MediumLockList *pMediumLockList(new MediumLockList());
3115 alock.release();
3116 rc = i_createMediumLockList(true /* fFailIfInaccessible */,
3117 this /* pToLockWrite */,
3118 true /* fMediumLockWriteAll */,
3119 NULL,
3120 *pMediumLockList);
3121 alock.acquire();
3122 if (FAILED(rc))
3123 {
3124 delete pMediumLockList;
3125 throw setError(rc,
3126 tr("Failed to create medium lock list for '%s'"),
3127 i_getLocationFull().c_str());
3128 }
3129 alock.release();
3130 rc = pMediumLockList->Lock();
3131 alock.acquire();
3132 if (FAILED(rc))
3133 {
3134 delete pMediumLockList;
3135 throw setError(rc,
3136 tr("Failed to lock media '%s'"),
3137 i_getLocationFull().c_str());
3138 }
3139
3140 pProgress.createObject();
3141 rc = pProgress->init(m->pVirtualBox,
3142 static_cast <IMedium *>(this),
3143 BstrFmt(tr("Moving medium '%s'"), m->strLocationFull.c_str()).raw(),
3144 TRUE /* aCancelable */);
3145
3146 /* Do the disk moving. */
3147 if (SUCCEEDED(rc))
3148 {
3149 ULONG mediumVariantFlags = i_getVariant();
3150
3151 /* setup task object to carry out the operation asynchronously */
3152 pTask = new Medium::MoveTask(this, pProgress,
3153 (MediumVariant_T)mediumVariantFlags,
3154 pMediumLockList);
3155 rc = pTask->rc();
3156 AssertComRC(rc);
3157 if (FAILED(rc))
3158 throw rc;
3159 }
3160
3161 }
3162 catch (HRESULT aRC) { rc = aRC; }
3163
3164 if (SUCCEEDED(rc))
3165 {
3166 rc = i_startThread(pTask);
3167
3168 if (SUCCEEDED(rc))
3169 pProgress.queryInterfaceTo(aProgress.asOutParam());
3170 }
3171 else
3172 {
3173 if (pTask != NULL)
3174 delete pTask;
3175 }
3176
3177 return rc;
3178}
3179
3180HRESULT Medium::compact(ComPtr<IProgress> &aProgress)
3181{
3182 HRESULT rc = S_OK;
3183 ComObjPtr<Progress> pProgress;
3184 Medium::Task *pTask = NULL;
3185
3186 try
3187 {
3188 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3189
3190 /* Build the medium lock list. */
3191 MediumLockList *pMediumLockList(new MediumLockList());
3192 alock.release();
3193 rc = i_createMediumLockList(true /* fFailIfInaccessible */ ,
3194 this /* pToLockWrite */,
3195 false /* fMediumLockWriteAll */,
3196 NULL,
3197 *pMediumLockList);
3198 alock.acquire();
3199 if (FAILED(rc))
3200 {
3201 delete pMediumLockList;
3202 throw rc;
3203 }
3204
3205 alock.release();
3206 rc = pMediumLockList->Lock();
3207 alock.acquire();
3208 if (FAILED(rc))
3209 {
3210 delete pMediumLockList;
3211 throw setError(rc,
3212 tr("Failed to lock media when compacting '%s'"),
3213 i_getLocationFull().c_str());
3214 }
3215
3216 pProgress.createObject();
3217 rc = pProgress->init(m->pVirtualBox,
3218 static_cast <IMedium *>(this),
3219 BstrFmt(tr("Compacting medium '%s'"), m->strLocationFull.c_str()).raw(),
3220 TRUE /* aCancelable */);
3221 if (FAILED(rc))
3222 {
3223 delete pMediumLockList;
3224 throw rc;
3225 }
3226
3227 /* setup task object to carry out the operation asynchronously */
3228 pTask = new Medium::CompactTask(this, pProgress, pMediumLockList);
3229 rc = pTask->rc();
3230 AssertComRC(rc);
3231 if (FAILED(rc))
3232 throw rc;
3233 }
3234 catch (HRESULT aRC) { rc = aRC; }
3235
3236 if (SUCCEEDED(rc))
3237 {
3238 rc = i_startThread(pTask);
3239
3240 if (SUCCEEDED(rc))
3241 pProgress.queryInterfaceTo(aProgress.asOutParam());
3242 }
3243 else if (pTask != NULL)
3244 delete pTask;
3245
3246 return rc;
3247}
3248
3249HRESULT Medium::resize(LONG64 aLogicalSize,
3250 ComPtr<IProgress> &aProgress)
3251{
3252 HRESULT rc = S_OK;
3253 ComObjPtr<Progress> pProgress;
3254 Medium::Task *pTask = NULL;
3255
3256 try
3257 {
3258 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3259
3260 /* Build the medium lock list. */
3261 MediumLockList *pMediumLockList(new MediumLockList());
3262 alock.release();
3263 rc = i_createMediumLockList(true /* fFailIfInaccessible */ ,
3264 this /* pToLockWrite */,
3265 false /* fMediumLockWriteAll */,
3266 NULL,
3267 *pMediumLockList);
3268 alock.acquire();
3269 if (FAILED(rc))
3270 {
3271 delete pMediumLockList;
3272 throw rc;
3273 }
3274
3275 alock.release();
3276 rc = pMediumLockList->Lock();
3277 alock.acquire();
3278 if (FAILED(rc))
3279 {
3280 delete pMediumLockList;
3281 throw setError(rc,
3282 tr("Failed to lock media when compacting '%s'"),
3283 i_getLocationFull().c_str());
3284 }
3285
3286 pProgress.createObject();
3287 rc = pProgress->init(m->pVirtualBox,
3288 static_cast <IMedium *>(this),
3289 BstrFmt(tr("Compacting medium '%s'"), m->strLocationFull.c_str()).raw(),
3290 TRUE /* aCancelable */);
3291 if (FAILED(rc))
3292 {
3293 delete pMediumLockList;
3294 throw rc;
3295 }
3296
3297 /* setup task object to carry out the operation asynchronously */
3298 pTask = new Medium::ResizeTask(this, aLogicalSize, pProgress, pMediumLockList);
3299 rc = pTask->rc();
3300 AssertComRC(rc);
3301 if (FAILED(rc))
3302 throw rc;
3303 }
3304 catch (HRESULT aRC) { rc = aRC; }
3305
3306 if (SUCCEEDED(rc))
3307 {
3308 rc = i_startThread(pTask);
3309
3310 if (SUCCEEDED(rc))
3311 pProgress.queryInterfaceTo(aProgress.asOutParam());
3312 }
3313 else if (pTask != NULL)
3314 delete pTask;
3315
3316 return rc;
3317}
3318
3319HRESULT Medium::reset(AutoCaller &autoCaller, ComPtr<IProgress> &aProgress)
3320{
3321 HRESULT rc = S_OK;
3322 ComObjPtr<Progress> pProgress;
3323 Medium::Task *pTask = NULL;
3324
3325 try
3326 {
3327 autoCaller.release();
3328
3329 /* It is possible that some previous/concurrent uninit has already
3330 * cleared the pVirtualBox reference, see #uninit(). */
3331 ComObjPtr<VirtualBox> pVirtualBox(m->pVirtualBox);
3332
3333 /* canClose() needs the tree lock */
3334 AutoMultiWriteLock2 multilock(!pVirtualBox.isNull() ? &pVirtualBox->i_getMediaTreeLockHandle() : NULL,
3335 this->lockHandle()
3336 COMMA_LOCKVAL_SRC_POS);
3337
3338 autoCaller.add();
3339 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3340
3341 LogFlowThisFunc(("ENTER for medium %s\n", m->strLocationFull.c_str()));
3342
3343 if (m->pParent.isNull())
3344 throw setError(VBOX_E_NOT_SUPPORTED,
3345 tr("Medium type of '%s' is not differencing"),
3346 m->strLocationFull.c_str());
3347
3348 rc = i_canClose();
3349 if (FAILED(rc))
3350 throw rc;
3351
3352 /* Build the medium lock list. */
3353 MediumLockList *pMediumLockList(new MediumLockList());
3354 multilock.release();
3355 rc = i_createMediumLockList(true /* fFailIfInaccessible */,
3356 this /* pToLockWrite */,
3357 false /* fMediumLockWriteAll */,
3358 NULL,
3359 *pMediumLockList);
3360 multilock.acquire();
3361 if (FAILED(rc))
3362 {
3363 delete pMediumLockList;
3364 throw rc;
3365 }
3366
3367 multilock.release();
3368 rc = pMediumLockList->Lock();
3369 multilock.acquire();
3370 if (FAILED(rc))
3371 {
3372 delete pMediumLockList;
3373 throw setError(rc,
3374 tr("Failed to lock media when resetting '%s'"),
3375 i_getLocationFull().c_str());
3376 }
3377
3378 pProgress.createObject();
3379 rc = pProgress->init(m->pVirtualBox,
3380 static_cast<IMedium*>(this),
3381 BstrFmt(tr("Resetting differencing medium '%s'"), m->strLocationFull.c_str()).raw(),
3382 FALSE /* aCancelable */);
3383 if (FAILED(rc))
3384 throw rc;
3385
3386 /* setup task object to carry out the operation asynchronously */
3387 pTask = new Medium::ResetTask(this, pProgress, pMediumLockList);
3388 rc = pTask->rc();
3389 AssertComRC(rc);
3390 if (FAILED(rc))
3391 throw rc;
3392 }
3393 catch (HRESULT aRC) { rc = aRC; }
3394
3395 if (SUCCEEDED(rc))
3396 {
3397 rc = i_startThread(pTask);
3398
3399 if (SUCCEEDED(rc))
3400 pProgress.queryInterfaceTo(aProgress.asOutParam());
3401 }
3402 else if (pTask != NULL)
3403 delete pTask;
3404
3405 LogFlowThisFunc(("LEAVE, rc=%Rhrc\n", rc));
3406
3407 return rc;
3408}
3409
3410HRESULT Medium::changeEncryption(const com::Utf8Str &aCurrentPassword, const com::Utf8Str &aCipher,
3411 const com::Utf8Str &aNewPassword, const com::Utf8Str &aNewPasswordId,
3412 ComPtr<IProgress> &aProgress)
3413{
3414 HRESULT rc = S_OK;
3415 ComObjPtr<Progress> pProgress;
3416 Medium::Task *pTask = NULL;
3417
3418 try
3419 {
3420 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3421
3422 DeviceType_T devType = i_getDeviceType();
3423 /* Cannot encrypt DVD or floppy images so far. */
3424 if ( devType == DeviceType_DVD
3425 || devType == DeviceType_Floppy)
3426 return setError(VBOX_E_INVALID_OBJECT_STATE,
3427 tr("Cannot encrypt DVD or Floppy medium '%s'"),
3428 m->strLocationFull.c_str());
3429
3430 /* Cannot encrypt media which are attached to more than one virtual machine. */
3431 if (m->backRefs.size() > 1)
3432 return setError(VBOX_E_INVALID_OBJECT_STATE,
3433 tr("Cannot encrypt medium '%s' because it is attached to %d virtual machines"),
3434 m->strLocationFull.c_str(), m->backRefs.size());
3435
3436 if (i_getChildren().size() != 0)
3437 return setError(VBOX_E_INVALID_OBJECT_STATE,
3438 tr("Cannot encrypt medium '%s' because it has %d children"),
3439 m->strLocationFull.c_str(), i_getChildren().size());
3440
3441 /* Build the medium lock list. */
3442 MediumLockList *pMediumLockList(new MediumLockList());
3443 alock.release();
3444 rc = i_createMediumLockList(true /* fFailIfInaccessible */ ,
3445 this /* pToLockWrite */,
3446 true /* fMediumLockAllWrite */,
3447 NULL,
3448 *pMediumLockList);
3449 alock.acquire();
3450 if (FAILED(rc))
3451 {
3452 delete pMediumLockList;
3453 throw rc;
3454 }
3455
3456 alock.release();
3457 rc = pMediumLockList->Lock();
3458 alock.acquire();
3459 if (FAILED(rc))
3460 {
3461 delete pMediumLockList;
3462 throw setError(rc,
3463 tr("Failed to lock media for encryption '%s'"),
3464 i_getLocationFull().c_str());
3465 }
3466
3467 /*
3468 * Check all media in the chain to not contain any branches or references to
3469 * other virtual machines, we support encrypting only a list of differencing media at the moment.
3470 */
3471 MediumLockList::Base::const_iterator mediumListBegin = pMediumLockList->GetBegin();
3472 MediumLockList::Base::const_iterator mediumListEnd = pMediumLockList->GetEnd();
3473 for (MediumLockList::Base::const_iterator it = mediumListBegin;
3474 it != mediumListEnd;
3475 ++it)
3476 {
3477 const MediumLock &mediumLock = *it;
3478 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
3479 AutoReadLock mediumReadLock(pMedium COMMA_LOCKVAL_SRC_POS);
3480
3481 Assert(pMedium->m->state == MediumState_LockedWrite);
3482
3483 if (pMedium->m->backRefs.size() > 1)
3484 {
3485 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
3486 tr("Cannot encrypt medium '%s' because it is attached to %d virtual machines"),
3487 pMedium->m->strLocationFull.c_str(), pMedium->m->backRefs.size());
3488 break;
3489 }
3490 else if (pMedium->i_getChildren().size() > 1)
3491 {
3492 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
3493 tr("Cannot encrypt medium '%s' because it has %d children"),
3494 pMedium->m->strLocationFull.c_str(), pMedium->i_getChildren().size());
3495 break;
3496 }
3497 }
3498
3499 if (FAILED(rc))
3500 {
3501 delete pMediumLockList;
3502 throw rc;
3503 }
3504
3505 const char *pszAction = "Encrypting";
3506 if ( aCurrentPassword.isNotEmpty()
3507 && aCipher.isEmpty())
3508 pszAction = "Decrypting";
3509
3510 pProgress.createObject();
3511 rc = pProgress->init(m->pVirtualBox,
3512 static_cast <IMedium *>(this),
3513 BstrFmt(tr("%s medium '%s'"), pszAction, m->strLocationFull.c_str()).raw(),
3514 TRUE /* aCancelable */);
3515 if (FAILED(rc))
3516 {
3517 delete pMediumLockList;
3518 throw rc;
3519 }
3520
3521 /* setup task object to carry out the operation asynchronously */
3522 pTask = new Medium::EncryptTask(this, aNewPassword, aCurrentPassword,
3523 aCipher, aNewPasswordId, pProgress, pMediumLockList);
3524 rc = pTask->rc();
3525 AssertComRC(rc);
3526 if (FAILED(rc))
3527 throw rc;
3528 }
3529 catch (HRESULT aRC) { rc = aRC; }
3530
3531 if (SUCCEEDED(rc))
3532 {
3533 rc = i_startThread(pTask);
3534
3535 if (SUCCEEDED(rc))
3536 pProgress.queryInterfaceTo(aProgress.asOutParam());
3537 }
3538 else if (pTask != NULL)
3539 delete pTask;
3540
3541 return rc;
3542}
3543
3544HRESULT Medium::getEncryptionSettings(com::Utf8Str &aCipher, com::Utf8Str &aPasswordId)
3545{
3546 HRESULT rc = S_OK;
3547
3548 try
3549 {
3550 ComObjPtr<Medium> pBase = i_getBase();
3551 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3552
3553 /* Check whether encryption is configured for this medium. */
3554 settings::StringsMap::iterator it = pBase->m->mapProperties.find("CRYPT/KeyStore");
3555 if (it == pBase->m->mapProperties.end())
3556 throw VBOX_E_NOT_SUPPORTED;
3557
3558# ifdef VBOX_WITH_EXTPACK
3559 static const Utf8Str strExtPackPuel("Oracle VM VirtualBox Extension Pack");
3560 static const char *s_pszVDPlugin = "VDPluginCrypt";
3561 ExtPackManager *pExtPackManager = m->pVirtualBox->i_getExtPackManager();
3562 if (pExtPackManager->i_isExtPackUsable(strExtPackPuel.c_str()))
3563 {
3564 /* Load the plugin */
3565 Utf8Str strPlugin;
3566 rc = pExtPackManager->i_getLibraryPathForExtPack(s_pszVDPlugin, &strExtPackPuel, &strPlugin);
3567 if (SUCCEEDED(rc))
3568 {
3569 int vrc = VDPluginLoadFromFilename(strPlugin.c_str());
3570 if (RT_FAILURE(vrc))
3571 throw setError(VBOX_E_NOT_SUPPORTED,
3572 tr("Retrieving encryption settings of the image failed because the encryption plugin could not be loaded (%s)"),
3573 i_vdError(vrc).c_str());
3574 }
3575 else
3576 throw setError(VBOX_E_NOT_SUPPORTED,
3577 tr("Encryption is not supported because the extension pack '%s' is missing the encryption plugin (old extension pack installed?)"),
3578 strExtPackPuel.c_str());
3579 }
3580 else
3581 throw setError(VBOX_E_NOT_SUPPORTED,
3582 tr("Encryption is not supported because the extension pack '%s' is missing"),
3583 strExtPackPuel.c_str());
3584
3585 PVBOXHDD pDisk = NULL;
3586 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &pDisk);
3587 ComAssertRCThrow(vrc, E_FAIL);
3588
3589 Medium::CryptoFilterSettings CryptoSettings;
3590
3591 i_taskEncryptSettingsSetup(&CryptoSettings, NULL, it->second.c_str(), NULL, false /* fCreateKeyStore */);
3592 vrc = VDFilterAdd(pDisk, "CRYPT", VD_FILTER_FLAGS_READ | VD_FILTER_FLAGS_INFO, CryptoSettings.vdFilterIfaces);
3593 if (RT_FAILURE(vrc))
3594 throw setError(VBOX_E_INVALID_OBJECT_STATE,
3595 tr("Failed to load the encryption filter: %s"),
3596 i_vdError(vrc).c_str());
3597
3598 it = pBase->m->mapProperties.find("CRYPT/KeyId");
3599 if (it == pBase->m->mapProperties.end())
3600 throw setError(VBOX_E_INVALID_OBJECT_STATE,
3601 tr("Image is configured for encryption but doesn't has a KeyId set"));
3602
3603 aPasswordId = it->second.c_str();
3604 aCipher = CryptoSettings.pszCipherReturned;
3605 RTStrFree(CryptoSettings.pszCipherReturned);
3606
3607 VDDestroy(pDisk);
3608# else
3609 throw setError(VBOX_E_NOT_SUPPORTED,
3610 tr("Encryption is not supported because extension pack support is not built in"));
3611# endif
3612 }
3613 catch (HRESULT aRC) { rc = aRC; }
3614
3615 return rc;
3616}
3617
3618HRESULT Medium::checkEncryptionPassword(const com::Utf8Str &aPassword)
3619{
3620 HRESULT rc = S_OK;
3621
3622 try
3623 {
3624 ComObjPtr<Medium> pBase = i_getBase();
3625 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3626
3627 settings::StringsMap::iterator it = pBase->m->mapProperties.find("CRYPT/KeyStore");
3628 if (it == pBase->m->mapProperties.end())
3629 throw setError(VBOX_E_NOT_SUPPORTED,
3630 tr("The image is not configured for encryption"));
3631
3632 if (aPassword.isEmpty())
3633 throw setError(E_INVALIDARG,
3634 tr("The given password must not be empty"));
3635
3636# ifdef VBOX_WITH_EXTPACK
3637 static const Utf8Str strExtPackPuel("Oracle VM VirtualBox Extension Pack");
3638 static const char *s_pszVDPlugin = "VDPluginCrypt";
3639 ExtPackManager *pExtPackManager = m->pVirtualBox->i_getExtPackManager();
3640 if (pExtPackManager->i_isExtPackUsable(strExtPackPuel.c_str()))
3641 {
3642 /* Load the plugin */
3643 Utf8Str strPlugin;
3644 rc = pExtPackManager->i_getLibraryPathForExtPack(s_pszVDPlugin, &strExtPackPuel, &strPlugin);
3645 if (SUCCEEDED(rc))
3646 {
3647 int vrc = VDPluginLoadFromFilename(strPlugin.c_str());
3648 if (RT_FAILURE(vrc))
3649 throw setError(VBOX_E_NOT_SUPPORTED,
3650 tr("Retrieving encryption settings of the image failed because the encryption plugin could not be loaded (%s)"),
3651 i_vdError(vrc).c_str());
3652 }
3653 else
3654 throw setError(VBOX_E_NOT_SUPPORTED,
3655 tr("Encryption is not supported because the extension pack '%s' is missing the encryption plugin (old extension pack installed?)"),
3656 strExtPackPuel.c_str());
3657 }
3658 else
3659 throw setError(VBOX_E_NOT_SUPPORTED,
3660 tr("Encryption is not supported because the extension pack '%s' is missing"),
3661 strExtPackPuel.c_str());
3662
3663 PVBOXHDD pDisk = NULL;
3664 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &pDisk);
3665 ComAssertRCThrow(vrc, E_FAIL);
3666
3667 Medium::CryptoFilterSettings CryptoSettings;
3668
3669 i_taskEncryptSettingsSetup(&CryptoSettings, NULL, it->second.c_str(), aPassword.c_str(),
3670 false /* fCreateKeyStore */);
3671 vrc = VDFilterAdd(pDisk, "CRYPT", VD_FILTER_FLAGS_READ, CryptoSettings.vdFilterIfaces);
3672 if (vrc == VERR_VD_PASSWORD_INCORRECT)
3673 throw setError(VBOX_E_PASSWORD_INCORRECT,
3674 tr("The given password is incorrect"));
3675 else if (RT_FAILURE(vrc))
3676 throw setError(VBOX_E_INVALID_OBJECT_STATE,
3677 tr("Failed to load the encryption filter: %s"),
3678 i_vdError(vrc).c_str());
3679
3680 VDDestroy(pDisk);
3681# else
3682 throw setError(VBOX_E_NOT_SUPPORTED,
3683 tr("Encryption is not supported because extension pack support is not built in"));
3684# endif
3685 }
3686 catch (HRESULT aRC) { rc = aRC; }
3687
3688 return rc;
3689}
3690
3691////////////////////////////////////////////////////////////////////////////////
3692//
3693// Medium public internal methods
3694//
3695////////////////////////////////////////////////////////////////////////////////
3696
3697/**
3698 * Internal method to return the medium's parent medium. Must have caller + locking!
3699 * @return
3700 */
3701const ComObjPtr<Medium>& Medium::i_getParent() const
3702{
3703 return m->pParent;
3704}
3705
3706/**
3707 * Internal method to return the medium's list of child media. Must have caller + locking!
3708 * @return
3709 */
3710const MediaList& Medium::i_getChildren() const
3711{
3712 return m->llChildren;
3713}
3714
3715/**
3716 * Internal method to return the medium's GUID. Must have caller + locking!
3717 * @return
3718 */
3719const Guid& Medium::i_getId() const
3720{
3721 return m->id;
3722}
3723
3724/**
3725 * Internal method to return the medium's state. Must have caller + locking!
3726 * @return
3727 */
3728MediumState_T Medium::i_getState() const
3729{
3730 return m->state;
3731}
3732
3733/**
3734 * Internal method to return the medium's variant. Must have caller + locking!
3735 * @return
3736 */
3737MediumVariant_T Medium::i_getVariant() const
3738{
3739 return m->variant;
3740}
3741
3742/**
3743 * Internal method which returns true if this medium represents a host drive.
3744 * @return
3745 */
3746bool Medium::i_isHostDrive() const
3747{
3748 return m->hostDrive;
3749}
3750
3751/**
3752 * Internal method to return the medium's full location. Must have caller + locking!
3753 * @return
3754 */
3755const Utf8Str& Medium::i_getLocationFull() const
3756{
3757 return m->strLocationFull;
3758}
3759
3760/**
3761 * Internal method to return the medium's format string. Must have caller + locking!
3762 * @return
3763 */
3764const Utf8Str& Medium::i_getFormat() const
3765{
3766 return m->strFormat;
3767}
3768
3769/**
3770 * Internal method to return the medium's format object. Must have caller + locking!
3771 * @return
3772 */
3773const ComObjPtr<MediumFormat>& Medium::i_getMediumFormat() const
3774{
3775 return m->formatObj;
3776}
3777
3778/**
3779 * Internal method that returns true if the medium is represented by a file on the host disk
3780 * (and not iSCSI or something).
3781 * @return
3782 */
3783bool Medium::i_isMediumFormatFile() const
3784{
3785 if ( m->formatObj
3786 && (m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File)
3787 )
3788 return true;
3789 return false;
3790}
3791
3792/**
3793 * Internal method to return the medium's size. Must have caller + locking!
3794 * @return
3795 */
3796uint64_t Medium::i_getSize() const
3797{
3798 return m->size;
3799}
3800
3801/**
3802 * Returns the medium device type. Must have caller + locking!
3803 * @return
3804 */
3805DeviceType_T Medium::i_getDeviceType() const
3806{
3807 return m->devType;
3808}
3809
3810/**
3811 * Returns the medium type. Must have caller + locking!
3812 * @return
3813 */
3814MediumType_T Medium::i_getType() const
3815{
3816 return m->type;
3817}
3818
3819/**
3820 * Returns a short version of the location attribute.
3821 *
3822 * @note Must be called from under this object's read or write lock.
3823 */
3824Utf8Str Medium::i_getName()
3825{
3826 Utf8Str name = RTPathFilename(m->strLocationFull.c_str());
3827 return name;
3828}
3829
3830/**
3831 * This adds the given UUID to the list of media registries in which this
3832 * medium should be registered. The UUID can either be a machine UUID,
3833 * to add a machine registry, or the global registry UUID as returned by
3834 * VirtualBox::getGlobalRegistryId().
3835 *
3836 * Note that for hard disks, this method does nothing if the medium is
3837 * already in another registry to avoid having hard disks in more than
3838 * one registry, which causes trouble with keeping diff images in sync.
3839 * See getFirstRegistryMachineId() for details.
3840 *
3841 * @param id
3842 * @return true if the registry was added; false if the given id was already on the list.
3843 */
3844bool Medium::i_addRegistry(const Guid& id)
3845{
3846 AutoCaller autoCaller(this);
3847 if (FAILED(autoCaller.rc()))
3848 return false;
3849 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3850
3851 bool fAdd = true;
3852
3853 // hard disks cannot be in more than one registry
3854 if ( m->devType == DeviceType_HardDisk
3855 && m->llRegistryIDs.size() > 0)
3856 fAdd = false;
3857
3858 // no need to add the UUID twice
3859 if (fAdd)
3860 {
3861 for (GuidList::const_iterator it = m->llRegistryIDs.begin();
3862 it != m->llRegistryIDs.end();
3863 ++it)
3864 {
3865 if ((*it) == id)
3866 {
3867 fAdd = false;
3868 break;
3869 }
3870 }
3871 }
3872
3873 if (fAdd)
3874 m->llRegistryIDs.push_back(id);
3875
3876 return fAdd;
3877}
3878
3879/**
3880 * This adds the given UUID to the list of media registries in which this
3881 * medium should be registered. The UUID can either be a machine UUID,
3882 * to add a machine registry, or the global registry UUID as returned by
3883 * VirtualBox::getGlobalRegistryId(). This recurses over all children.
3884 *
3885 * Note that for hard disks, this method does nothing if the medium is
3886 * already in another registry to avoid having hard disks in more than
3887 * one registry, which causes trouble with keeping diff images in sync.
3888 * See getFirstRegistryMachineId() for details.
3889 *
3890 * @note the caller must hold the media tree lock for reading.
3891 *
3892 * @param id
3893 * @return true if the registry was added; false if the given id was already on the list.
3894 */
3895bool Medium::i_addRegistryRecursive(const Guid &id)
3896{
3897 AutoCaller autoCaller(this);
3898 if (FAILED(autoCaller.rc()))
3899 return false;
3900
3901 bool fAdd = i_addRegistry(id);
3902
3903 // protected by the medium tree lock held by our original caller
3904 for (MediaList::const_iterator it = i_getChildren().begin();
3905 it != i_getChildren().end();
3906 ++it)
3907 {
3908 Medium *pChild = *it;
3909 fAdd |= pChild->i_addRegistryRecursive(id);
3910 }
3911
3912 return fAdd;
3913}
3914
3915/**
3916 * Removes the given UUID from the list of media registry UUIDs of this medium.
3917 *
3918 * @param id
3919 * @return true if the UUID was found or false if not.
3920 */
3921bool Medium::i_removeRegistry(const Guid &id)
3922{
3923 AutoCaller autoCaller(this);
3924 if (FAILED(autoCaller.rc()))
3925 return false;
3926 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3927
3928 bool fRemove = false;
3929
3930 // @todo r=klaus eliminate this code, replace it by using find.
3931 for (GuidList::iterator it = m->llRegistryIDs.begin();
3932 it != m->llRegistryIDs.end();
3933 ++it)
3934 {
3935 if ((*it) == id)
3936 {
3937 // getting away with this as the iterator isn't used after
3938 m->llRegistryIDs.erase(it);
3939 fRemove = true;
3940 break;
3941 }
3942 }
3943
3944 return fRemove;
3945}
3946
3947/**
3948 * Removes the given UUID from the list of media registry UUIDs, for this
3949 * medium and all its children recursively.
3950 *
3951 * @note the caller must hold the media tree lock for reading.
3952 *
3953 * @param id
3954 * @return true if the UUID was found or false if not.
3955 */
3956bool Medium::i_removeRegistryRecursive(const Guid &id)
3957{
3958 AutoCaller autoCaller(this);
3959 if (FAILED(autoCaller.rc()))
3960 return false;
3961
3962 bool fRemove = i_removeRegistry(id);
3963
3964 // protected by the medium tree lock held by our original caller
3965 for (MediaList::const_iterator it = i_getChildren().begin();
3966 it != i_getChildren().end();
3967 ++it)
3968 {
3969 Medium *pChild = *it;
3970 fRemove |= pChild->i_removeRegistryRecursive(id);
3971 }
3972
3973 return fRemove;
3974}
3975
3976/**
3977 * Returns true if id is in the list of media registries for this medium.
3978 *
3979 * Must have caller + read locking!
3980 *
3981 * @param id
3982 * @return
3983 */
3984bool Medium::i_isInRegistry(const Guid &id)
3985{
3986 // @todo r=klaus eliminate this code, replace it by using find.
3987 for (GuidList::const_iterator it = m->llRegistryIDs.begin();
3988 it != m->llRegistryIDs.end();
3989 ++it)
3990 {
3991 if (*it == id)
3992 return true;
3993 }
3994
3995 return false;
3996}
3997
3998/**
3999 * Internal method to return the medium's first registry machine (i.e. the machine in whose
4000 * machine XML this medium is listed).
4001 *
4002 * Every attached medium must now (4.0) reside in at least one media registry, which is identified
4003 * by a UUID. This is either a machine UUID if the machine is from 4.0 or newer, in which case
4004 * machines have their own media registries, or it is the pseudo-UUID of the VirtualBox
4005 * object if the machine is old and still needs the global registry in VirtualBox.xml.
4006 *
4007 * By definition, hard disks may only be in one media registry, in which all its children
4008 * will be stored as well. Otherwise we run into problems with having keep multiple registries
4009 * in sync. (This is the "cloned VM" case in which VM1 may link to the disks of VM2; in this
4010 * case, only VM2's registry is used for the disk in question.)
4011 *
4012 * If there is no medium registry, particularly if the medium has not been attached yet, this
4013 * does not modify uuid and returns false.
4014 *
4015 * ISOs and RAWs, by contrast, can be in more than one repository to make things easier for
4016 * the user.
4017 *
4018 * Must have caller + locking!
4019 *
4020 * @param uuid Receives first registry machine UUID, if available.
4021 * @return true if uuid was set.
4022 */
4023bool Medium::i_getFirstRegistryMachineId(Guid &uuid) const
4024{
4025 if (m->llRegistryIDs.size())
4026 {
4027 uuid = m->llRegistryIDs.front();
4028 return true;
4029 }
4030 return false;
4031}
4032
4033/**
4034 * Marks all the registries in which this medium is registered as modified.
4035 */
4036void Medium::i_markRegistriesModified()
4037{
4038 AutoCaller autoCaller(this);
4039 if (FAILED(autoCaller.rc())) return;
4040
4041 // Get local copy, as keeping the lock over VirtualBox::markRegistryModified
4042 // causes trouble with the lock order
4043 GuidList llRegistryIDs;
4044 {
4045 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4046 llRegistryIDs = m->llRegistryIDs;
4047 }
4048
4049 autoCaller.release();
4050
4051 /* Save the error information now, the implicit restore when this goes
4052 * out of scope will throw away spurious additional errors created below. */
4053 ErrorInfoKeeper eik;
4054 for (GuidList::const_iterator it = llRegistryIDs.begin();
4055 it != llRegistryIDs.end();
4056 ++it)
4057 {
4058 m->pVirtualBox->i_markRegistryModified(*it);
4059 }
4060}
4061
4062/**
4063 * Adds the given machine and optionally the snapshot to the list of the objects
4064 * this medium is attached to.
4065 *
4066 * @param aMachineId Machine ID.
4067 * @param aSnapshotId Snapshot ID; when non-empty, adds a snapshot attachment.
4068 */
4069HRESULT Medium::i_addBackReference(const Guid &aMachineId,
4070 const Guid &aSnapshotId /*= Guid::Empty*/)
4071{
4072 AssertReturn(aMachineId.isValid(), E_FAIL);
4073
4074 LogFlowThisFunc(("ENTER, aMachineId: {%RTuuid}, aSnapshotId: {%RTuuid}\n", aMachineId.raw(), aSnapshotId.raw()));
4075
4076 AutoCaller autoCaller(this);
4077 AssertComRCReturnRC(autoCaller.rc());
4078
4079 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4080
4081 switch (m->state)
4082 {
4083 case MediumState_Created:
4084 case MediumState_Inaccessible:
4085 case MediumState_LockedRead:
4086 case MediumState_LockedWrite:
4087 break;
4088
4089 default:
4090 return i_setStateError();
4091 }
4092
4093 if (m->numCreateDiffTasks > 0)
4094 return setError(VBOX_E_OBJECT_IN_USE,
4095 tr("Cannot attach medium '%s' {%RTuuid}: %u differencing child media are being created"),
4096 m->strLocationFull.c_str(),
4097 m->id.raw(),
4098 m->numCreateDiffTasks);
4099
4100 BackRefList::iterator it = std::find_if(m->backRefs.begin(),
4101 m->backRefs.end(),
4102 BackRef::EqualsTo(aMachineId));
4103 if (it == m->backRefs.end())
4104 {
4105 BackRef ref(aMachineId, aSnapshotId);
4106 m->backRefs.push_back(ref);
4107
4108 return S_OK;
4109 }
4110
4111 // if the caller has not supplied a snapshot ID, then we're attaching
4112 // to a machine a medium which represents the machine's current state,
4113 // so set the flag
4114
4115 if (aSnapshotId.isZero())
4116 {
4117 /* sanity: no duplicate attachments */
4118 if (it->fInCurState)
4119 return setError(VBOX_E_OBJECT_IN_USE,
4120 tr("Cannot attach medium '%s' {%RTuuid}: medium is already associated with the current state of machine uuid {%RTuuid}!"),
4121 m->strLocationFull.c_str(),
4122 m->id.raw(),
4123 aMachineId.raw());
4124 it->fInCurState = true;
4125
4126 return S_OK;
4127 }
4128
4129 // otherwise: a snapshot medium is being attached
4130
4131 /* sanity: no duplicate attachments */
4132 for (GuidList::const_iterator jt = it->llSnapshotIds.begin();
4133 jt != it->llSnapshotIds.end();
4134 ++jt)
4135 {
4136 const Guid &idOldSnapshot = *jt;
4137
4138 if (idOldSnapshot == aSnapshotId)
4139 {
4140#ifdef DEBUG
4141 i_dumpBackRefs();
4142#endif
4143 return setError(VBOX_E_OBJECT_IN_USE,
4144 tr("Cannot attach medium '%s' {%RTuuid} from snapshot '%RTuuid': medium is already in use by this snapshot!"),
4145 m->strLocationFull.c_str(),
4146 m->id.raw(),
4147 aSnapshotId.raw());
4148 }
4149 }
4150
4151 it->llSnapshotIds.push_back(aSnapshotId);
4152 // Do not touch fInCurState, as the image may be attached to the current
4153 // state *and* a snapshot, otherwise we lose the current state association!
4154
4155 LogFlowThisFuncLeave();
4156
4157 return S_OK;
4158}
4159
4160/**
4161 * Removes the given machine and optionally the snapshot from the list of the
4162 * objects this medium is attached to.
4163 *
4164 * @param aMachineId Machine ID.
4165 * @param aSnapshotId Snapshot ID; when non-empty, removes the snapshot
4166 * attachment.
4167 */
4168HRESULT Medium::i_removeBackReference(const Guid &aMachineId,
4169 const Guid &aSnapshotId /*= Guid::Empty*/)
4170{
4171 AssertReturn(aMachineId.isValid(), E_FAIL);
4172
4173 AutoCaller autoCaller(this);
4174 AssertComRCReturnRC(autoCaller.rc());
4175
4176 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4177
4178 BackRefList::iterator it =
4179 std::find_if(m->backRefs.begin(), m->backRefs.end(),
4180 BackRef::EqualsTo(aMachineId));
4181 AssertReturn(it != m->backRefs.end(), E_FAIL);
4182
4183 if (aSnapshotId.isZero())
4184 {
4185 /* remove the current state attachment */
4186 it->fInCurState = false;
4187 }
4188 else
4189 {
4190 /* remove the snapshot attachment */
4191 GuidList::iterator jt = std::find(it->llSnapshotIds.begin(),
4192 it->llSnapshotIds.end(),
4193 aSnapshotId);
4194
4195 AssertReturn(jt != it->llSnapshotIds.end(), E_FAIL);
4196 it->llSnapshotIds.erase(jt);
4197 }
4198
4199 /* if the backref becomes empty, remove it */
4200 if (it->fInCurState == false && it->llSnapshotIds.size() == 0)
4201 m->backRefs.erase(it);
4202
4203 return S_OK;
4204}
4205
4206/**
4207 * Internal method to return the medium's list of backrefs. Must have caller + locking!
4208 * @return
4209 */
4210const Guid* Medium::i_getFirstMachineBackrefId() const
4211{
4212 if (!m->backRefs.size())
4213 return NULL;
4214
4215 return &m->backRefs.front().machineId;
4216}
4217
4218/**
4219 * Internal method which returns a machine that either this medium or one of its children
4220 * is attached to. This is used for finding a replacement media registry when an existing
4221 * media registry is about to be deleted in VirtualBox::unregisterMachine().
4222 *
4223 * Must have caller + locking, *and* caller must hold the media tree lock!
4224 * @return
4225 */
4226const Guid* Medium::i_getAnyMachineBackref() const
4227{
4228 if (m->backRefs.size())
4229 return &m->backRefs.front().machineId;
4230
4231 for (MediaList::const_iterator it = i_getChildren().begin();
4232 it != i_getChildren().end();
4233 ++it)
4234 {
4235 Medium *pChild = *it;
4236 // recurse for this child
4237 const Guid* puuid;
4238 if ((puuid = pChild->i_getAnyMachineBackref()))
4239 return puuid;
4240 }
4241
4242 return NULL;
4243}
4244
4245const Guid* Medium::i_getFirstMachineBackrefSnapshotId() const
4246{
4247 if (!m->backRefs.size())
4248 return NULL;
4249
4250 const BackRef &ref = m->backRefs.front();
4251 if (ref.llSnapshotIds.empty())
4252 return NULL;
4253
4254 return &ref.llSnapshotIds.front();
4255}
4256
4257size_t Medium::i_getMachineBackRefCount() const
4258{
4259 return m->backRefs.size();
4260}
4261
4262#ifdef DEBUG
4263/**
4264 * Debugging helper that gets called after VirtualBox initialization that writes all
4265 * machine backreferences to the debug log.
4266 */
4267void Medium::i_dumpBackRefs()
4268{
4269 AutoCaller autoCaller(this);
4270 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4271
4272 LogFlowThisFunc(("Dumping backrefs for medium '%s':\n", m->strLocationFull.c_str()));
4273
4274 for (BackRefList::iterator it2 = m->backRefs.begin();
4275 it2 != m->backRefs.end();
4276 ++it2)
4277 {
4278 const BackRef &ref = *it2;
4279 LogFlowThisFunc((" Backref from machine {%RTuuid} (fInCurState: %d)\n", ref.machineId.raw(), ref.fInCurState));
4280
4281 for (GuidList::const_iterator jt2 = it2->llSnapshotIds.begin();
4282 jt2 != it2->llSnapshotIds.end();
4283 ++jt2)
4284 {
4285 const Guid &id = *jt2;
4286 LogFlowThisFunc((" Backref from snapshot {%RTuuid}\n", id.raw()));
4287 }
4288 }
4289}
4290#endif
4291
4292/**
4293 * Checks if the given change of \a aOldPath to \a aNewPath affects the location
4294 * of this media and updates it if necessary to reflect the new location.
4295 *
4296 * @param aOldPath Old path (full).
4297 * @param aNewPath New path (full).
4298 *
4299 * @note Locks this object for writing.
4300 */
4301HRESULT Medium::i_updatePath(const Utf8Str &strOldPath, const Utf8Str &strNewPath)
4302{
4303 AssertReturn(!strOldPath.isEmpty(), E_FAIL);
4304 AssertReturn(!strNewPath.isEmpty(), E_FAIL);
4305
4306 AutoCaller autoCaller(this);
4307 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4308
4309 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4310
4311 LogFlowThisFunc(("locationFull.before='%s'\n", m->strLocationFull.c_str()));
4312
4313 const char *pcszMediumPath = m->strLocationFull.c_str();
4314
4315 if (RTPathStartsWith(pcszMediumPath, strOldPath.c_str()))
4316 {
4317 Utf8Str newPath(strNewPath);
4318 newPath.append(pcszMediumPath + strOldPath.length());
4319 unconst(m->strLocationFull) = newPath;
4320
4321 LogFlowThisFunc(("locationFull.after='%s'\n", m->strLocationFull.c_str()));
4322 // we changed something
4323 return S_OK;
4324 }
4325
4326 // no change was necessary, signal error which the caller needs to interpret
4327 return VBOX_E_FILE_ERROR;
4328}
4329
4330/**
4331 * Returns the base medium of the media chain this medium is part of.
4332 *
4333 * The base medium is found by walking up the parent-child relationship axis.
4334 * If the medium doesn't have a parent (i.e. it's a base medium), it
4335 * returns itself in response to this method.
4336 *
4337 * @param aLevel Where to store the number of ancestors of this medium
4338 * (zero for the base), may be @c NULL.
4339 *
4340 * @note Locks medium tree for reading.
4341 */
4342ComObjPtr<Medium> Medium::i_getBase(uint32_t *aLevel /*= NULL*/)
4343{
4344 ComObjPtr<Medium> pBase;
4345
4346 /* it is possible that some previous/concurrent uninit has already cleared
4347 * the pVirtualBox reference, and in this case we don't need to continue */
4348 ComObjPtr<VirtualBox> pVirtualBox(m->pVirtualBox);
4349 if (!pVirtualBox)
4350 return pBase;
4351
4352 /* we access m->pParent */
4353 AutoReadLock treeLock(pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4354
4355 AutoCaller autoCaller(this);
4356 AssertReturn(autoCaller.isOk(), pBase);
4357
4358 pBase = this;
4359 uint32_t level = 0;
4360
4361 if (m->pParent)
4362 {
4363 for (;;)
4364 {
4365 AutoCaller baseCaller(pBase);
4366 AssertReturn(baseCaller.isOk(), pBase);
4367
4368 if (pBase->m->pParent.isNull())
4369 break;
4370
4371 pBase = pBase->m->pParent;
4372 ++level;
4373 }
4374 }
4375
4376 if (aLevel != NULL)
4377 *aLevel = level;
4378
4379 return pBase;
4380}
4381
4382/**
4383 * Returns the depth of this medium in the media chain.
4384 *
4385 * @note Locks medium tree for reading.
4386 */
4387uint32_t Medium::i_getDepth()
4388{
4389 /* it is possible that some previous/concurrent uninit has already cleared
4390 * the pVirtualBox reference, and in this case we don't need to continue */
4391 ComObjPtr<VirtualBox> pVirtualBox(m->pVirtualBox);
4392 if (!pVirtualBox)
4393 return 1;
4394
4395 /* we access m->pParent */
4396 AutoReadLock treeLock(pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4397
4398 uint32_t cDepth = 0;
4399 ComObjPtr<Medium> pMedium(this);
4400 while (!pMedium.isNull())
4401 {
4402 AutoCaller autoCaller(this);
4403 AssertReturn(autoCaller.isOk(), cDepth + 1);
4404
4405 pMedium = pMedium->m->pParent;
4406 cDepth++;
4407 }
4408
4409 return cDepth;
4410}
4411
4412/**
4413 * Returns @c true if this medium cannot be modified because it has
4414 * dependents (children) or is part of the snapshot. Related to the medium
4415 * type and posterity, not to the current media state.
4416 *
4417 * @note Locks this object and medium tree for reading.
4418 */
4419bool Medium::i_isReadOnly()
4420{
4421 /* it is possible that some previous/concurrent uninit has already cleared
4422 * the pVirtualBox reference, and in this case we don't need to continue */
4423 ComObjPtr<VirtualBox> pVirtualBox(m->pVirtualBox);
4424 if (!pVirtualBox)
4425 return false;
4426
4427 /* we access children */
4428 AutoReadLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4429
4430 AutoCaller autoCaller(this);
4431 AssertComRCReturn(autoCaller.rc(), false);
4432
4433 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4434
4435 switch (m->type)
4436 {
4437 case MediumType_Normal:
4438 {
4439 if (i_getChildren().size() != 0)
4440 return true;
4441
4442 for (BackRefList::const_iterator it = m->backRefs.begin();
4443 it != m->backRefs.end(); ++it)
4444 if (it->llSnapshotIds.size() != 0)
4445 return true;
4446
4447 if (m->variant & MediumVariant_VmdkStreamOptimized)
4448 return true;
4449
4450 return false;
4451 }
4452 case MediumType_Immutable:
4453 case MediumType_MultiAttach:
4454 return true;
4455 case MediumType_Writethrough:
4456 case MediumType_Shareable:
4457 case MediumType_Readonly: /* explicit readonly media has no diffs */
4458 return false;
4459 default:
4460 break;
4461 }
4462
4463 AssertFailedReturn(false);
4464}
4465
4466/**
4467 * Internal method to return the medium's size. Must have caller + locking!
4468 * @return
4469 */
4470void Medium::i_updateId(const Guid &id)
4471{
4472 unconst(m->id) = id;
4473}
4474
4475/**
4476 * Saves the settings of one medium.
4477 *
4478 * @note Caller MUST take care of the medium tree lock and caller.
4479 *
4480 * @param data Settings struct to be updated.
4481 * @param strHardDiskFolder Folder for which paths should be relative.
4482 */
4483void Medium::i_saveSettingsOne(settings::Medium &data, const Utf8Str &strHardDiskFolder)
4484{
4485 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4486
4487 data.uuid = m->id;
4488
4489 // make path relative if needed
4490 if ( !strHardDiskFolder.isEmpty()
4491 && RTPathStartsWith(m->strLocationFull.c_str(), strHardDiskFolder.c_str())
4492 )
4493 data.strLocation = m->strLocationFull.substr(strHardDiskFolder.length() + 1);
4494 else
4495 data.strLocation = m->strLocationFull;
4496 data.strFormat = m->strFormat;
4497
4498 /* optional, only for diffs, default is false */
4499 if (m->pParent)
4500 data.fAutoReset = m->autoReset;
4501 else
4502 data.fAutoReset = false;
4503
4504 /* optional */
4505 data.strDescription = m->strDescription;
4506
4507 /* optional properties */
4508 data.properties.clear();
4509
4510 /* handle iSCSI initiator secrets transparently */
4511 bool fHaveInitiatorSecretEncrypted = false;
4512 Utf8Str strCiphertext;
4513 settings::StringsMap::const_iterator itPln = m->mapProperties.find("InitiatorSecret");
4514 if ( itPln != m->mapProperties.end()
4515 && !itPln->second.isEmpty())
4516 {
4517 /* Encrypt the plain secret. If that does not work (i.e. no or wrong settings key
4518 * specified), just use the encrypted secret (if there is any). */
4519 int rc = m->pVirtualBox->i_encryptSetting(itPln->second, &strCiphertext);
4520 if (RT_SUCCESS(rc))
4521 fHaveInitiatorSecretEncrypted = true;
4522 }
4523 for (settings::StringsMap::const_iterator it = m->mapProperties.begin();
4524 it != m->mapProperties.end();
4525 ++it)
4526 {
4527 /* only save properties that have non-default values */
4528 if (!it->second.isEmpty())
4529 {
4530 const Utf8Str &name = it->first;
4531 const Utf8Str &value = it->second;
4532 /* do NOT store the plain InitiatorSecret */
4533 if ( !fHaveInitiatorSecretEncrypted
4534 || !name.equals("InitiatorSecret"))
4535 data.properties[name] = value;
4536 }
4537 }
4538 if (fHaveInitiatorSecretEncrypted)
4539 data.properties["InitiatorSecretEncrypted"] = strCiphertext;
4540
4541 /* only for base media */
4542 if (m->pParent.isNull())
4543 data.hdType = m->type;
4544}
4545
4546/**
4547 * Saves medium data by putting it into the provided data structure.
4548 * Recurses over all children to save their settings, too.
4549 *
4550 * @param data Settings struct to be updated.
4551 * @param strHardDiskFolder Folder for which paths should be relative.
4552 *
4553 * @note Locks this object, medium tree and children for reading.
4554 */
4555HRESULT Medium::i_saveSettings(settings::Medium &data,
4556 const Utf8Str &strHardDiskFolder)
4557{
4558 /* we access m->pParent */
4559 AutoReadLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4560
4561 AutoCaller autoCaller(this);
4562 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4563
4564 i_saveSettingsOne(data, strHardDiskFolder);
4565
4566 /* save all children */
4567 settings::MediaList &llSettingsChildren = data.llChildren;
4568 for (MediaList::const_iterator it = i_getChildren().begin();
4569 it != i_getChildren().end();
4570 ++it)
4571 {
4572 // Use the element straight in the list to reduce both unnecessary
4573 // deep copying (when unwinding the recursion the entire medium
4574 // settings sub-tree is copied) and the stack footprint (the settings
4575 // need almost 1K, and there can be VMs with long image chains.
4576 llSettingsChildren.push_back(settings::Medium::Empty);
4577 HRESULT rc = (*it)->i_saveSettings(llSettingsChildren.back(), strHardDiskFolder);
4578 if (FAILED(rc))
4579 {
4580 llSettingsChildren.pop_back();
4581 return rc;
4582 }
4583 }
4584
4585 return S_OK;
4586}
4587
4588/**
4589 * Constructs a medium lock list for this medium. The lock is not taken.
4590 *
4591 * @note Caller MUST NOT hold the media tree or medium lock.
4592 *
4593 * @param fFailIfInaccessible If true, this fails with an error if a medium is inaccessible. If false,
4594 * inaccessible media are silently skipped and not locked (i.e. their state remains "Inaccessible");
4595 * this is necessary for a VM's removable media VM startup for which we do not want to fail.
4596 * @param pToLockWrite If not NULL, associate a write lock with this medium object.
4597 * @param fMediumLockWriteAll Whether to associate a write lock to all other media too.
4598 * @param pToBeParent Medium which will become the parent of this medium.
4599 * @param mediumLockList Where to store the resulting list.
4600 */
4601HRESULT Medium::i_createMediumLockList(bool fFailIfInaccessible,
4602 Medium *pToLockWrite,
4603 bool fMediumLockWriteAll,
4604 Medium *pToBeParent,
4605 MediumLockList &mediumLockList)
4606{
4607 /** @todo r=klaus this needs to be reworked, as the code below uses
4608 * i_getParent without holding the tree lock, and changing this is
4609 * a significant amount of effort. */
4610 Assert(!m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
4611 Assert(!isWriteLockOnCurrentThread());
4612
4613 AutoCaller autoCaller(this);
4614 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4615
4616 HRESULT rc = S_OK;
4617
4618 /* paranoid sanity checking if the medium has a to-be parent medium */
4619 if (pToBeParent)
4620 {
4621 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4622 ComAssertRet(i_getParent().isNull(), E_FAIL);
4623 ComAssertRet(i_getChildren().size() == 0, E_FAIL);
4624 }
4625
4626 ErrorInfoKeeper eik;
4627 MultiResult mrc(S_OK);
4628
4629 ComObjPtr<Medium> pMedium = this;
4630 while (!pMedium.isNull())
4631 {
4632 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
4633
4634 /* Accessibility check must be first, otherwise locking interferes
4635 * with getting the medium state. Lock lists are not created for
4636 * fun, and thus getting the medium status is no luxury. */
4637 MediumState_T mediumState = pMedium->i_getState();
4638 if (mediumState == MediumState_Inaccessible)
4639 {
4640 alock.release();
4641 rc = pMedium->i_queryInfo(false /* fSetImageId */, false /* fSetParentId */,
4642 autoCaller);
4643 alock.acquire();
4644 if (FAILED(rc)) return rc;
4645
4646 mediumState = pMedium->i_getState();
4647 if (mediumState == MediumState_Inaccessible)
4648 {
4649 // ignore inaccessible ISO media and silently return S_OK,
4650 // otherwise VM startup (esp. restore) may fail without good reason
4651 if (!fFailIfInaccessible)
4652 return S_OK;
4653
4654 // otherwise report an error
4655 Bstr error;
4656 rc = pMedium->COMGETTER(LastAccessError)(error.asOutParam());
4657 if (FAILED(rc)) return rc;
4658
4659 /* collect multiple errors */
4660 eik.restore();
4661 Assert(!error.isEmpty());
4662 mrc = setError(E_FAIL,
4663 "%ls",
4664 error.raw());
4665 // error message will be something like
4666 // "Could not open the medium ... VD: error VERR_FILE_NOT_FOUND opening image file ... (VERR_FILE_NOT_FOUND).
4667 eik.fetch();
4668 }
4669 }
4670
4671 if (pMedium == pToLockWrite)
4672 mediumLockList.Prepend(pMedium, true);
4673 else
4674 mediumLockList.Prepend(pMedium, fMediumLockWriteAll);
4675
4676 pMedium = pMedium->i_getParent();
4677 if (pMedium.isNull() && pToBeParent)
4678 {
4679 pMedium = pToBeParent;
4680 pToBeParent = NULL;
4681 }
4682 }
4683
4684 return mrc;
4685}
4686
4687/**
4688 * Creates a new differencing storage unit using the format of the given target
4689 * medium and the location. Note that @c aTarget must be NotCreated.
4690 *
4691 * The @a aMediumLockList parameter contains the associated medium lock list,
4692 * which must be in locked state. If @a aWait is @c true then the caller is
4693 * responsible for unlocking.
4694 *
4695 * If @a aProgress is not NULL but the object it points to is @c null then a
4696 * new progress object will be created and assigned to @a *aProgress on
4697 * success, otherwise the existing progress object is used. If @a aProgress is
4698 * NULL, then no progress object is created/used at all.
4699 *
4700 * When @a aWait is @c false, this method will create a thread to perform the
4701 * create operation asynchronously and will return immediately. Otherwise, it
4702 * will perform the operation on the calling thread and will not return to the
4703 * caller until the operation is completed. Note that @a aProgress cannot be
4704 * NULL when @a aWait is @c false (this method will assert in this case).
4705 *
4706 * @param aTarget Target medium.
4707 * @param aVariant Precise medium variant to create.
4708 * @param aMediumLockList List of media which should be locked.
4709 * @param aProgress Where to find/store a Progress object to track
4710 * operation completion.
4711 * @param aWait @c true if this method should block instead of
4712 * creating an asynchronous thread.
4713 *
4714 * @note Locks this object and @a aTarget for writing.
4715 */
4716HRESULT Medium::i_createDiffStorage(ComObjPtr<Medium> &aTarget,
4717 MediumVariant_T aVariant,
4718 MediumLockList *aMediumLockList,
4719 ComObjPtr<Progress> *aProgress,
4720 bool aWait)
4721{
4722 AssertReturn(!aTarget.isNull(), E_FAIL);
4723 AssertReturn(aMediumLockList, E_FAIL);
4724 AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
4725
4726 AutoCaller autoCaller(this);
4727 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4728
4729 AutoCaller targetCaller(aTarget);
4730 if (FAILED(targetCaller.rc())) return targetCaller.rc();
4731
4732 HRESULT rc = S_OK;
4733 ComObjPtr<Progress> pProgress;
4734 Medium::Task *pTask = NULL;
4735
4736 try
4737 {
4738 AutoMultiWriteLock2 alock(this, aTarget COMMA_LOCKVAL_SRC_POS);
4739
4740 ComAssertThrow( m->type != MediumType_Writethrough
4741 && m->type != MediumType_Shareable
4742 && m->type != MediumType_Readonly, E_FAIL);
4743 ComAssertThrow(m->state == MediumState_LockedRead, E_FAIL);
4744
4745 if (aTarget->m->state != MediumState_NotCreated)
4746 throw aTarget->i_setStateError();
4747
4748 /* Check that the medium is not attached to the current state of
4749 * any VM referring to it. */
4750 for (BackRefList::const_iterator it = m->backRefs.begin();
4751 it != m->backRefs.end();
4752 ++it)
4753 {
4754 if (it->fInCurState)
4755 {
4756 /* Note: when a VM snapshot is being taken, all normal media
4757 * attached to the VM in the current state will be, as an
4758 * exception, also associated with the snapshot which is about
4759 * to create (see SnapshotMachine::init()) before deassociating
4760 * them from the current state (which takes place only on
4761 * success in Machine::fixupHardDisks()), so that the size of
4762 * snapshotIds will be 1 in this case. The extra condition is
4763 * used to filter out this legal situation. */
4764 if (it->llSnapshotIds.size() == 0)
4765 throw setError(VBOX_E_INVALID_OBJECT_STATE,
4766 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"),
4767 m->strLocationFull.c_str(), it->machineId.raw());
4768
4769 Assert(it->llSnapshotIds.size() == 1);
4770 }
4771 }
4772
4773 if (aProgress != NULL)
4774 {
4775 /* use the existing progress object... */
4776 pProgress = *aProgress;
4777
4778 /* ...but create a new one if it is null */
4779 if (pProgress.isNull())
4780 {
4781 pProgress.createObject();
4782 rc = pProgress->init(m->pVirtualBox,
4783 static_cast<IMedium*>(this),
4784 BstrFmt(tr("Creating differencing medium storage unit '%s'"),
4785 aTarget->m->strLocationFull.c_str()).raw(),
4786 TRUE /* aCancelable */);
4787 if (FAILED(rc))
4788 throw rc;
4789 }
4790 }
4791
4792 /* setup task object to carry out the operation sync/async */
4793 pTask = new Medium::CreateDiffTask(this, pProgress, aTarget, aVariant,
4794 aMediumLockList,
4795 aWait /* fKeepMediumLockList */);
4796 rc = pTask->rc();
4797 AssertComRC(rc);
4798 if (FAILED(rc))
4799 throw rc;
4800
4801 /* register a task (it will deregister itself when done) */
4802 ++m->numCreateDiffTasks;
4803 Assert(m->numCreateDiffTasks != 0); /* overflow? */
4804
4805 aTarget->m->state = MediumState_Creating;
4806 }
4807 catch (HRESULT aRC) { rc = aRC; }
4808
4809 if (SUCCEEDED(rc))
4810 {
4811 if (aWait)
4812 rc = i_runNow(pTask);
4813 else
4814 rc = i_startThread(pTask);
4815
4816 if (SUCCEEDED(rc) && aProgress != NULL)
4817 *aProgress = pProgress;
4818 }
4819 else if (pTask != NULL)
4820 delete pTask;
4821
4822 return rc;
4823}
4824
4825/**
4826 * Returns a preferred format for differencing media.
4827 */
4828Utf8Str Medium::i_getPreferredDiffFormat()
4829{
4830 AutoCaller autoCaller(this);
4831 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
4832
4833 /* check that our own format supports diffs */
4834 if (!(m->formatObj->i_getCapabilities() & MediumFormatCapabilities_Differencing))
4835 {
4836 /* use the default format if not */
4837 Utf8Str tmp;
4838 m->pVirtualBox->i_getDefaultHardDiskFormat(tmp);
4839 return tmp;
4840 }
4841
4842 /* m->strFormat is const, no need to lock */
4843 return m->strFormat;
4844}
4845
4846/**
4847 * Returns a preferred variant for differencing media.
4848 */
4849MediumVariant_T Medium::i_getPreferredDiffVariant()
4850{
4851 AutoCaller autoCaller(this);
4852 AssertComRCReturn(autoCaller.rc(), MediumVariant_Standard);
4853
4854 /* check that our own format supports diffs */
4855 if (!(m->formatObj->i_getCapabilities() & MediumFormatCapabilities_Differencing))
4856 return MediumVariant_Standard;
4857
4858 /* m->variant is const, no need to lock */
4859 ULONG mediumVariantFlags = (ULONG)m->variant;
4860 mediumVariantFlags &= ~MediumVariant_Fixed;
4861 mediumVariantFlags |= MediumVariant_Diff;
4862 return (MediumVariant_T)mediumVariantFlags;
4863}
4864
4865/**
4866 * Implementation for the public Medium::Close() with the exception of calling
4867 * VirtualBox::saveRegistries(), in case someone wants to call this for several
4868 * media.
4869 *
4870 * After this returns with success, uninit() has been called on the medium, and
4871 * the object is no longer usable ("not ready" state).
4872 *
4873 * @param autoCaller AutoCaller instance which must have been created on the caller's
4874 * stack for this medium. This gets released hereupon
4875 * which the Medium instance gets uninitialized.
4876 * @return
4877 */
4878HRESULT Medium::i_close(AutoCaller &autoCaller)
4879{
4880 // must temporarily drop the caller, need the tree lock first
4881 autoCaller.release();
4882
4883 // we're accessing parent/child and backrefs, so lock the tree first, then ourselves
4884 AutoMultiWriteLock2 multilock(&m->pVirtualBox->i_getMediaTreeLockHandle(),
4885 this->lockHandle()
4886 COMMA_LOCKVAL_SRC_POS);
4887
4888 autoCaller.add();
4889 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4890
4891 LogFlowFunc(("ENTER for %s\n", i_getLocationFull().c_str()));
4892
4893 bool wasCreated = true;
4894
4895 switch (m->state)
4896 {
4897 case MediumState_NotCreated:
4898 wasCreated = false;
4899 break;
4900 case MediumState_Created:
4901 case MediumState_Inaccessible:
4902 break;
4903 default:
4904 return i_setStateError();
4905 }
4906
4907 if (m->backRefs.size() != 0)
4908 return setError(VBOX_E_OBJECT_IN_USE,
4909 tr("Medium '%s' cannot be closed because it is still attached to %d virtual machines"),
4910 m->strLocationFull.c_str(), m->backRefs.size());
4911
4912 // perform extra media-dependent close checks
4913 HRESULT rc = i_canClose();
4914 if (FAILED(rc)) return rc;
4915
4916 m->fClosing = true;
4917
4918 if (wasCreated)
4919 {
4920 // remove from the list of known media before performing actual
4921 // uninitialization (to keep the media registry consistent on
4922 // failure to do so)
4923 rc = i_unregisterWithVirtualBox();
4924 if (FAILED(rc)) return rc;
4925
4926 multilock.release();
4927 // Release the AutoCaller now, as otherwise uninit() will simply hang.
4928 // Needs to be done before mark the registries as modified and saving
4929 // the registry, as otherwise there may be a deadlock with someone else
4930 // closing this object while we're in i_saveModifiedRegistries(), which
4931 // needs the media tree lock, which the other thread holds until after
4932 // uninit() below.
4933 autoCaller.release();
4934 i_markRegistriesModified();
4935 m->pVirtualBox->i_saveModifiedRegistries();
4936 }
4937 else
4938 {
4939 multilock.release();
4940 // release the AutoCaller, as otherwise uninit() will simply hang
4941 autoCaller.release();
4942 }
4943
4944 // Keep the locks held until after uninit, as otherwise the consistency
4945 // of the medium tree cannot be guaranteed.
4946 uninit();
4947
4948 LogFlowFuncLeave();
4949
4950 return rc;
4951}
4952
4953/**
4954 * Deletes the medium storage unit.
4955 *
4956 * If @a aProgress is not NULL but the object it points to is @c null then a new
4957 * progress object will be created and assigned to @a *aProgress on success,
4958 * otherwise the existing progress object is used. If Progress is NULL, then no
4959 * progress object is created/used at all.
4960 *
4961 * When @a aWait is @c false, this method will create a thread to perform the
4962 * delete operation asynchronously and will return immediately. Otherwise, it
4963 * will perform the operation on the calling thread and will not return to the
4964 * caller until the operation is completed. Note that @a aProgress cannot be
4965 * NULL when @a aWait is @c false (this method will assert in this case).
4966 *
4967 * @param aProgress Where to find/store a Progress object to track operation
4968 * completion.
4969 * @param aWait @c true if this method should block instead of creating
4970 * an asynchronous thread.
4971 *
4972 * @note Locks mVirtualBox and this object for writing. Locks medium tree for
4973 * writing.
4974 */
4975HRESULT Medium::i_deleteStorage(ComObjPtr<Progress> *aProgress,
4976 bool aWait)
4977{
4978 /** @todo r=klaus The code below needs to be double checked with regard
4979 * to lock order violations, it probably causes lock order issues related
4980 * to the AutoCaller usage. */
4981 AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
4982
4983 AutoCaller autoCaller(this);
4984 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4985
4986 HRESULT rc = S_OK;
4987 ComObjPtr<Progress> pProgress;
4988 Medium::Task *pTask = NULL;
4989
4990 try
4991 {
4992 /* we're accessing the media tree, and canClose() needs it too */
4993 AutoMultiWriteLock2 multilock(&m->pVirtualBox->i_getMediaTreeLockHandle(),
4994 this->lockHandle()
4995 COMMA_LOCKVAL_SRC_POS);
4996 LogFlowThisFunc(("aWait=%RTbool locationFull=%s\n", aWait, i_getLocationFull().c_str() ));
4997
4998 if ( !(m->formatObj->i_getCapabilities() & ( MediumFormatCapabilities_CreateDynamic
4999 | MediumFormatCapabilities_CreateFixed)))
5000 throw setError(VBOX_E_NOT_SUPPORTED,
5001 tr("Medium format '%s' does not support storage deletion"),
5002 m->strFormat.c_str());
5003
5004 /* Wait for a concurrently running Medium::i_queryInfo to complete. */
5005 /** @todo r=klaus would be great if this could be moved to the async
5006 * part of the operation as it can take quite a while */
5007 if (m->queryInfoRunning)
5008 {
5009 while (m->queryInfoRunning)
5010 {
5011 multilock.release();
5012 /* Must not hold the media tree lock or the object lock, as
5013 * Medium::i_queryInfo needs this lock and thus we would run
5014 * into a deadlock here. */
5015 Assert(!m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
5016 Assert(!isWriteLockOnCurrentThread());
5017 {
5018 AutoReadLock qlock(m->queryInfoSem COMMA_LOCKVAL_SRC_POS);
5019 }
5020 multilock.acquire();
5021 }
5022 }
5023
5024 /* Note that we are fine with Inaccessible state too: a) for symmetry
5025 * with create calls and b) because it doesn't really harm to try, if
5026 * it is really inaccessible, the delete operation will fail anyway.
5027 * Accepting Inaccessible state is especially important because all
5028 * registered media are initially Inaccessible upon VBoxSVC startup
5029 * until COMGETTER(RefreshState) is called. Accept Deleting state
5030 * because some callers need to put the medium in this state early
5031 * to prevent races. */
5032 switch (m->state)
5033 {
5034 case MediumState_Created:
5035 case MediumState_Deleting:
5036 case MediumState_Inaccessible:
5037 break;
5038 default:
5039 throw i_setStateError();
5040 }
5041
5042 if (m->backRefs.size() != 0)
5043 {
5044 Utf8Str strMachines;
5045 for (BackRefList::const_iterator it = m->backRefs.begin();
5046 it != m->backRefs.end();
5047 ++it)
5048 {
5049 const BackRef &b = *it;
5050 if (strMachines.length())
5051 strMachines.append(", ");
5052 strMachines.append(b.machineId.toString().c_str());
5053 }
5054#ifdef DEBUG
5055 i_dumpBackRefs();
5056#endif
5057 throw setError(VBOX_E_OBJECT_IN_USE,
5058 tr("Cannot delete storage: medium '%s' is still attached to the following %d virtual machine(s): %s"),
5059 m->strLocationFull.c_str(),
5060 m->backRefs.size(),
5061 strMachines.c_str());
5062 }
5063
5064 rc = i_canClose();
5065 if (FAILED(rc))
5066 throw rc;
5067
5068 /* go to Deleting state, so that the medium is not actually locked */
5069 if (m->state != MediumState_Deleting)
5070 {
5071 rc = i_markForDeletion();
5072 if (FAILED(rc))
5073 throw rc;
5074 }
5075
5076 /* Build the medium lock list. */
5077 MediumLockList *pMediumLockList(new MediumLockList());
5078 multilock.release();
5079 rc = i_createMediumLockList(true /* fFailIfInaccessible */,
5080 this /* pToLockWrite */,
5081 false /* fMediumLockWriteAll */,
5082 NULL,
5083 *pMediumLockList);
5084 multilock.acquire();
5085 if (FAILED(rc))
5086 {
5087 delete pMediumLockList;
5088 throw rc;
5089 }
5090
5091 multilock.release();
5092 rc = pMediumLockList->Lock();
5093 multilock.acquire();
5094 if (FAILED(rc))
5095 {
5096 delete pMediumLockList;
5097 throw setError(rc,
5098 tr("Failed to lock media when deleting '%s'"),
5099 i_getLocationFull().c_str());
5100 }
5101
5102 /* try to remove from the list of known media before performing
5103 * actual deletion (we favor the consistency of the media registry
5104 * which would have been broken if unregisterWithVirtualBox() failed
5105 * after we successfully deleted the storage) */
5106 rc = i_unregisterWithVirtualBox();
5107 if (FAILED(rc))
5108 throw rc;
5109 // no longer need lock
5110 multilock.release();
5111 i_markRegistriesModified();
5112
5113 if (aProgress != NULL)
5114 {
5115 /* use the existing progress object... */
5116 pProgress = *aProgress;
5117
5118 /* ...but create a new one if it is null */
5119 if (pProgress.isNull())
5120 {
5121 pProgress.createObject();
5122 rc = pProgress->init(m->pVirtualBox,
5123 static_cast<IMedium*>(this),
5124 BstrFmt(tr("Deleting medium storage unit '%s'"), m->strLocationFull.c_str()).raw(),
5125 FALSE /* aCancelable */);
5126 if (FAILED(rc))
5127 throw rc;
5128 }
5129 }
5130
5131 /* setup task object to carry out the operation sync/async */
5132 pTask = new Medium::DeleteTask(this, pProgress, pMediumLockList);
5133 rc = pTask->rc();
5134 AssertComRC(rc);
5135 if (FAILED(rc))
5136 throw rc;
5137 }
5138 catch (HRESULT aRC) { rc = aRC; }
5139
5140 if (SUCCEEDED(rc))
5141 {
5142 if (aWait)
5143 rc = i_runNow(pTask);
5144 else
5145 rc = i_startThread(pTask);
5146
5147 if (SUCCEEDED(rc) && aProgress != NULL)
5148 *aProgress = pProgress;
5149
5150 }
5151 else
5152 {
5153 if (pTask)
5154 delete pTask;
5155
5156 /* Undo deleting state if necessary. */
5157 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5158 /* Make sure that any error signalled by unmarkForDeletion() is not
5159 * ending up in the error list (if the caller uses MultiResult). It
5160 * usually is spurious, as in most cases the medium hasn't been marked
5161 * for deletion when the error was thrown above. */
5162 ErrorInfoKeeper eik;
5163 i_unmarkForDeletion();
5164 }
5165
5166 return rc;
5167}
5168
5169/**
5170 * Mark a medium for deletion.
5171 *
5172 * @note Caller must hold the write lock on this medium!
5173 */
5174HRESULT Medium::i_markForDeletion()
5175{
5176 ComAssertRet(isWriteLockOnCurrentThread(), E_FAIL);
5177 switch (m->state)
5178 {
5179 case MediumState_Created:
5180 case MediumState_Inaccessible:
5181 m->preLockState = m->state;
5182 m->state = MediumState_Deleting;
5183 return S_OK;
5184 default:
5185 return i_setStateError();
5186 }
5187}
5188
5189/**
5190 * Removes the "mark for deletion".
5191 *
5192 * @note Caller must hold the write lock on this medium!
5193 */
5194HRESULT Medium::i_unmarkForDeletion()
5195{
5196 ComAssertRet(isWriteLockOnCurrentThread(), E_FAIL);
5197 switch (m->state)
5198 {
5199 case MediumState_Deleting:
5200 m->state = m->preLockState;
5201 return S_OK;
5202 default:
5203 return i_setStateError();
5204 }
5205}
5206
5207/**
5208 * Mark a medium for deletion which is in locked state.
5209 *
5210 * @note Caller must hold the write lock on this medium!
5211 */
5212HRESULT Medium::i_markLockedForDeletion()
5213{
5214 ComAssertRet(isWriteLockOnCurrentThread(), E_FAIL);
5215 if ( ( m->state == MediumState_LockedRead
5216 || m->state == MediumState_LockedWrite)
5217 && m->preLockState == MediumState_Created)
5218 {
5219 m->preLockState = MediumState_Deleting;
5220 return S_OK;
5221 }
5222 else
5223 return i_setStateError();
5224}
5225
5226/**
5227 * Removes the "mark for deletion" for a medium in locked state.
5228 *
5229 * @note Caller must hold the write lock on this medium!
5230 */
5231HRESULT Medium::i_unmarkLockedForDeletion()
5232{
5233 ComAssertRet(isWriteLockOnCurrentThread(), E_FAIL);
5234 if ( ( m->state == MediumState_LockedRead
5235 || m->state == MediumState_LockedWrite)
5236 && m->preLockState == MediumState_Deleting)
5237 {
5238 m->preLockState = MediumState_Created;
5239 return S_OK;
5240 }
5241 else
5242 return i_setStateError();
5243}
5244
5245/**
5246 * Queries the preferred merge direction from this to the other medium, i.e.
5247 * the one which requires the least amount of I/O and therefore time and
5248 * disk consumption.
5249 *
5250 * @returns Status code.
5251 * @retval E_FAIL in case determining the merge direction fails for some reason,
5252 * for example if getting the size of the media fails. There is no
5253 * error set though and the caller is free to continue to find out
5254 * what was going wrong later. Leaves fMergeForward unset.
5255 * @retval VBOX_E_INVALID_OBJECT_STATE if both media are not related to each other
5256 * An error is set.
5257 * @param pOther The other medium to merge with.
5258 * @param fMergeForward Resulting preferred merge direction (out).
5259 */
5260HRESULT Medium::i_queryPreferredMergeDirection(const ComObjPtr<Medium> &pOther,
5261 bool &fMergeForward)
5262{
5263 /** @todo r=klaus The code below needs to be double checked with regard
5264 * to lock order violations, it probably causes lock order issues related
5265 * to the AutoCaller usage. Likewise the code using this method seems
5266 * problematic. */
5267 AssertReturn(pOther != NULL, E_FAIL);
5268 AssertReturn(pOther != this, E_FAIL);
5269
5270 AutoCaller autoCaller(this);
5271 AssertComRCReturnRC(autoCaller.rc());
5272
5273 AutoCaller otherCaller(pOther);
5274 AssertComRCReturnRC(otherCaller.rc());
5275
5276 HRESULT rc = S_OK;
5277 bool fThisParent = false; /**<< Flag whether this medium is the parent of pOther. */
5278
5279 try
5280 {
5281 // locking: we need the tree lock first because we access parent pointers
5282 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
5283
5284 /* more sanity checking and figuring out the current merge direction */
5285 ComObjPtr<Medium> pMedium = i_getParent();
5286 while (!pMedium.isNull() && pMedium != pOther)
5287 pMedium = pMedium->i_getParent();
5288 if (pMedium == pOther)
5289 fThisParent = false;
5290 else
5291 {
5292 pMedium = pOther->i_getParent();
5293 while (!pMedium.isNull() && pMedium != this)
5294 pMedium = pMedium->i_getParent();
5295 if (pMedium == this)
5296 fThisParent = true;
5297 else
5298 {
5299 Utf8Str tgtLoc;
5300 {
5301 AutoReadLock alock(pOther COMMA_LOCKVAL_SRC_POS);
5302 tgtLoc = pOther->i_getLocationFull();
5303 }
5304
5305 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5306 throw setError(VBOX_E_INVALID_OBJECT_STATE,
5307 tr("Media '%s' and '%s' are unrelated"),
5308 m->strLocationFull.c_str(), tgtLoc.c_str());
5309 }
5310 }
5311
5312 /*
5313 * Figure out the preferred merge direction. The current way is to
5314 * get the current sizes of file based images and select the merge
5315 * direction depending on the size.
5316 *
5317 * Can't use the VD API to get current size here as the media might
5318 * be write locked by a running VM. Resort to RTFileQuerySize().
5319 */
5320 int vrc = VINF_SUCCESS;
5321 uint64_t cbMediumThis = 0;
5322 uint64_t cbMediumOther = 0;
5323
5324 if (i_isMediumFormatFile() && pOther->i_isMediumFormatFile())
5325 {
5326 vrc = RTFileQuerySize(this->i_getLocationFull().c_str(), &cbMediumThis);
5327 if (RT_SUCCESS(vrc))
5328 {
5329 vrc = RTFileQuerySize(pOther->i_getLocationFull().c_str(),
5330 &cbMediumOther);
5331 }
5332
5333 if (RT_FAILURE(vrc))
5334 rc = E_FAIL;
5335 else
5336 {
5337 /*
5338 * Check which merge direction might be more optimal.
5339 * This method is not bullet proof of course as there might
5340 * be overlapping blocks in the images so the file size is
5341 * not the best indicator but it is good enough for our purpose
5342 * and everything else is too complicated, especially when the
5343 * media are used by a running VM.
5344 */
5345 bool fMergeIntoThis = cbMediumThis > cbMediumOther;
5346 fMergeForward = fMergeIntoThis ^ fThisParent;
5347 }
5348 }
5349 }
5350 catch (HRESULT aRC) { rc = aRC; }
5351
5352 return rc;
5353}
5354
5355/**
5356 * Prepares this (source) medium, target medium and all intermediate media
5357 * for the merge operation.
5358 *
5359 * This method is to be called prior to calling the #mergeTo() to perform
5360 * necessary consistency checks and place involved media to appropriate
5361 * states. If #mergeTo() is not called or fails, the state modifications
5362 * performed by this method must be undone by #cancelMergeTo().
5363 *
5364 * See #mergeTo() for more information about merging.
5365 *
5366 * @param pTarget Target medium.
5367 * @param aMachineId Allowed machine attachment. NULL means do not check.
5368 * @param aSnapshotId Allowed snapshot attachment. NULL or empty UUID means
5369 * do not check.
5370 * @param fLockMedia Flag whether to lock the medium lock list or not.
5371 * If set to false and the medium lock list locking fails
5372 * later you must call #cancelMergeTo().
5373 * @param fMergeForward Resulting merge direction (out).
5374 * @param pParentForTarget New parent for target medium after merge (out).
5375 * @param aChildrenToReparent Medium lock list containing all children of the
5376 * source which will have to be reparented to the target
5377 * after merge (out).
5378 * @param aMediumLockList Medium locking information (out).
5379 *
5380 * @note Locks medium tree for reading. Locks this object, aTarget and all
5381 * intermediate media for writing.
5382 */
5383HRESULT Medium::i_prepareMergeTo(const ComObjPtr<Medium> &pTarget,
5384 const Guid *aMachineId,
5385 const Guid *aSnapshotId,
5386 bool fLockMedia,
5387 bool &fMergeForward,
5388 ComObjPtr<Medium> &pParentForTarget,
5389 MediumLockList * &aChildrenToReparent,
5390 MediumLockList * &aMediumLockList)
5391{
5392 /** @todo r=klaus The code below needs to be double checked with regard
5393 * to lock order violations, it probably causes lock order issues related
5394 * to the AutoCaller usage. Likewise the code using this method seems
5395 * problematic. */
5396 AssertReturn(pTarget != NULL, E_FAIL);
5397 AssertReturn(pTarget != this, E_FAIL);
5398
5399 AutoCaller autoCaller(this);
5400 AssertComRCReturnRC(autoCaller.rc());
5401
5402 AutoCaller targetCaller(pTarget);
5403 AssertComRCReturnRC(targetCaller.rc());
5404
5405 HRESULT rc = S_OK;
5406 fMergeForward = false;
5407 pParentForTarget.setNull();
5408 Assert(aChildrenToReparent == NULL);
5409 aChildrenToReparent = NULL;
5410 Assert(aMediumLockList == NULL);
5411 aMediumLockList = NULL;
5412
5413 try
5414 {
5415 // locking: we need the tree lock first because we access parent pointers
5416 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
5417
5418 /* more sanity checking and figuring out the merge direction */
5419 ComObjPtr<Medium> pMedium = i_getParent();
5420 while (!pMedium.isNull() && pMedium != pTarget)
5421 pMedium = pMedium->i_getParent();
5422 if (pMedium == pTarget)
5423 fMergeForward = false;
5424 else
5425 {
5426 pMedium = pTarget->i_getParent();
5427 while (!pMedium.isNull() && pMedium != this)
5428 pMedium = pMedium->i_getParent();
5429 if (pMedium == this)
5430 fMergeForward = true;
5431 else
5432 {
5433 Utf8Str tgtLoc;
5434 {
5435 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
5436 tgtLoc = pTarget->i_getLocationFull();
5437 }
5438
5439 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5440 throw setError(VBOX_E_INVALID_OBJECT_STATE,
5441 tr("Media '%s' and '%s' are unrelated"),
5442 m->strLocationFull.c_str(), tgtLoc.c_str());
5443 }
5444 }
5445
5446 /* Build the lock list. */
5447 aMediumLockList = new MediumLockList();
5448 treeLock.release();
5449 if (fMergeForward)
5450 rc = pTarget->i_createMediumLockList(true /* fFailIfInaccessible */,
5451 pTarget /* pToLockWrite */,
5452 false /* fMediumLockWriteAll */,
5453 NULL,
5454 *aMediumLockList);
5455 else
5456 rc = i_createMediumLockList(true /* fFailIfInaccessible */,
5457 pTarget /* pToLockWrite */,
5458 false /* fMediumLockWriteAll */,
5459 NULL,
5460 *aMediumLockList);
5461 treeLock.acquire();
5462 if (FAILED(rc))
5463 throw rc;
5464
5465 /* Sanity checking, must be after lock list creation as it depends on
5466 * valid medium states. The medium objects must be accessible. Only
5467 * do this if immediate locking is requested, otherwise it fails when
5468 * we construct a medium lock list for an already running VM. Snapshot
5469 * deletion uses this to simplify its life. */
5470 if (fLockMedia)
5471 {
5472 {
5473 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5474 if (m->state != MediumState_Created)
5475 throw i_setStateError();
5476 }
5477 {
5478 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
5479 if (pTarget->m->state != MediumState_Created)
5480 throw pTarget->i_setStateError();
5481 }
5482 }
5483
5484 /* check medium attachment and other sanity conditions */
5485 if (fMergeForward)
5486 {
5487 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5488 if (i_getChildren().size() > 1)
5489 {
5490 throw setError(VBOX_E_INVALID_OBJECT_STATE,
5491 tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
5492 m->strLocationFull.c_str(), i_getChildren().size());
5493 }
5494 /* One backreference is only allowed if the machine ID is not empty
5495 * and it matches the machine the medium is attached to (including
5496 * the snapshot ID if not empty). */
5497 if ( m->backRefs.size() != 0
5498 && ( !aMachineId
5499 || m->backRefs.size() != 1
5500 || aMachineId->isZero()
5501 || *i_getFirstMachineBackrefId() != *aMachineId
5502 || ( (!aSnapshotId || !aSnapshotId->isZero())
5503 && *i_getFirstMachineBackrefSnapshotId() != *aSnapshotId)))
5504 throw setError(VBOX_E_OBJECT_IN_USE,
5505 tr("Medium '%s' is attached to %d virtual machines"),
5506 m->strLocationFull.c_str(), m->backRefs.size());
5507 if (m->type == MediumType_Immutable)
5508 throw setError(VBOX_E_INVALID_OBJECT_STATE,
5509 tr("Medium '%s' is immutable"),
5510 m->strLocationFull.c_str());
5511 if (m->type == MediumType_MultiAttach)
5512 throw setError(VBOX_E_INVALID_OBJECT_STATE,
5513 tr("Medium '%s' is multi-attach"),
5514 m->strLocationFull.c_str());
5515 }
5516 else
5517 {
5518 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
5519 if (pTarget->i_getChildren().size() > 1)
5520 {
5521 throw setError(VBOX_E_OBJECT_IN_USE,
5522 tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
5523 pTarget->m->strLocationFull.c_str(),
5524 pTarget->i_getChildren().size());
5525 }
5526 if (pTarget->m->type == MediumType_Immutable)
5527 throw setError(VBOX_E_INVALID_OBJECT_STATE,
5528 tr("Medium '%s' is immutable"),
5529 pTarget->m->strLocationFull.c_str());
5530 if (pTarget->m->type == MediumType_MultiAttach)
5531 throw setError(VBOX_E_INVALID_OBJECT_STATE,
5532 tr("Medium '%s' is multi-attach"),
5533 pTarget->m->strLocationFull.c_str());
5534 }
5535 ComObjPtr<Medium> pLast(fMergeForward ? (Medium *)pTarget : this);
5536 ComObjPtr<Medium> pLastIntermediate = pLast->i_getParent();
5537 for (pLast = pLastIntermediate;
5538 !pLast.isNull() && pLast != pTarget && pLast != this;
5539 pLast = pLast->i_getParent())
5540 {
5541 AutoReadLock alock(pLast COMMA_LOCKVAL_SRC_POS);
5542 if (pLast->i_getChildren().size() > 1)
5543 {
5544 throw setError(VBOX_E_OBJECT_IN_USE,
5545 tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
5546 pLast->m->strLocationFull.c_str(),
5547 pLast->i_getChildren().size());
5548 }
5549 if (pLast->m->backRefs.size() != 0)
5550 throw setError(VBOX_E_OBJECT_IN_USE,
5551 tr("Medium '%s' is attached to %d virtual machines"),
5552 pLast->m->strLocationFull.c_str(),
5553 pLast->m->backRefs.size());
5554
5555 }
5556
5557 /* Update medium states appropriately */
5558 {
5559 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5560
5561 if (m->state == MediumState_Created)
5562 {
5563 rc = i_markForDeletion();
5564 if (FAILED(rc))
5565 throw rc;
5566 }
5567 else
5568 {
5569 if (fLockMedia)
5570 throw i_setStateError();
5571 else if ( m->state == MediumState_LockedWrite
5572 || m->state == MediumState_LockedRead)
5573 {
5574 /* Either mark it for deletion in locked state or allow
5575 * others to have done so. */
5576 if (m->preLockState == MediumState_Created)
5577 i_markLockedForDeletion();
5578 else if (m->preLockState != MediumState_Deleting)
5579 throw i_setStateError();
5580 }
5581 else
5582 throw i_setStateError();
5583 }
5584 }
5585
5586 if (fMergeForward)
5587 {
5588 /* we will need parent to reparent target */
5589 pParentForTarget = i_getParent();
5590 }
5591 else
5592 {
5593 /* we will need to reparent children of the source */
5594 aChildrenToReparent = new MediumLockList();
5595 for (MediaList::const_iterator it = i_getChildren().begin();
5596 it != i_getChildren().end();
5597 ++it)
5598 {
5599 pMedium = *it;
5600 aChildrenToReparent->Append(pMedium, true /* fLockWrite */);
5601 }
5602 if (fLockMedia && aChildrenToReparent)
5603 {
5604 treeLock.release();
5605 rc = aChildrenToReparent->Lock();
5606 treeLock.acquire();
5607 if (FAILED(rc))
5608 throw rc;
5609 }
5610 }
5611 for (pLast = pLastIntermediate;
5612 !pLast.isNull() && pLast != pTarget && pLast != this;
5613 pLast = pLast->i_getParent())
5614 {
5615 AutoWriteLock alock(pLast COMMA_LOCKVAL_SRC_POS);
5616 if (pLast->m->state == MediumState_Created)
5617 {
5618 rc = pLast->i_markForDeletion();
5619 if (FAILED(rc))
5620 throw rc;
5621 }
5622 else
5623 throw pLast->i_setStateError();
5624 }
5625
5626 /* Tweak the lock list in the backward merge case, as the target
5627 * isn't marked to be locked for writing yet. */
5628 if (!fMergeForward)
5629 {
5630 MediumLockList::Base::iterator lockListBegin =
5631 aMediumLockList->GetBegin();
5632 MediumLockList::Base::iterator lockListEnd =
5633 aMediumLockList->GetEnd();
5634 ++lockListEnd;
5635 for (MediumLockList::Base::iterator it = lockListBegin;
5636 it != lockListEnd;
5637 ++it)
5638 {
5639 MediumLock &mediumLock = *it;
5640 if (mediumLock.GetMedium() == pTarget)
5641 {
5642 HRESULT rc2 = mediumLock.UpdateLock(true);
5643 AssertComRC(rc2);
5644 break;
5645 }
5646 }
5647 }
5648
5649 if (fLockMedia)
5650 {
5651 treeLock.release();
5652 rc = aMediumLockList->Lock();
5653 treeLock.acquire();
5654 if (FAILED(rc))
5655 {
5656 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
5657 throw setError(rc,
5658 tr("Failed to lock media when merging to '%s'"),
5659 pTarget->i_getLocationFull().c_str());
5660 }
5661 }
5662 }
5663 catch (HRESULT aRC) { rc = aRC; }
5664
5665 if (FAILED(rc))
5666 {
5667 if (aMediumLockList)
5668 {
5669 delete aMediumLockList;
5670 aMediumLockList = NULL;
5671 }
5672 if (aChildrenToReparent)
5673 {
5674 delete aChildrenToReparent;
5675 aChildrenToReparent = NULL;
5676 }
5677 }
5678
5679 return rc;
5680}
5681
5682/**
5683 * Merges this medium to the specified medium which must be either its
5684 * direct ancestor or descendant.
5685 *
5686 * Given this medium is SOURCE and the specified medium is TARGET, we will
5687 * get two variants of the merge operation:
5688 *
5689 * forward merge
5690 * ------------------------->
5691 * [Extra] <- SOURCE <- Intermediate <- TARGET
5692 * Any Del Del LockWr
5693 *
5694 *
5695 * backward merge
5696 * <-------------------------
5697 * TARGET <- Intermediate <- SOURCE <- [Extra]
5698 * LockWr Del Del LockWr
5699 *
5700 * Each diagram shows the involved media on the media chain where
5701 * SOURCE and TARGET belong. Under each medium there is a state value which
5702 * the medium must have at a time of the mergeTo() call.
5703 *
5704 * The media in the square braces may be absent (e.g. when the forward
5705 * operation takes place and SOURCE is the base medium, or when the backward
5706 * merge operation takes place and TARGET is the last child in the chain) but if
5707 * they present they are involved too as shown.
5708 *
5709 * Neither the source medium nor intermediate media may be attached to
5710 * any VM directly or in the snapshot, otherwise this method will assert.
5711 *
5712 * The #prepareMergeTo() method must be called prior to this method to place all
5713 * involved to necessary states and perform other consistency checks.
5714 *
5715 * If @a aWait is @c true then this method will perform the operation on the
5716 * calling thread and will not return to the caller until the operation is
5717 * completed. When this method succeeds, all intermediate medium objects in
5718 * the chain will be uninitialized, the state of the target medium (and all
5719 * involved extra media) will be restored. @a aMediumLockList will not be
5720 * deleted, whether the operation is successful or not. The caller has to do
5721 * this if appropriate. Note that this (source) medium is not uninitialized
5722 * because of possible AutoCaller instances held by the caller of this method
5723 * on the current thread. It's therefore the responsibility of the caller to
5724 * call Medium::uninit() after releasing all callers.
5725 *
5726 * If @a aWait is @c false then this method will create a thread to perform the
5727 * operation asynchronously and will return immediately. If the operation
5728 * succeeds, the thread will uninitialize the source medium object and all
5729 * intermediate medium objects in the chain, reset the state of the target
5730 * medium (and all involved extra media) and delete @a aMediumLockList.
5731 * If the operation fails, the thread will only reset the states of all
5732 * involved media and delete @a aMediumLockList.
5733 *
5734 * When this method fails (regardless of the @a aWait mode), it is a caller's
5735 * responsibility to undo state changes and delete @a aMediumLockList using
5736 * #cancelMergeTo().
5737 *
5738 * If @a aProgress is not NULL but the object it points to is @c null then a new
5739 * progress object will be created and assigned to @a *aProgress on success,
5740 * otherwise the existing progress object is used. If Progress is NULL, then no
5741 * progress object is created/used at all. Note that @a aProgress cannot be
5742 * NULL when @a aWait is @c false (this method will assert in this case).
5743 *
5744 * @param pTarget Target medium.
5745 * @param fMergeForward Merge direction.
5746 * @param pParentForTarget New parent for target medium after merge.
5747 * @param aChildrenToReparent List of children of the source which will have
5748 * to be reparented to the target after merge.
5749 * @param aMediumLockList Medium locking information.
5750 * @param aProgress Where to find/store a Progress object to track operation
5751 * completion.
5752 * @param aWait @c true if this method should block instead of creating
5753 * an asynchronous thread.
5754 *
5755 * @note Locks the tree lock for writing. Locks the media from the chain
5756 * for writing.
5757 */
5758HRESULT Medium::i_mergeTo(const ComObjPtr<Medium> &pTarget,
5759 bool fMergeForward,
5760 const ComObjPtr<Medium> &pParentForTarget,
5761 MediumLockList *aChildrenToReparent,
5762 MediumLockList *aMediumLockList,
5763 ComObjPtr<Progress> *aProgress,
5764 bool aWait)
5765{
5766 AssertReturn(pTarget != NULL, E_FAIL);
5767 AssertReturn(pTarget != this, E_FAIL);
5768 AssertReturn(aMediumLockList != NULL, E_FAIL);
5769 AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
5770
5771 AutoCaller autoCaller(this);
5772 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5773
5774 AutoCaller targetCaller(pTarget);
5775 AssertComRCReturnRC(targetCaller.rc());
5776
5777 HRESULT rc = S_OK;
5778 ComObjPtr<Progress> pProgress;
5779 Medium::Task *pTask = NULL;
5780
5781 try
5782 {
5783 if (aProgress != NULL)
5784 {
5785 /* use the existing progress object... */
5786 pProgress = *aProgress;
5787
5788 /* ...but create a new one if it is null */
5789 if (pProgress.isNull())
5790 {
5791 Utf8Str tgtName;
5792 {
5793 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
5794 tgtName = pTarget->i_getName();
5795 }
5796
5797 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5798
5799 pProgress.createObject();
5800 rc = pProgress->init(m->pVirtualBox,
5801 static_cast<IMedium*>(this),
5802 BstrFmt(tr("Merging medium '%s' to '%s'"),
5803 i_getName().c_str(),
5804 tgtName.c_str()).raw(),
5805 TRUE /* aCancelable */);
5806 if (FAILED(rc))
5807 throw rc;
5808 }
5809 }
5810
5811 /* setup task object to carry out the operation sync/async */
5812 pTask = new Medium::MergeTask(this, pTarget, fMergeForward,
5813 pParentForTarget, aChildrenToReparent,
5814 pProgress, aMediumLockList,
5815 aWait /* fKeepMediumLockList */);
5816 rc = pTask->rc();
5817 AssertComRC(rc);
5818 if (FAILED(rc))
5819 throw rc;
5820 }
5821 catch (HRESULT aRC) { rc = aRC; }
5822
5823 if (SUCCEEDED(rc))
5824 {
5825 if (aWait)
5826 rc = i_runNow(pTask);
5827 else
5828 rc = i_startThread(pTask);
5829
5830 if (SUCCEEDED(rc) && aProgress != NULL)
5831 *aProgress = pProgress;
5832 }
5833 else if (pTask != NULL)
5834 delete pTask;
5835
5836 return rc;
5837}
5838
5839/**
5840 * Undoes what #prepareMergeTo() did. Must be called if #mergeTo() is not
5841 * called or fails. Frees memory occupied by @a aMediumLockList and unlocks
5842 * the medium objects in @a aChildrenToReparent.
5843 *
5844 * @param aChildrenToReparent List of children of the source which will have
5845 * to be reparented to the target after merge.
5846 * @param aMediumLockList Medium locking information.
5847 *
5848 * @note Locks the media from the chain for writing.
5849 */
5850void Medium::i_cancelMergeTo(MediumLockList *aChildrenToReparent,
5851 MediumLockList *aMediumLockList)
5852{
5853 AutoCaller autoCaller(this);
5854 AssertComRCReturnVoid(autoCaller.rc());
5855
5856 AssertReturnVoid(aMediumLockList != NULL);
5857
5858 /* Revert media marked for deletion to previous state. */
5859 HRESULT rc;
5860 MediumLockList::Base::const_iterator mediumListBegin =
5861 aMediumLockList->GetBegin();
5862 MediumLockList::Base::const_iterator mediumListEnd =
5863 aMediumLockList->GetEnd();
5864 for (MediumLockList::Base::const_iterator it = mediumListBegin;
5865 it != mediumListEnd;
5866 ++it)
5867 {
5868 const MediumLock &mediumLock = *it;
5869 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
5870 AutoWriteLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
5871
5872 if (pMedium->m->state == MediumState_Deleting)
5873 {
5874 rc = pMedium->i_unmarkForDeletion();
5875 AssertComRC(rc);
5876 }
5877 else if ( ( pMedium->m->state == MediumState_LockedWrite
5878 || pMedium->m->state == MediumState_LockedRead)
5879 && pMedium->m->preLockState == MediumState_Deleting)
5880 {
5881 rc = pMedium->i_unmarkLockedForDeletion();
5882 AssertComRC(rc);
5883 }
5884 }
5885
5886 /* the destructor will do the work */
5887 delete aMediumLockList;
5888
5889 /* unlock the children which had to be reparented, the destructor will do
5890 * the work */
5891 if (aChildrenToReparent)
5892 delete aChildrenToReparent;
5893}
5894
5895/**
5896 * Fix the parent UUID of all children to point to this medium as their
5897 * parent.
5898 */
5899HRESULT Medium::i_fixParentUuidOfChildren(MediumLockList *pChildrenToReparent)
5900{
5901 /** @todo r=klaus The code below needs to be double checked with regard
5902 * to lock order violations, it probably causes lock order issues related
5903 * to the AutoCaller usage. Likewise the code using this method seems
5904 * problematic. */
5905 Assert(!isWriteLockOnCurrentThread());
5906 Assert(!m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
5907 MediumLockList mediumLockList;
5908 HRESULT rc = i_createMediumLockList(true /* fFailIfInaccessible */,
5909 NULL /* pToLockWrite */,
5910 false /* fMediumLockWriteAll */,
5911 this,
5912 mediumLockList);
5913 AssertComRCReturnRC(rc);
5914
5915 try
5916 {
5917 PVBOXHDD hdd;
5918 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
5919 ComAssertRCThrow(vrc, E_FAIL);
5920
5921 try
5922 {
5923 MediumLockList::Base::iterator lockListBegin =
5924 mediumLockList.GetBegin();
5925 MediumLockList::Base::iterator lockListEnd =
5926 mediumLockList.GetEnd();
5927 for (MediumLockList::Base::iterator it = lockListBegin;
5928 it != lockListEnd;
5929 ++it)
5930 {
5931 MediumLock &mediumLock = *it;
5932 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
5933 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
5934
5935 // open the medium
5936 vrc = VDOpen(hdd,
5937 pMedium->m->strFormat.c_str(),
5938 pMedium->m->strLocationFull.c_str(),
5939 VD_OPEN_FLAGS_READONLY | m->uOpenFlagsDef,
5940 pMedium->m->vdImageIfaces);
5941 if (RT_FAILURE(vrc))
5942 throw vrc;
5943 }
5944
5945 MediumLockList::Base::iterator childrenBegin = pChildrenToReparent->GetBegin();
5946 MediumLockList::Base::iterator childrenEnd = pChildrenToReparent->GetEnd();
5947 for (MediumLockList::Base::iterator it = childrenBegin;
5948 it != childrenEnd;
5949 ++it)
5950 {
5951 Medium *pMedium = it->GetMedium();
5952 /* VD_OPEN_FLAGS_INFO since UUID is wrong yet */
5953 vrc = VDOpen(hdd,
5954 pMedium->m->strFormat.c_str(),
5955 pMedium->m->strLocationFull.c_str(),
5956 VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
5957 pMedium->m->vdImageIfaces);
5958 if (RT_FAILURE(vrc))
5959 throw vrc;
5960
5961 vrc = VDSetParentUuid(hdd, VD_LAST_IMAGE, m->id.raw());
5962 if (RT_FAILURE(vrc))
5963 throw vrc;
5964
5965 vrc = VDClose(hdd, false /* fDelete */);
5966 if (RT_FAILURE(vrc))
5967 throw vrc;
5968 }
5969 }
5970 catch (HRESULT aRC) { rc = aRC; }
5971 catch (int aVRC)
5972 {
5973 rc = setError(E_FAIL,
5974 tr("Could not update medium UUID references to parent '%s' (%s)"),
5975 m->strLocationFull.c_str(),
5976 i_vdError(aVRC).c_str());
5977 }
5978
5979 VDDestroy(hdd);
5980 }
5981 catch (HRESULT aRC) { rc = aRC; }
5982
5983 return rc;
5984}
5985
5986/**
5987 * Used by IAppliance to export disk images.
5988 *
5989 * @param aFilename Filename to create (UTF8).
5990 * @param aFormat Medium format for creating @a aFilename.
5991 * @param aVariant Which exact image format variant to use
5992 * for the destination image.
5993 * @param pKeyStore The optional key store for decrypting the data
5994 * for encrypted media during the export.
5995 * @param aVDImageIOCallbacks Pointer to the callback table for a
5996 * VDINTERFACEIO interface. May be NULL.
5997 * @param aVDImageIOUser Opaque data for the callbacks.
5998 * @param aProgress Progress object to use.
5999 * @return
6000 * @note The source format is defined by the Medium instance.
6001 */
6002HRESULT Medium::i_exportFile(const char *aFilename,
6003 const ComObjPtr<MediumFormat> &aFormat,
6004 MediumVariant_T aVariant,
6005 SecretKeyStore *pKeyStore,
6006 PVDINTERFACEIO aVDImageIOIf, void *aVDImageIOUser,
6007 const ComObjPtr<Progress> &aProgress)
6008{
6009 AssertPtrReturn(aFilename, E_INVALIDARG);
6010 AssertReturn(!aFormat.isNull(), E_INVALIDARG);
6011 AssertReturn(!aProgress.isNull(), E_INVALIDARG);
6012
6013 AutoCaller autoCaller(this);
6014 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6015
6016 HRESULT rc = S_OK;
6017 Medium::Task *pTask = NULL;
6018
6019 try
6020 {
6021 // This needs no extra locks besides what is done in the called methods.
6022
6023 /* Build the source lock list. */
6024 MediumLockList *pSourceMediumLockList(new MediumLockList());
6025 rc = i_createMediumLockList(true /* fFailIfInaccessible */,
6026 NULL /* pToLockWrite */,
6027 false /* fMediumLockWriteAll */,
6028 NULL,
6029 *pSourceMediumLockList);
6030 if (FAILED(rc))
6031 {
6032 delete pSourceMediumLockList;
6033 throw rc;
6034 }
6035
6036 rc = pSourceMediumLockList->Lock();
6037 if (FAILED(rc))
6038 {
6039 delete pSourceMediumLockList;
6040 throw setError(rc,
6041 tr("Failed to lock source media '%s'"),
6042 i_getLocationFull().c_str());
6043 }
6044
6045 /* setup task object to carry out the operation asynchronously */
6046 pTask = new Medium::ExportTask(this, aProgress, aFilename, aFormat,
6047 aVariant, pKeyStore, aVDImageIOIf,
6048 aVDImageIOUser, pSourceMediumLockList);
6049 rc = pTask->rc();
6050 AssertComRC(rc);
6051 if (FAILED(rc))
6052 throw rc;
6053 }
6054 catch (HRESULT aRC) { rc = aRC; }
6055
6056 if (SUCCEEDED(rc))
6057 rc = i_startThread(pTask);
6058 else if (pTask != NULL)
6059 delete pTask;
6060
6061 return rc;
6062}
6063
6064/**
6065 * Used by IAppliance to import disk images.
6066 *
6067 * @param aFilename Filename to read (UTF8).
6068 * @param aFormat Medium format for reading @a aFilename.
6069 * @param aVariant Which exact image format variant to use
6070 * for the destination image.
6071 * @param aVfsIosSrc Handle to the source I/O stream.
6072 * @param aParent Parent medium. May be NULL.
6073 * @param aProgress Progress object to use.
6074 * @return
6075 * @note The destination format is defined by the Medium instance.
6076 */
6077HRESULT Medium::i_importFile(const char *aFilename,
6078 const ComObjPtr<MediumFormat> &aFormat,
6079 MediumVariant_T aVariant,
6080 RTVFSIOSTREAM aVfsIosSrc,
6081 const ComObjPtr<Medium> &aParent,
6082 const ComObjPtr<Progress> &aProgress)
6083{
6084 /** @todo r=klaus The code below needs to be double checked with regard
6085 * to lock order violations, it probably causes lock order issues related
6086 * to the AutoCaller usage. */
6087 AssertPtrReturn(aFilename, E_INVALIDARG);
6088 AssertReturn(!aFormat.isNull(), E_INVALIDARG);
6089 AssertReturn(!aProgress.isNull(), E_INVALIDARG);
6090
6091 AutoCaller autoCaller(this);
6092 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6093
6094 HRESULT rc = S_OK;
6095 Medium::Task *pTask = NULL;
6096
6097 try
6098 {
6099 // locking: we need the tree lock first because we access parent pointers
6100 // and we need to write-lock the media involved
6101 uint32_t cHandles = 2;
6102 LockHandle* pHandles[3] = { &m->pVirtualBox->i_getMediaTreeLockHandle(),
6103 this->lockHandle() };
6104 /* Only add parent to the lock if it is not null */
6105 if (!aParent.isNull())
6106 pHandles[cHandles++] = aParent->lockHandle();
6107 AutoWriteLock alock(cHandles,
6108 pHandles
6109 COMMA_LOCKVAL_SRC_POS);
6110
6111 if ( m->state != MediumState_NotCreated
6112 && m->state != MediumState_Created)
6113 throw i_setStateError();
6114
6115 /* Build the target lock list. */
6116 MediumLockList *pTargetMediumLockList(new MediumLockList());
6117 alock.release();
6118 rc = i_createMediumLockList(true /* fFailIfInaccessible */,
6119 this /* pToLockWrite */,
6120 false /* fMediumLockWriteAll */,
6121 aParent,
6122 *pTargetMediumLockList);
6123 alock.acquire();
6124 if (FAILED(rc))
6125 {
6126 delete pTargetMediumLockList;
6127 throw rc;
6128 }
6129
6130 alock.release();
6131 rc = pTargetMediumLockList->Lock();
6132 alock.acquire();
6133 if (FAILED(rc))
6134 {
6135 delete pTargetMediumLockList;
6136 throw setError(rc,
6137 tr("Failed to lock target media '%s'"),
6138 i_getLocationFull().c_str());
6139 }
6140
6141 /* setup task object to carry out the operation asynchronously */
6142 pTask = new Medium::ImportTask(this, aProgress, aFilename, aFormat, aVariant,
6143 aVfsIosSrc, aParent, pTargetMediumLockList);
6144 rc = pTask->rc();
6145 AssertComRC(rc);
6146 if (FAILED(rc))
6147 throw rc;
6148
6149 if (m->state == MediumState_NotCreated)
6150 m->state = MediumState_Creating;
6151 }
6152 catch (HRESULT aRC) { rc = aRC; }
6153
6154 if (SUCCEEDED(rc))
6155 rc = i_startThread(pTask);
6156 else if (pTask != NULL)
6157 delete pTask;
6158
6159 return rc;
6160}
6161
6162/**
6163 * Internal version of the public CloneTo API which allows to enable certain
6164 * optimizations to improve speed during VM cloning.
6165 *
6166 * @param aTarget Target medium
6167 * @param aVariant Which exact image format variant to use
6168 * for the destination image.
6169 * @param aParent Parent medium. May be NULL.
6170 * @param aProgress Progress object to use.
6171 * @param idxSrcImageSame The last image in the source chain which has the
6172 * same content as the given image in the destination
6173 * chain. Use UINT32_MAX to disable this optimization.
6174 * @param idxDstImageSame The last image in the destination chain which has the
6175 * same content as the given image in the source chain.
6176 * Use UINT32_MAX to disable this optimization.
6177 * @return
6178 */
6179HRESULT Medium::i_cloneToEx(const ComObjPtr<Medium> &aTarget, ULONG aVariant,
6180 const ComObjPtr<Medium> &aParent, IProgress **aProgress,
6181 uint32_t idxSrcImageSame, uint32_t idxDstImageSame)
6182{
6183 /** @todo r=klaus The code below needs to be double checked with regard
6184 * to lock order violations, it probably causes lock order issues related
6185 * to the AutoCaller usage. */
6186 CheckComArgNotNull(aTarget);
6187 CheckComArgOutPointerValid(aProgress);
6188 ComAssertRet(aTarget != this, E_INVALIDARG);
6189
6190 AutoCaller autoCaller(this);
6191 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6192
6193 HRESULT rc = S_OK;
6194 ComObjPtr<Progress> pProgress;
6195 Medium::Task *pTask = NULL;
6196
6197 try
6198 {
6199 // locking: we need the tree lock first because we access parent pointers
6200 // and we need to write-lock the media involved
6201 uint32_t cHandles = 3;
6202 LockHandle* pHandles[4] = { &m->pVirtualBox->i_getMediaTreeLockHandle(),
6203 this->lockHandle(),
6204 aTarget->lockHandle() };
6205 /* Only add parent to the lock if it is not null */
6206 if (!aParent.isNull())
6207 pHandles[cHandles++] = aParent->lockHandle();
6208 AutoWriteLock alock(cHandles,
6209 pHandles
6210 COMMA_LOCKVAL_SRC_POS);
6211
6212 if ( aTarget->m->state != MediumState_NotCreated
6213 && aTarget->m->state != MediumState_Created)
6214 throw aTarget->i_setStateError();
6215
6216 /* Build the source lock list. */
6217 MediumLockList *pSourceMediumLockList(new MediumLockList());
6218 alock.release();
6219 rc = i_createMediumLockList(true /* fFailIfInaccessible */,
6220 NULL /* pToLockWrite */,
6221 false /* fMediumLockWriteAll */,
6222 NULL,
6223 *pSourceMediumLockList);
6224 alock.acquire();
6225 if (FAILED(rc))
6226 {
6227 delete pSourceMediumLockList;
6228 throw rc;
6229 }
6230
6231 /* Build the target lock list (including the to-be parent chain). */
6232 MediumLockList *pTargetMediumLockList(new MediumLockList());
6233 alock.release();
6234 rc = aTarget->i_createMediumLockList(true /* fFailIfInaccessible */,
6235 aTarget /* pToLockWrite */,
6236 false /* fMediumLockWriteAll */,
6237 aParent,
6238 *pTargetMediumLockList);
6239 alock.acquire();
6240 if (FAILED(rc))
6241 {
6242 delete pSourceMediumLockList;
6243 delete pTargetMediumLockList;
6244 throw rc;
6245 }
6246
6247 alock.release();
6248 rc = pSourceMediumLockList->Lock();
6249 alock.acquire();
6250 if (FAILED(rc))
6251 {
6252 delete pSourceMediumLockList;
6253 delete pTargetMediumLockList;
6254 throw setError(rc,
6255 tr("Failed to lock source media '%s'"),
6256 i_getLocationFull().c_str());
6257 }
6258 alock.release();
6259 rc = pTargetMediumLockList->Lock();
6260 alock.acquire();
6261 if (FAILED(rc))
6262 {
6263 delete pSourceMediumLockList;
6264 delete pTargetMediumLockList;
6265 throw setError(rc,
6266 tr("Failed to lock target media '%s'"),
6267 aTarget->i_getLocationFull().c_str());
6268 }
6269
6270 pProgress.createObject();
6271 rc = pProgress->init(m->pVirtualBox,
6272 static_cast <IMedium *>(this),
6273 BstrFmt(tr("Creating clone medium '%s'"), aTarget->m->strLocationFull.c_str()).raw(),
6274 TRUE /* aCancelable */);
6275 if (FAILED(rc))
6276 {
6277 delete pSourceMediumLockList;
6278 delete pTargetMediumLockList;
6279 throw rc;
6280 }
6281
6282 /* setup task object to carry out the operation asynchronously */
6283 pTask = new Medium::CloneTask(this, pProgress, aTarget,
6284 (MediumVariant_T)aVariant,
6285 aParent, idxSrcImageSame,
6286 idxDstImageSame, pSourceMediumLockList,
6287 pTargetMediumLockList);
6288 rc = pTask->rc();
6289 AssertComRC(rc);
6290 if (FAILED(rc))
6291 throw rc;
6292
6293 if (aTarget->m->state == MediumState_NotCreated)
6294 aTarget->m->state = MediumState_Creating;
6295 }
6296 catch (HRESULT aRC) { rc = aRC; }
6297
6298 if (SUCCEEDED(rc))
6299 {
6300 rc = i_startThread(pTask);
6301
6302 if (SUCCEEDED(rc))
6303 pProgress.queryInterfaceTo(aProgress);
6304 }
6305 else if (pTask != NULL)
6306 delete pTask;
6307
6308 return rc;
6309}
6310
6311/**
6312 * Returns the key identifier for this medium if encryption is configured.
6313 *
6314 * @returns Key identifier or empty string if no encryption is configured.
6315 */
6316const Utf8Str& Medium::i_getKeyId()
6317{
6318 ComObjPtr<Medium> pBase = i_getBase();
6319
6320 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6321
6322 settings::StringsMap::const_iterator it = pBase->m->mapProperties.find("CRYPT/KeyId");
6323 if (it == pBase->m->mapProperties.end())
6324 return Utf8Str::Empty;
6325
6326 return it->second;
6327}
6328
6329/**
6330 * Returns all filter related properties.
6331 *
6332 * @returns COM status code.
6333 * @param aReturnNames Where to store the properties names on success.
6334 * @param aReturnValues Where to store the properties values on success.
6335 */
6336HRESULT Medium::i_getFilterProperties(std::vector<com::Utf8Str> &aReturnNames,
6337 std::vector<com::Utf8Str> &aReturnValues)
6338{
6339 std::vector<com::Utf8Str> aPropNames;
6340 std::vector<com::Utf8Str> aPropValues;
6341 HRESULT hrc = getProperties(Utf8Str(""), aPropNames, aPropValues);
6342
6343 if (SUCCEEDED(hrc))
6344 {
6345 unsigned cReturnSize = 0;
6346 aReturnNames.resize(0);
6347 aReturnValues.resize(0);
6348 for (unsigned idx = 0; idx < aPropNames.size(); idx++)
6349 {
6350 if (i_isPropertyForFilter(aPropNames[idx]))
6351 {
6352 aReturnNames.resize(cReturnSize + 1);
6353 aReturnValues.resize(cReturnSize + 1);
6354 aReturnNames[cReturnSize] = aPropNames[idx];
6355 aReturnValues[cReturnSize] = aPropValues[idx];
6356 cReturnSize++;
6357 }
6358 }
6359 }
6360
6361 return hrc;
6362}
6363
6364/**
6365 * Preparation to move this medium to a new location
6366 *
6367 * @param aLocation Location of the storage unit. If the location is a FS-path,
6368 * then it can be relative to the VirtualBox home directory.
6369 *
6370 * @note Must be called from under this object's write lock.
6371 */
6372HRESULT Medium::i_preparationForMoving(const Utf8Str &aLocation)
6373{
6374 HRESULT rc = E_FAIL;
6375
6376 if (i_getLocationFull() != aLocation)
6377 {
6378 m->strNewLocationFull = aLocation;
6379 m->fMoveThisMedium = true;
6380 rc = S_OK;
6381 }
6382
6383 return rc;
6384}
6385
6386/**
6387 * Checking whether current operation "moving" or not
6388 */
6389bool Medium::i_isMoveOperation(const ComObjPtr<Medium> &aTarget) const
6390{
6391 return (this == aTarget && m->fMoveThisMedium == true) ? true:false;
6392}
6393
6394bool Medium::i_resetMoveOperationData()
6395{
6396 m->strNewLocationFull.setNull();
6397 m->fMoveThisMedium = false;
6398 return true;
6399}
6400
6401Utf8Str Medium::i_getNewLocationForMoving() const
6402{
6403 if(m->fMoveThisMedium == true)
6404 return m->strNewLocationFull;
6405 else
6406 return Utf8Str();
6407}
6408////////////////////////////////////////////////////////////////////////////////
6409//
6410// Private methods
6411//
6412////////////////////////////////////////////////////////////////////////////////
6413
6414/**
6415 * Queries information from the medium.
6416 *
6417 * As a result of this call, the accessibility state and data members such as
6418 * size and description will be updated with the current information.
6419 *
6420 * @note This method may block during a system I/O call that checks storage
6421 * accessibility.
6422 *
6423 * @note Caller MUST NOT hold the media tree or medium lock.
6424 *
6425 * @note Locks m->pParent for reading. Locks this object for writing.
6426 *
6427 * @param fSetImageId Whether to reset the UUID contained in the image file to the UUID in the medium instance data (see SetIDs())
6428 * @param fSetParentId Whether to reset the parent UUID contained in the image file to the parent
6429 * UUID in the medium instance data (see SetIDs())
6430 * @return
6431 */
6432HRESULT Medium::i_queryInfo(bool fSetImageId, bool fSetParentId, AutoCaller &autoCaller)
6433{
6434 Assert(!isWriteLockOnCurrentThread());
6435 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6436
6437 if ( ( m->state != MediumState_Created
6438 && m->state != MediumState_Inaccessible
6439 && m->state != MediumState_LockedRead)
6440 || m->fClosing)
6441 return E_FAIL;
6442
6443 HRESULT rc = S_OK;
6444
6445 int vrc = VINF_SUCCESS;
6446
6447 /* check if a blocking i_queryInfo() call is in progress on some other thread,
6448 * and wait for it to finish if so instead of querying data ourselves */
6449 if (m->queryInfoRunning)
6450 {
6451 Assert( m->state == MediumState_LockedRead
6452 || m->state == MediumState_LockedWrite);
6453
6454 while (m->queryInfoRunning)
6455 {
6456 alock.release();
6457 /* must not hold the object lock now */
6458 Assert(!isWriteLockOnCurrentThread());
6459 {
6460 AutoReadLock qlock(m->queryInfoSem COMMA_LOCKVAL_SRC_POS);
6461 }
6462 alock.acquire();
6463 }
6464
6465 return S_OK;
6466 }
6467
6468 bool success = false;
6469 Utf8Str lastAccessError;
6470
6471 /* are we dealing with a new medium constructed using the existing
6472 * location? */
6473 bool isImport = m->id.isZero();
6474 unsigned uOpenFlags = VD_OPEN_FLAGS_INFO;
6475
6476 /* Note that we don't use VD_OPEN_FLAGS_READONLY when opening new
6477 * media because that would prevent necessary modifications
6478 * when opening media of some third-party formats for the first
6479 * time in VirtualBox (such as VMDK for which VDOpen() needs to
6480 * generate an UUID if it is missing) */
6481 if ( m->hddOpenMode == OpenReadOnly
6482 || m->type == MediumType_Readonly
6483 || (!isImport && !fSetImageId && !fSetParentId)
6484 )
6485 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
6486
6487 /* Open shareable medium with the appropriate flags */
6488 if (m->type == MediumType_Shareable)
6489 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
6490
6491 /* Lock the medium, which makes the behavior much more consistent, must be
6492 * done before dropping the object lock and setting queryInfoRunning. */
6493 ComPtr<IToken> pToken;
6494 if (uOpenFlags & (VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SHAREABLE))
6495 rc = LockRead(pToken.asOutParam());
6496 else
6497 rc = LockWrite(pToken.asOutParam());
6498 if (FAILED(rc)) return rc;
6499
6500 /* Copies of the input state fields which are not read-only,
6501 * as we're dropping the lock. CAUTION: be extremely careful what
6502 * you do with the contents of this medium object, as you will
6503 * create races if there are concurrent changes. */
6504 Utf8Str format(m->strFormat);
6505 Utf8Str location(m->strLocationFull);
6506 ComObjPtr<MediumFormat> formatObj = m->formatObj;
6507
6508 /* "Output" values which can't be set because the lock isn't held
6509 * at the time the values are determined. */
6510 Guid mediumId = m->id;
6511 uint64_t mediumSize = 0;
6512 uint64_t mediumLogicalSize = 0;
6513
6514 /* Flag whether a base image has a non-zero parent UUID and thus
6515 * need repairing after it was closed again. */
6516 bool fRepairImageZeroParentUuid = false;
6517
6518 ComObjPtr<VirtualBox> pVirtualBox = m->pVirtualBox;
6519
6520 /* must be set before leaving the object lock the first time */
6521 m->queryInfoRunning = true;
6522
6523 /* must leave object lock now, because a lock from a higher lock class
6524 * is needed and also a lengthy operation is coming */
6525 alock.release();
6526 autoCaller.release();
6527
6528 /* Note that taking the queryInfoSem after leaving the object lock above
6529 * can lead to short spinning of the loops waiting for i_queryInfo() to
6530 * complete. This is unavoidable since the other order causes a lock order
6531 * violation: here it would be requesting the object lock (at the beginning
6532 * of the method), then queryInfoSem, and below the other way round. */
6533 AutoWriteLock qlock(m->queryInfoSem COMMA_LOCKVAL_SRC_POS);
6534
6535 /* take the opportunity to have a media tree lock, released initially */
6536 Assert(!isWriteLockOnCurrentThread());
6537 Assert(!pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
6538 AutoWriteLock treeLock(pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
6539 treeLock.release();
6540
6541 /* re-take the caller, but not the object lock, to keep uninit away */
6542 autoCaller.add();
6543 if (FAILED(autoCaller.rc()))
6544 {
6545 m->queryInfoRunning = false;
6546 return autoCaller.rc();
6547 }
6548
6549 try
6550 {
6551 /* skip accessibility checks for host drives */
6552 if (m->hostDrive)
6553 {
6554 success = true;
6555 throw S_OK;
6556 }
6557
6558 PVBOXHDD hdd;
6559 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
6560 ComAssertRCThrow(vrc, E_FAIL);
6561
6562 try
6563 {
6564 /** @todo This kind of opening of media is assuming that diff
6565 * media can be opened as base media. Should be documented that
6566 * it must work for all medium format backends. */
6567 vrc = VDOpen(hdd,
6568 format.c_str(),
6569 location.c_str(),
6570 uOpenFlags | m->uOpenFlagsDef,
6571 m->vdImageIfaces);
6572 if (RT_FAILURE(vrc))
6573 {
6574 lastAccessError = Utf8StrFmt(tr("Could not open the medium '%s'%s"),
6575 location.c_str(), i_vdError(vrc).c_str());
6576 throw S_OK;
6577 }
6578
6579 if (formatObj->i_getCapabilities() & MediumFormatCapabilities_Uuid)
6580 {
6581 /* Modify the UUIDs if necessary. The associated fields are
6582 * not modified by other code, so no need to copy. */
6583 if (fSetImageId)
6584 {
6585 alock.acquire();
6586 vrc = VDSetUuid(hdd, 0, m->uuidImage.raw());
6587 alock.release();
6588 if (RT_FAILURE(vrc))
6589 {
6590 lastAccessError = Utf8StrFmt(tr("Could not update the UUID of medium '%s'%s"),
6591 location.c_str(), i_vdError(vrc).c_str());
6592 throw S_OK;
6593 }
6594 mediumId = m->uuidImage;
6595 }
6596 if (fSetParentId)
6597 {
6598 alock.acquire();
6599 vrc = VDSetParentUuid(hdd, 0, m->uuidParentImage.raw());
6600 alock.release();
6601 if (RT_FAILURE(vrc))
6602 {
6603 lastAccessError = Utf8StrFmt(tr("Could not update the parent UUID of medium '%s'%s"),
6604 location.c_str(), i_vdError(vrc).c_str());
6605 throw S_OK;
6606 }
6607 }
6608 /* zap the information, these are no long-term members */
6609 alock.acquire();
6610 unconst(m->uuidImage).clear();
6611 unconst(m->uuidParentImage).clear();
6612 alock.release();
6613
6614 /* check the UUID */
6615 RTUUID uuid;
6616 vrc = VDGetUuid(hdd, 0, &uuid);
6617 ComAssertRCThrow(vrc, E_FAIL);
6618
6619 if (isImport)
6620 {
6621 mediumId = uuid;
6622
6623 if (mediumId.isZero() && (m->hddOpenMode == OpenReadOnly))
6624 // only when importing a VDMK that has no UUID, create one in memory
6625 mediumId.create();
6626 }
6627 else
6628 {
6629 Assert(!mediumId.isZero());
6630
6631 if (mediumId != uuid)
6632 {
6633 /** @todo r=klaus this always refers to VirtualBox.xml as the medium registry, even for new VMs */
6634 lastAccessError = Utf8StrFmt(
6635 tr("UUID {%RTuuid} of the medium '%s' does not match the value {%RTuuid} stored in the media registry ('%s')"),
6636 &uuid,
6637 location.c_str(),
6638 mediumId.raw(),
6639 pVirtualBox->i_settingsFilePath().c_str());
6640 throw S_OK;
6641 }
6642 }
6643 }
6644 else
6645 {
6646 /* the backend does not support storing UUIDs within the
6647 * underlying storage so use what we store in XML */
6648
6649 if (fSetImageId)
6650 {
6651 /* set the UUID if an API client wants to change it */
6652 alock.acquire();
6653 mediumId = m->uuidImage;
6654 alock.release();
6655 }
6656 else if (isImport)
6657 {
6658 /* generate an UUID for an imported UUID-less medium */
6659 mediumId.create();
6660 }
6661 }
6662
6663 /* set the image uuid before the below parent uuid handling code
6664 * might place it somewhere in the media tree, so that the medium
6665 * UUID is valid at this point */
6666 alock.acquire();
6667 if (isImport || fSetImageId)
6668 unconst(m->id) = mediumId;
6669 alock.release();
6670
6671 /* get the medium variant */
6672 unsigned uImageFlags;
6673 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
6674 ComAssertRCThrow(vrc, E_FAIL);
6675 alock.acquire();
6676 m->variant = (MediumVariant_T)uImageFlags;
6677 alock.release();
6678
6679 /* check/get the parent uuid and update corresponding state */
6680 if (uImageFlags & VD_IMAGE_FLAGS_DIFF)
6681 {
6682 RTUUID parentId;
6683 vrc = VDGetParentUuid(hdd, 0, &parentId);
6684 ComAssertRCThrow(vrc, E_FAIL);
6685
6686 /* streamOptimized VMDK images are only accepted as base
6687 * images, as this allows automatic repair of OVF appliances.
6688 * Since such images don't support random writes they will not
6689 * be created for diff images. Only an overly smart user might
6690 * manually create this case. Too bad for him. */
6691 if ( (isImport || fSetParentId)
6692 && !(uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED))
6693 {
6694 /* the parent must be known to us. Note that we freely
6695 * call locking methods of mVirtualBox and parent, as all
6696 * relevant locks must be already held. There may be no
6697 * concurrent access to the just opened medium on other
6698 * threads yet (and init() will fail if this method reports
6699 * MediumState_Inaccessible) */
6700
6701 ComObjPtr<Medium> pParent;
6702 if (RTUuidIsNull(&parentId))
6703 rc = VBOX_E_OBJECT_NOT_FOUND;
6704 else
6705 rc = pVirtualBox->i_findHardDiskById(Guid(parentId), false /* aSetError */, &pParent);
6706 if (FAILED(rc))
6707 {
6708 if (fSetImageId && !fSetParentId)
6709 {
6710 /* If the image UUID gets changed for an existing
6711 * image then the parent UUID can be stale. In such
6712 * cases clear the parent information. The parent
6713 * information may/will be re-set later if the
6714 * API client wants to adjust a complete medium
6715 * hierarchy one by one. */
6716 rc = S_OK;
6717 alock.acquire();
6718 RTUuidClear(&parentId);
6719 vrc = VDSetParentUuid(hdd, 0, &parentId);
6720 alock.release();
6721 ComAssertRCThrow(vrc, E_FAIL);
6722 }
6723 else
6724 {
6725 lastAccessError = Utf8StrFmt(tr("Parent medium with UUID {%RTuuid} of the medium '%s' is not found in the media registry ('%s')"),
6726 &parentId, location.c_str(),
6727 pVirtualBox->i_settingsFilePath().c_str());
6728 throw S_OK;
6729 }
6730 }
6731
6732 /* must drop the caller before taking the tree lock */
6733 autoCaller.release();
6734 /* we set m->pParent & children() */
6735 treeLock.acquire();
6736 autoCaller.add();
6737 if (FAILED(autoCaller.rc()))
6738 throw autoCaller.rc();
6739
6740 if (m->pParent)
6741 i_deparent();
6742
6743 if (!pParent.isNull())
6744 if (pParent->i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX)
6745 {
6746 AutoReadLock plock(pParent COMMA_LOCKVAL_SRC_POS);
6747 throw setError(VBOX_E_INVALID_OBJECT_STATE,
6748 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"),
6749 pParent->m->strLocationFull.c_str());
6750 }
6751 i_setParent(pParent);
6752
6753 treeLock.release();
6754 }
6755 else
6756 {
6757 /* must drop the caller before taking the tree lock */
6758 autoCaller.release();
6759 /* we access m->pParent */
6760 treeLock.acquire();
6761 autoCaller.add();
6762 if (FAILED(autoCaller.rc()))
6763 throw autoCaller.rc();
6764
6765 /* check that parent UUIDs match. Note that there's no need
6766 * for the parent's AutoCaller (our lifetime is bound to
6767 * it) */
6768
6769 if (m->pParent.isNull())
6770 {
6771 /* Due to a bug in VDCopy() in VirtualBox 3.0.0-3.0.14
6772 * and 3.1.0-3.1.8 there are base images out there
6773 * which have a non-zero parent UUID. No point in
6774 * complaining about them, instead automatically
6775 * repair the problem. Later we can bring back the
6776 * error message, but we should wait until really
6777 * most users have repaired their images, either with
6778 * VBoxFixHdd or this way. */
6779#if 1
6780 fRepairImageZeroParentUuid = true;
6781#else /* 0 */
6782 lastAccessError = Utf8StrFmt(
6783 tr("Medium type of '%s' is differencing but it is not associated with any parent medium in the media registry ('%s')"),
6784 location.c_str(),
6785 pVirtualBox->settingsFilePath().c_str());
6786 treeLock.release();
6787 throw S_OK;
6788#endif /* 0 */
6789 }
6790
6791 {
6792 autoCaller.release();
6793 AutoReadLock parentLock(m->pParent COMMA_LOCKVAL_SRC_POS);
6794 autoCaller.add();
6795 if (FAILED(autoCaller.rc()))
6796 throw autoCaller.rc();
6797
6798 if ( !fRepairImageZeroParentUuid
6799 && m->pParent->i_getState() != MediumState_Inaccessible
6800 && m->pParent->i_getId() != parentId)
6801 {
6802 /** @todo r=klaus this always refers to VirtualBox.xml as the medium registry, even for new VMs */
6803 lastAccessError = Utf8StrFmt(
6804 tr("Parent UUID {%RTuuid} of the medium '%s' does not match UUID {%RTuuid} of its parent medium stored in the media registry ('%s')"),
6805 &parentId, location.c_str(),
6806 m->pParent->i_getId().raw(),
6807 pVirtualBox->i_settingsFilePath().c_str());
6808 parentLock.release();
6809 treeLock.release();
6810 throw S_OK;
6811 }
6812 }
6813
6814 /// @todo NEWMEDIA what to do if the parent is not
6815 /// accessible while the diff is? Probably nothing. The
6816 /// real code will detect the mismatch anyway.
6817
6818 treeLock.release();
6819 }
6820 }
6821
6822 mediumSize = VDGetFileSize(hdd, 0);
6823 mediumLogicalSize = VDGetSize(hdd, 0);
6824
6825 success = true;
6826 }
6827 catch (HRESULT aRC)
6828 {
6829 rc = aRC;
6830 }
6831
6832 vrc = VDDestroy(hdd);
6833 if (RT_FAILURE(vrc))
6834 {
6835 lastAccessError = Utf8StrFmt(tr("Could not update and close the medium '%s'%s"),
6836 location.c_str(), i_vdError(vrc).c_str());
6837 success = false;
6838 throw S_OK;
6839 }
6840 }
6841 catch (HRESULT aRC)
6842 {
6843 rc = aRC;
6844 }
6845
6846 autoCaller.release();
6847 treeLock.acquire();
6848 autoCaller.add();
6849 if (FAILED(autoCaller.rc()))
6850 {
6851 m->queryInfoRunning = false;
6852 return autoCaller.rc();
6853 }
6854 alock.acquire();
6855
6856 if (success)
6857 {
6858 m->size = mediumSize;
6859 m->logicalSize = mediumLogicalSize;
6860 m->strLastAccessError.setNull();
6861 }
6862 else
6863 {
6864 m->strLastAccessError = lastAccessError;
6865 Log1WarningFunc(("'%s' is not accessible (error='%s', rc=%Rhrc, vrc=%Rrc)\n",
6866 location.c_str(), m->strLastAccessError.c_str(), rc, vrc));
6867 }
6868
6869 /* Set the proper state according to the result of the check */
6870 if (success)
6871 m->preLockState = MediumState_Created;
6872 else
6873 m->preLockState = MediumState_Inaccessible;
6874
6875 /* unblock anyone waiting for the i_queryInfo results */
6876 qlock.release();
6877 m->queryInfoRunning = false;
6878
6879 pToken->Abandon();
6880 pToken.setNull();
6881
6882 if (FAILED(rc)) return rc;
6883
6884 /* If this is a base image which incorrectly has a parent UUID set,
6885 * repair the image now by zeroing the parent UUID. This is only done
6886 * when we have structural information from a config file, on import
6887 * this is not possible. If someone would accidentally call openMedium
6888 * with a diff image before the base is registered this would destroy
6889 * the diff. Not acceptable. */
6890 if (fRepairImageZeroParentUuid)
6891 {
6892 rc = LockWrite(pToken.asOutParam());
6893 if (FAILED(rc)) return rc;
6894
6895 alock.release();
6896
6897 try
6898 {
6899 PVBOXHDD hdd;
6900 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
6901 ComAssertRCThrow(vrc, E_FAIL);
6902
6903 try
6904 {
6905 vrc = VDOpen(hdd,
6906 format.c_str(),
6907 location.c_str(),
6908 (uOpenFlags & ~VD_OPEN_FLAGS_READONLY) | m->uOpenFlagsDef,
6909 m->vdImageIfaces);
6910 if (RT_FAILURE(vrc))
6911 throw S_OK;
6912
6913 RTUUID zeroParentUuid;
6914 RTUuidClear(&zeroParentUuid);
6915 vrc = VDSetParentUuid(hdd, 0, &zeroParentUuid);
6916 ComAssertRCThrow(vrc, E_FAIL);
6917 }
6918 catch (HRESULT aRC)
6919 {
6920 rc = aRC;
6921 }
6922
6923 VDDestroy(hdd);
6924 }
6925 catch (HRESULT aRC)
6926 {
6927 rc = aRC;
6928 }
6929
6930 pToken->Abandon();
6931 pToken.setNull();
6932 if (FAILED(rc)) return rc;
6933 }
6934
6935 return rc;
6936}
6937
6938/**
6939 * Performs extra checks if the medium can be closed and returns S_OK in
6940 * this case. Otherwise, returns a respective error message. Called by
6941 * Close() under the medium tree lock and the medium lock.
6942 *
6943 * @note Also reused by Medium::Reset().
6944 *
6945 * @note Caller must hold the media tree write lock!
6946 */
6947HRESULT Medium::i_canClose()
6948{
6949 Assert(m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
6950
6951 if (i_getChildren().size() != 0)
6952 return setError(VBOX_E_OBJECT_IN_USE,
6953 tr("Cannot close medium '%s' because it has %d child media"),
6954 m->strLocationFull.c_str(), i_getChildren().size());
6955
6956 return S_OK;
6957}
6958
6959/**
6960 * Unregisters this medium with mVirtualBox. Called by close() under the medium tree lock.
6961 *
6962 * @note Caller must have locked the media tree lock for writing!
6963 */
6964HRESULT Medium::i_unregisterWithVirtualBox()
6965{
6966 /* Note that we need to de-associate ourselves from the parent to let
6967 * VirtualBox::i_unregisterMedium() properly save the registry */
6968
6969 /* we modify m->pParent and access children */
6970 Assert(m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
6971
6972 Medium *pParentBackup = m->pParent;
6973 AssertReturn(i_getChildren().size() == 0, E_FAIL);
6974 if (m->pParent)
6975 i_deparent();
6976
6977 HRESULT rc = m->pVirtualBox->i_unregisterMedium(this);
6978 if (FAILED(rc))
6979 {
6980 if (pParentBackup)
6981 {
6982 // re-associate with the parent as we are still relatives in the registry
6983 i_setParent(pParentBackup);
6984 }
6985 }
6986
6987 return rc;
6988}
6989
6990/**
6991 * Like SetProperty but do not trigger a settings store. Only for internal use!
6992 */
6993HRESULT Medium::i_setPropertyDirect(const Utf8Str &aName, const Utf8Str &aValue)
6994{
6995 AutoCaller autoCaller(this);
6996 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6997
6998 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
6999
7000 switch (m->state)
7001 {
7002 case MediumState_Created:
7003 case MediumState_Inaccessible:
7004 break;
7005 default:
7006 return i_setStateError();
7007 }
7008
7009 m->mapProperties[aName] = aValue;
7010
7011 return S_OK;
7012}
7013
7014/**
7015 * Sets the extended error info according to the current media state.
7016 *
7017 * @note Must be called from under this object's write or read lock.
7018 */
7019HRESULT Medium::i_setStateError()
7020{
7021 HRESULT rc = E_FAIL;
7022
7023 switch (m->state)
7024 {
7025 case MediumState_NotCreated:
7026 {
7027 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
7028 tr("Storage for the medium '%s' is not created"),
7029 m->strLocationFull.c_str());
7030 break;
7031 }
7032 case MediumState_Created:
7033 {
7034 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
7035 tr("Storage for the medium '%s' is already created"),
7036 m->strLocationFull.c_str());
7037 break;
7038 }
7039 case MediumState_LockedRead:
7040 {
7041 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
7042 tr("Medium '%s' is locked for reading by another task"),
7043 m->strLocationFull.c_str());
7044 break;
7045 }
7046 case MediumState_LockedWrite:
7047 {
7048 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
7049 tr("Medium '%s' is locked for writing by another task"),
7050 m->strLocationFull.c_str());
7051 break;
7052 }
7053 case MediumState_Inaccessible:
7054 {
7055 /* be in sync with Console::powerUpThread() */
7056 if (!m->strLastAccessError.isEmpty())
7057 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
7058 tr("Medium '%s' is not accessible. %s"),
7059 m->strLocationFull.c_str(), m->strLastAccessError.c_str());
7060 else
7061 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
7062 tr("Medium '%s' is not accessible"),
7063 m->strLocationFull.c_str());
7064 break;
7065 }
7066 case MediumState_Creating:
7067 {
7068 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
7069 tr("Storage for the medium '%s' is being created"),
7070 m->strLocationFull.c_str());
7071 break;
7072 }
7073 case MediumState_Deleting:
7074 {
7075 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
7076 tr("Storage for the medium '%s' is being deleted"),
7077 m->strLocationFull.c_str());
7078 break;
7079 }
7080 default:
7081 {
7082 AssertFailed();
7083 break;
7084 }
7085 }
7086
7087 return rc;
7088}
7089
7090/**
7091 * Sets the value of m->strLocationFull. The given location must be a fully
7092 * qualified path; relative paths are not supported here.
7093 *
7094 * As a special exception, if the specified location is a file path that ends with '/'
7095 * then the file name part will be generated by this method automatically in the format
7096 * '{<uuid>}.<ext>' where <uuid> is a fresh UUID that this method will generate
7097 * and assign to this medium, and <ext> is the default extension for this
7098 * medium's storage format. Note that this procedure requires the media state to
7099 * be NotCreated and will return a failure otherwise.
7100 *
7101 * @param aLocation Location of the storage unit. If the location is a FS-path,
7102 * then it can be relative to the VirtualBox home directory.
7103 * @param aFormat Optional fallback format if it is an import and the format
7104 * cannot be determined.
7105 *
7106 * @note Must be called from under this object's write lock.
7107 */
7108HRESULT Medium::i_setLocation(const Utf8Str &aLocation,
7109 const Utf8Str &aFormat /* = Utf8Str::Empty */)
7110{
7111 AssertReturn(!aLocation.isEmpty(), E_FAIL);
7112
7113 AutoCaller autoCaller(this);
7114 AssertComRCReturnRC(autoCaller.rc());
7115
7116 /* formatObj may be null only when initializing from an existing path and
7117 * no format is known yet */
7118 AssertReturn( (!m->strFormat.isEmpty() && !m->formatObj.isNull())
7119 || ( getObjectState().getState() == ObjectState::InInit
7120 && m->state != MediumState_NotCreated
7121 && m->id.isZero()
7122 && m->strFormat.isEmpty()
7123 && m->formatObj.isNull()),
7124 E_FAIL);
7125
7126 /* are we dealing with a new medium constructed using the existing
7127 * location? */
7128 bool isImport = m->strFormat.isEmpty();
7129
7130 if ( isImport
7131 || ( (m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File)
7132 && !m->hostDrive))
7133 {
7134 Guid id;
7135
7136 Utf8Str locationFull(aLocation);
7137
7138 if (m->state == MediumState_NotCreated)
7139 {
7140 /* must be a file (formatObj must be already known) */
7141 Assert(m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File);
7142
7143 if (RTPathFilename(aLocation.c_str()) == NULL)
7144 {
7145 /* no file name is given (either an empty string or ends with a
7146 * slash), generate a new UUID + file name if the state allows
7147 * this */
7148
7149 ComAssertMsgRet(!m->formatObj->i_getFileExtensions().empty(),
7150 ("Must be at least one extension if it is MediumFormatCapabilities_File\n"),
7151 E_FAIL);
7152
7153 Utf8Str strExt = m->formatObj->i_getFileExtensions().front();
7154 ComAssertMsgRet(!strExt.isEmpty(),
7155 ("Default extension must not be empty\n"),
7156 E_FAIL);
7157
7158 id.create();
7159
7160 locationFull = Utf8StrFmt("%s{%RTuuid}.%s",
7161 aLocation.c_str(), id.raw(), strExt.c_str());
7162 }
7163 }
7164
7165 // we must always have full paths now (if it refers to a file)
7166 if ( ( m->formatObj.isNull()
7167 || m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File)
7168 && !RTPathStartsWithRoot(locationFull.c_str()))
7169 return setError(VBOX_E_FILE_ERROR,
7170 tr("The given path '%s' is not fully qualified"),
7171 locationFull.c_str());
7172
7173 /* detect the backend from the storage unit if importing */
7174 if (isImport)
7175 {
7176 VDTYPE enmType = VDTYPE_INVALID;
7177 char *backendName = NULL;
7178
7179 int vrc = VINF_SUCCESS;
7180
7181 /* is it a file? */
7182 {
7183 RTFILE file;
7184 vrc = RTFileOpen(&file, locationFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
7185 if (RT_SUCCESS(vrc))
7186 RTFileClose(file);
7187 }
7188 if (RT_SUCCESS(vrc))
7189 {
7190 vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
7191 locationFull.c_str(), &backendName, &enmType);
7192 }
7193 else if ( vrc != VERR_FILE_NOT_FOUND
7194 && vrc != VERR_PATH_NOT_FOUND
7195 && vrc != VERR_ACCESS_DENIED
7196 && locationFull != aLocation)
7197 {
7198 /* assume it's not a file, restore the original location */
7199 locationFull = aLocation;
7200 vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
7201 locationFull.c_str(), &backendName, &enmType);
7202 }
7203
7204 if (RT_FAILURE(vrc))
7205 {
7206 if (vrc == VERR_ACCESS_DENIED)
7207 return setError(VBOX_E_FILE_ERROR,
7208 tr("Permission problem accessing the file for the medium '%s' (%Rrc)"),
7209 locationFull.c_str(), vrc);
7210 else if (vrc == VERR_FILE_NOT_FOUND || vrc == VERR_PATH_NOT_FOUND)
7211 return setError(VBOX_E_FILE_ERROR,
7212 tr("Could not find file for the medium '%s' (%Rrc)"),
7213 locationFull.c_str(), vrc);
7214 else if (aFormat.isEmpty())
7215 return setError(VBOX_E_IPRT_ERROR,
7216 tr("Could not get the storage format of the medium '%s' (%Rrc)"),
7217 locationFull.c_str(), vrc);
7218 else
7219 {
7220 HRESULT rc = i_setFormat(aFormat);
7221 /* setFormat() must not fail since we've just used the backend so
7222 * the format object must be there */
7223 AssertComRCReturnRC(rc);
7224 }
7225 }
7226 else if ( enmType == VDTYPE_INVALID
7227 || m->devType != i_convertToDeviceType(enmType))
7228 {
7229 /*
7230 * The user tried to use a image as a device which is not supported
7231 * by the backend.
7232 */
7233 return setError(E_FAIL,
7234 tr("The medium '%s' can't be used as the requested device type"),
7235 locationFull.c_str());
7236 }
7237 else
7238 {
7239 ComAssertRet(backendName != NULL && *backendName != '\0', E_FAIL);
7240
7241 HRESULT rc = i_setFormat(backendName);
7242 RTStrFree(backendName);
7243
7244 /* setFormat() must not fail since we've just used the backend so
7245 * the format object must be there */
7246 AssertComRCReturnRC(rc);
7247 }
7248 }
7249
7250 m->strLocationFull = locationFull;
7251
7252 /* is it still a file? */
7253 if ( (m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File)
7254 && (m->state == MediumState_NotCreated)
7255 )
7256 /* assign a new UUID (this UUID will be used when calling
7257 * VDCreateBase/VDCreateDiff as a wanted UUID). Note that we
7258 * also do that if we didn't generate it to make sure it is
7259 * either generated by us or reset to null */
7260 unconst(m->id) = id;
7261 }
7262 else
7263 m->strLocationFull = aLocation;
7264
7265 return S_OK;
7266}
7267
7268/**
7269 * Checks that the format ID is valid and sets it on success.
7270 *
7271 * Note that this method will caller-reference the format object on success!
7272 * This reference must be released somewhere to let the MediumFormat object be
7273 * uninitialized.
7274 *
7275 * @note Must be called from under this object's write lock.
7276 */
7277HRESULT Medium::i_setFormat(const Utf8Str &aFormat)
7278{
7279 /* get the format object first */
7280 {
7281 SystemProperties *pSysProps = m->pVirtualBox->i_getSystemProperties();
7282 AutoReadLock propsLock(pSysProps COMMA_LOCKVAL_SRC_POS);
7283
7284 unconst(m->formatObj) = pSysProps->i_mediumFormat(aFormat);
7285 if (m->formatObj.isNull())
7286 return setError(E_INVALIDARG,
7287 tr("Invalid medium storage format '%s'"),
7288 aFormat.c_str());
7289
7290 /* get properties (preinsert them as keys in the map). Note that the
7291 * map doesn't grow over the object life time since the set of
7292 * properties is meant to be constant. */
7293
7294 Assert(m->mapProperties.empty());
7295
7296 for (MediumFormat::PropertyArray::const_iterator it = m->formatObj->i_getProperties().begin();
7297 it != m->formatObj->i_getProperties().end();
7298 ++it)
7299 {
7300 m->mapProperties.insert(std::make_pair(it->strName, Utf8Str::Empty));
7301 }
7302 }
7303
7304 unconst(m->strFormat) = aFormat;
7305
7306 return S_OK;
7307}
7308
7309/**
7310 * Converts the Medium device type to the VD type.
7311 */
7312VDTYPE Medium::i_convertDeviceType()
7313{
7314 VDTYPE enmType;
7315
7316 switch (m->devType)
7317 {
7318 case DeviceType_HardDisk:
7319 enmType = VDTYPE_HDD;
7320 break;
7321 case DeviceType_DVD:
7322 enmType = VDTYPE_DVD;
7323 break;
7324 case DeviceType_Floppy:
7325 enmType = VDTYPE_FLOPPY;
7326 break;
7327 default:
7328 ComAssertFailedRet(VDTYPE_INVALID);
7329 }
7330
7331 return enmType;
7332}
7333
7334/**
7335 * Converts from the VD type to the medium type.
7336 */
7337DeviceType_T Medium::i_convertToDeviceType(VDTYPE enmType)
7338{
7339 DeviceType_T devType;
7340
7341 switch (enmType)
7342 {
7343 case VDTYPE_HDD:
7344 devType = DeviceType_HardDisk;
7345 break;
7346 case VDTYPE_DVD:
7347 devType = DeviceType_DVD;
7348 break;
7349 case VDTYPE_FLOPPY:
7350 devType = DeviceType_Floppy;
7351 break;
7352 default:
7353 ComAssertFailedRet(DeviceType_Null);
7354 }
7355
7356 return devType;
7357}
7358
7359/**
7360 * Internal method which checks whether a property name is for a filter plugin.
7361 */
7362bool Medium::i_isPropertyForFilter(const com::Utf8Str &aName)
7363{
7364 /* If the name contains "/" use the part before as a filter name and lookup the filter. */
7365 size_t offSlash;
7366 if ((offSlash = aName.find("/", 0)) != aName.npos)
7367 {
7368 com::Utf8Str strFilter;
7369 com::Utf8Str strKey;
7370
7371 HRESULT rc = strFilter.assignEx(aName, 0, offSlash);
7372 if (FAILED(rc))
7373 return false;
7374
7375 rc = strKey.assignEx(aName, offSlash + 1, aName.length() - offSlash - 1); /* Skip slash */
7376 if (FAILED(rc))
7377 return false;
7378
7379 VDFILTERINFO FilterInfo;
7380 int vrc = VDFilterInfoOne(strFilter.c_str(), &FilterInfo);
7381 if (RT_SUCCESS(vrc))
7382 {
7383 /* Check that the property exists. */
7384 PCVDCONFIGINFO paConfig = FilterInfo.paConfigInfo;
7385 while (paConfig->pszKey)
7386 {
7387 if (strKey.equals(paConfig->pszKey))
7388 return true;
7389 paConfig++;
7390 }
7391 }
7392 }
7393
7394 return false;
7395}
7396
7397/**
7398 * Returns the last error message collected by the i_vdErrorCall callback and
7399 * resets it.
7400 *
7401 * The error message is returned prepended with a dot and a space, like this:
7402 * <code>
7403 * ". <error_text> (%Rrc)"
7404 * </code>
7405 * to make it easily appendable to a more general error message. The @c %Rrc
7406 * format string is given @a aVRC as an argument.
7407 *
7408 * If there is no last error message collected by i_vdErrorCall or if it is a
7409 * null or empty string, then this function returns the following text:
7410 * <code>
7411 * " (%Rrc)"
7412 * </code>
7413 *
7414 * @note Doesn't do any object locking; it is assumed that the caller makes sure
7415 * the callback isn't called by more than one thread at a time.
7416 *
7417 * @param aVRC VBox error code to use when no error message is provided.
7418 */
7419Utf8Str Medium::i_vdError(int aVRC)
7420{
7421 Utf8Str error;
7422
7423 if (m->vdError.isEmpty())
7424 error = Utf8StrFmt(" (%Rrc)", aVRC);
7425 else
7426 error = Utf8StrFmt(".\n%s", m->vdError.c_str());
7427
7428 m->vdError.setNull();
7429
7430 return error;
7431}
7432
7433/**
7434 * Error message callback.
7435 *
7436 * Puts the reported error message to the m->vdError field.
7437 *
7438 * @note Doesn't do any object locking; it is assumed that the caller makes sure
7439 * the callback isn't called by more than one thread at a time.
7440 *
7441 * @param pvUser The opaque data passed on container creation.
7442 * @param rc The VBox error code.
7443 * @param RT_SRC_POS_DECL Use RT_SRC_POS.
7444 * @param pszFormat Error message format string.
7445 * @param va Error message arguments.
7446 */
7447/*static*/
7448DECLCALLBACK(void) Medium::i_vdErrorCall(void *pvUser, int rc, RT_SRC_POS_DECL,
7449 const char *pszFormat, va_list va)
7450{
7451 NOREF(pszFile); NOREF(iLine); NOREF(pszFunction); /* RT_SRC_POS_DECL */
7452
7453 Medium *that = static_cast<Medium*>(pvUser);
7454 AssertReturnVoid(that != NULL);
7455
7456 if (that->m->vdError.isEmpty())
7457 that->m->vdError =
7458 Utf8StrFmt("%s (%Rrc)", Utf8Str(pszFormat, va).c_str(), rc);
7459 else
7460 that->m->vdError =
7461 Utf8StrFmt("%s.\n%s (%Rrc)", that->m->vdError.c_str(),
7462 Utf8Str(pszFormat, va).c_str(), rc);
7463}
7464
7465/* static */
7466DECLCALLBACK(bool) Medium::i_vdConfigAreKeysValid(void *pvUser,
7467 const char * /* pszzValid */)
7468{
7469 Medium *that = static_cast<Medium*>(pvUser);
7470 AssertReturn(that != NULL, false);
7471
7472 /* we always return true since the only keys we have are those found in
7473 * VDBACKENDINFO */
7474 return true;
7475}
7476
7477/* static */
7478DECLCALLBACK(int) Medium::i_vdConfigQuerySize(void *pvUser,
7479 const char *pszName,
7480 size_t *pcbValue)
7481{
7482 AssertReturn(VALID_PTR(pcbValue), VERR_INVALID_POINTER);
7483
7484 Medium *that = static_cast<Medium*>(pvUser);
7485 AssertReturn(that != NULL, VERR_GENERAL_FAILURE);
7486
7487 settings::StringsMap::const_iterator it = that->m->mapProperties.find(Utf8Str(pszName));
7488 if (it == that->m->mapProperties.end())
7489 return VERR_CFGM_VALUE_NOT_FOUND;
7490
7491 /* we interpret null values as "no value" in Medium */
7492 if (it->second.isEmpty())
7493 return VERR_CFGM_VALUE_NOT_FOUND;
7494
7495 *pcbValue = it->second.length() + 1 /* include terminator */;
7496
7497 return VINF_SUCCESS;
7498}
7499
7500/* static */
7501DECLCALLBACK(int) Medium::i_vdConfigQuery(void *pvUser,
7502 const char *pszName,
7503 char *pszValue,
7504 size_t cchValue)
7505{
7506 AssertReturn(VALID_PTR(pszValue), VERR_INVALID_POINTER);
7507
7508 Medium *that = static_cast<Medium*>(pvUser);
7509 AssertReturn(that != NULL, VERR_GENERAL_FAILURE);
7510
7511 settings::StringsMap::const_iterator it = that->m->mapProperties.find(Utf8Str(pszName));
7512 if (it == that->m->mapProperties.end())
7513 return VERR_CFGM_VALUE_NOT_FOUND;
7514
7515 /* we interpret null values as "no value" in Medium */
7516 if (it->second.isEmpty())
7517 return VERR_CFGM_VALUE_NOT_FOUND;
7518
7519 const Utf8Str &value = it->second;
7520 if (value.length() >= cchValue)
7521 return VERR_CFGM_NOT_ENOUGH_SPACE;
7522
7523 memcpy(pszValue, value.c_str(), value.length() + 1);
7524
7525 return VINF_SUCCESS;
7526}
7527
7528DECLCALLBACK(int) Medium::i_vdTcpSocketCreate(uint32_t fFlags, PVDSOCKET pSock)
7529{
7530 PVDSOCKETINT pSocketInt = NULL;
7531
7532 if ((fFlags & VD_INTERFACETCPNET_CONNECT_EXTENDED_SELECT) != 0)
7533 return VERR_NOT_SUPPORTED;
7534
7535 pSocketInt = (PVDSOCKETINT)RTMemAllocZ(sizeof(VDSOCKETINT));
7536 if (!pSocketInt)
7537 return VERR_NO_MEMORY;
7538
7539 pSocketInt->hSocket = NIL_RTSOCKET;
7540 *pSock = pSocketInt;
7541 return VINF_SUCCESS;
7542}
7543
7544DECLCALLBACK(int) Medium::i_vdTcpSocketDestroy(VDSOCKET Sock)
7545{
7546 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
7547
7548 if (pSocketInt->hSocket != NIL_RTSOCKET)
7549 RTTcpClientCloseEx(pSocketInt->hSocket, false /*fGracefulShutdown*/);
7550
7551 RTMemFree(pSocketInt);
7552
7553 return VINF_SUCCESS;
7554}
7555
7556DECLCALLBACK(int) Medium::i_vdTcpClientConnect(VDSOCKET Sock, const char *pszAddress, uint32_t uPort,
7557 RTMSINTERVAL cMillies)
7558{
7559 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
7560
7561 return RTTcpClientConnectEx(pszAddress, uPort, &pSocketInt->hSocket, cMillies, NULL);
7562}
7563
7564DECLCALLBACK(int) Medium::i_vdTcpClientClose(VDSOCKET Sock)
7565{
7566 int rc = VINF_SUCCESS;
7567 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
7568
7569 rc = RTTcpClientCloseEx(pSocketInt->hSocket, false /*fGracefulShutdown*/);
7570 pSocketInt->hSocket = NIL_RTSOCKET;
7571 return rc;
7572}
7573
7574DECLCALLBACK(bool) Medium::i_vdTcpIsClientConnected(VDSOCKET Sock)
7575{
7576 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
7577 return pSocketInt->hSocket != NIL_RTSOCKET;
7578}
7579
7580DECLCALLBACK(int) Medium::i_vdTcpSelectOne(VDSOCKET Sock, RTMSINTERVAL cMillies)
7581{
7582 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
7583 return RTTcpSelectOne(pSocketInt->hSocket, cMillies);
7584}
7585
7586DECLCALLBACK(int) Medium::i_vdTcpRead(VDSOCKET Sock, void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
7587{
7588 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
7589 return RTTcpRead(pSocketInt->hSocket, pvBuffer, cbBuffer, pcbRead);
7590}
7591
7592DECLCALLBACK(int) Medium::i_vdTcpWrite(VDSOCKET Sock, const void *pvBuffer, size_t cbBuffer)
7593{
7594 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
7595 return RTTcpWrite(pSocketInt->hSocket, pvBuffer, cbBuffer);
7596}
7597
7598DECLCALLBACK(int) Medium::i_vdTcpSgWrite(VDSOCKET Sock, PCRTSGBUF pSgBuf)
7599{
7600 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
7601 return RTTcpSgWrite(pSocketInt->hSocket, pSgBuf);
7602}
7603
7604DECLCALLBACK(int) Medium::i_vdTcpFlush(VDSOCKET Sock)
7605{
7606 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
7607 return RTTcpFlush(pSocketInt->hSocket);
7608}
7609
7610DECLCALLBACK(int) Medium::i_vdTcpSetSendCoalescing(VDSOCKET Sock, bool fEnable)
7611{
7612 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
7613 return RTTcpSetSendCoalescing(pSocketInt->hSocket, fEnable);
7614}
7615
7616DECLCALLBACK(int) Medium::i_vdTcpGetLocalAddress(VDSOCKET Sock, PRTNETADDR pAddr)
7617{
7618 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
7619 return RTTcpGetLocalAddress(pSocketInt->hSocket, pAddr);
7620}
7621
7622DECLCALLBACK(int) Medium::i_vdTcpGetPeerAddress(VDSOCKET Sock, PRTNETADDR pAddr)
7623{
7624 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
7625 return RTTcpGetPeerAddress(pSocketInt->hSocket, pAddr);
7626}
7627
7628DECLCALLBACK(bool) Medium::i_vdCryptoConfigAreKeysValid(void *pvUser, const char *pszzValid)
7629{
7630 /* Just return always true here. */
7631 NOREF(pvUser);
7632 NOREF(pszzValid);
7633 return true;
7634}
7635
7636DECLCALLBACK(int) Medium::i_vdCryptoConfigQuerySize(void *pvUser, const char *pszName, size_t *pcbValue)
7637{
7638 Medium::CryptoFilterSettings *pSettings = (Medium::CryptoFilterSettings *)pvUser;
7639 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
7640 AssertReturn(VALID_PTR(pcbValue), VERR_INVALID_POINTER);
7641
7642 size_t cbValue = 0;
7643 if (!strcmp(pszName, "Algorithm"))
7644 cbValue = strlen(pSettings->pszCipher) + 1;
7645 else if (!strcmp(pszName, "KeyId"))
7646 cbValue = sizeof("irrelevant");
7647 else if (!strcmp(pszName, "KeyStore"))
7648 {
7649 if (!pSettings->pszKeyStoreLoad)
7650 return VERR_CFGM_VALUE_NOT_FOUND;
7651 cbValue = strlen(pSettings->pszKeyStoreLoad) + 1;
7652 }
7653 else if (!strcmp(pszName, "CreateKeyStore"))
7654 cbValue = 2; /* Single digit + terminator. */
7655 else
7656 return VERR_CFGM_VALUE_NOT_FOUND;
7657
7658 *pcbValue = cbValue + 1 /* include terminator */;
7659
7660 return VINF_SUCCESS;
7661}
7662
7663DECLCALLBACK(int) Medium::i_vdCryptoConfigQuery(void *pvUser, const char *pszName,
7664 char *pszValue, size_t cchValue)
7665{
7666 Medium::CryptoFilterSettings *pSettings = (Medium::CryptoFilterSettings *)pvUser;
7667 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
7668 AssertReturn(VALID_PTR(pszValue), VERR_INVALID_POINTER);
7669
7670 const char *psz = NULL;
7671 if (!strcmp(pszName, "Algorithm"))
7672 psz = pSettings->pszCipher;
7673 else if (!strcmp(pszName, "KeyId"))
7674 psz = "irrelevant";
7675 else if (!strcmp(pszName, "KeyStore"))
7676 psz = pSettings->pszKeyStoreLoad;
7677 else if (!strcmp(pszName, "CreateKeyStore"))
7678 {
7679 if (pSettings->fCreateKeyStore)
7680 psz = "1";
7681 else
7682 psz = "0";
7683 }
7684 else
7685 return VERR_CFGM_VALUE_NOT_FOUND;
7686
7687 size_t cch = strlen(psz);
7688 if (cch >= cchValue)
7689 return VERR_CFGM_NOT_ENOUGH_SPACE;
7690
7691 memcpy(pszValue, psz, cch + 1);
7692 return VINF_SUCCESS;
7693}
7694
7695DECLCALLBACK(int) Medium::i_vdCryptoKeyRetain(void *pvUser, const char *pszId,
7696 const uint8_t **ppbKey, size_t *pcbKey)
7697{
7698 Medium::CryptoFilterSettings *pSettings = (Medium::CryptoFilterSettings *)pvUser;
7699 NOREF(pszId);
7700 NOREF(ppbKey);
7701 NOREF(pcbKey);
7702 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
7703 AssertMsgFailedReturn(("This method should not be called here!\n"), VERR_INVALID_STATE);
7704}
7705
7706DECLCALLBACK(int) Medium::i_vdCryptoKeyRelease(void *pvUser, const char *pszId)
7707{
7708 Medium::CryptoFilterSettings *pSettings = (Medium::CryptoFilterSettings *)pvUser;
7709 NOREF(pszId);
7710 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
7711 AssertMsgFailedReturn(("This method should not be called here!\n"), VERR_INVALID_STATE);
7712}
7713
7714DECLCALLBACK(int) Medium::i_vdCryptoKeyStorePasswordRetain(void *pvUser, const char *pszId, const char **ppszPassword)
7715{
7716 Medium::CryptoFilterSettings *pSettings = (Medium::CryptoFilterSettings *)pvUser;
7717 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
7718
7719 NOREF(pszId);
7720 *ppszPassword = pSettings->pszPassword;
7721 return VINF_SUCCESS;
7722}
7723
7724DECLCALLBACK(int) Medium::i_vdCryptoKeyStorePasswordRelease(void *pvUser, const char *pszId)
7725{
7726 Medium::CryptoFilterSettings *pSettings = (Medium::CryptoFilterSettings *)pvUser;
7727 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
7728 NOREF(pszId);
7729 return VINF_SUCCESS;
7730}
7731
7732DECLCALLBACK(int) Medium::i_vdCryptoKeyStoreSave(void *pvUser, const void *pvKeyStore, size_t cbKeyStore)
7733{
7734 Medium::CryptoFilterSettings *pSettings = (Medium::CryptoFilterSettings *)pvUser;
7735 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
7736
7737 pSettings->pszKeyStore = (char *)RTMemAllocZ(cbKeyStore);
7738 if (!pSettings->pszKeyStore)
7739 return VERR_NO_MEMORY;
7740
7741 memcpy(pSettings->pszKeyStore, pvKeyStore, cbKeyStore);
7742 return VINF_SUCCESS;
7743}
7744
7745DECLCALLBACK(int) Medium::i_vdCryptoKeyStoreReturnParameters(void *pvUser, const char *pszCipher,
7746 const uint8_t *pbDek, size_t cbDek)
7747{
7748 Medium::CryptoFilterSettings *pSettings = (Medium::CryptoFilterSettings *)pvUser;
7749 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
7750
7751 pSettings->pszCipherReturned = RTStrDup(pszCipher);
7752 pSettings->pbDek = pbDek;
7753 pSettings->cbDek = cbDek;
7754
7755 return pSettings->pszCipherReturned ? VINF_SUCCESS : VERR_NO_MEMORY;
7756}
7757
7758/**
7759 * Starts a new thread driven by the appropriate Medium::Task::handler() method.
7760 *
7761 * @note When the task is executed by this method, IProgress::notifyComplete()
7762 * is automatically called for the progress object associated with this
7763 * task when the task is finished to signal the operation completion for
7764 * other threads asynchronously waiting for it.
7765 */
7766HRESULT Medium::i_startThread(Medium::Task *pTask)
7767{
7768#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
7769 /* Extreme paranoia: The calling thread should not hold the medium
7770 * tree lock or any medium lock. Since there is no separate lock class
7771 * for medium objects be even more strict: no other object locks. */
7772 Assert(!AutoLockHoldsLocksInClass(LOCKCLASS_LISTOFMEDIA));
7773 Assert(!AutoLockHoldsLocksInClass(getLockingClass()));
7774#endif
7775
7776 /// @todo use a more descriptive task name
7777 int vrc = RTThreadCreate(NULL, Medium::Task::fntMediumTask, pTask,
7778 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
7779 "Medium::Task");
7780 if (RT_FAILURE(vrc))
7781 {
7782 delete pTask;
7783 return setError(E_FAIL, "Could not create Medium::Task thread (%Rrc)\n", vrc);
7784 }
7785
7786 return S_OK;
7787}
7788
7789/**
7790 * Runs Medium::Task::handler() on the current thread instead of creating
7791 * a new one.
7792 *
7793 * This call implies that it is made on another temporary thread created for
7794 * some asynchronous task. Avoid calling it from a normal thread since the task
7795 * operations are potentially lengthy and will block the calling thread in this
7796 * case.
7797 *
7798 * @note When the task is executed by this method, IProgress::notifyComplete()
7799 * is not called for the progress object associated with this task when
7800 * the task is finished. Instead, the result of the operation is returned
7801 * by this method directly and it's the caller's responsibility to
7802 * complete the progress object in this case.
7803 */
7804HRESULT Medium::i_runNow(Medium::Task *pTask)
7805{
7806#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
7807 /* Extreme paranoia: The calling thread should not hold the medium
7808 * tree lock or any medium lock. Since there is no separate lock class
7809 * for medium objects be even more strict: no other object locks. */
7810 Assert(!AutoLockHoldsLocksInClass(LOCKCLASS_LISTOFMEDIA));
7811 Assert(!AutoLockHoldsLocksInClass(getLockingClass()));
7812#endif
7813
7814 /* NIL_RTTHREAD indicates synchronous call. */
7815 return (HRESULT)Medium::Task::fntMediumTask(NIL_RTTHREAD, pTask);
7816}
7817
7818/**
7819 * Implementation code for the "create base" task.
7820 *
7821 * This only gets started from Medium::CreateBaseStorage() and always runs
7822 * asynchronously. As a result, we always save the VirtualBox.xml file when
7823 * we're done here.
7824 *
7825 * @param task
7826 * @return
7827 */
7828HRESULT Medium::i_taskCreateBaseHandler(Medium::CreateBaseTask &task)
7829{
7830 /** @todo r=klaus The code below needs to be double checked with regard
7831 * to lock order violations, it probably causes lock order issues related
7832 * to the AutoCaller usage. */
7833 HRESULT rc = S_OK;
7834
7835 /* these parameters we need after creation */
7836 uint64_t size = 0, logicalSize = 0;
7837 MediumVariant_T variant = MediumVariant_Standard;
7838 bool fGenerateUuid = false;
7839
7840 try
7841 {
7842 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
7843
7844 /* The object may request a specific UUID (through a special form of
7845 * the setLocation() argument). Otherwise we have to generate it */
7846 Guid id = m->id;
7847
7848 fGenerateUuid = id.isZero();
7849 if (fGenerateUuid)
7850 {
7851 id.create();
7852 /* VirtualBox::i_registerMedium() will need UUID */
7853 unconst(m->id) = id;
7854 }
7855
7856 Utf8Str format(m->strFormat);
7857 Utf8Str location(m->strLocationFull);
7858 uint64_t capabilities = m->formatObj->i_getCapabilities();
7859 ComAssertThrow(capabilities & ( MediumFormatCapabilities_CreateFixed
7860 | MediumFormatCapabilities_CreateDynamic), E_FAIL);
7861 Assert(m->state == MediumState_Creating);
7862
7863 PVBOXHDD hdd;
7864 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
7865 ComAssertRCThrow(vrc, E_FAIL);
7866
7867 /* unlock before the potentially lengthy operation */
7868 thisLock.release();
7869
7870 try
7871 {
7872 /* ensure the directory exists */
7873 if (capabilities & MediumFormatCapabilities_File)
7874 {
7875 rc = VirtualBox::i_ensureFilePathExists(location, !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
7876 if (FAILED(rc))
7877 throw rc;
7878 }
7879
7880 VDGEOMETRY geo = { 0, 0, 0 }; /* auto-detect */
7881
7882 vrc = VDCreateBase(hdd,
7883 format.c_str(),
7884 location.c_str(),
7885 task.mSize,
7886 task.mVariant & ~MediumVariant_NoCreateDir,
7887 NULL,
7888 &geo,
7889 &geo,
7890 id.raw(),
7891 VD_OPEN_FLAGS_NORMAL | m->uOpenFlagsDef,
7892 m->vdImageIfaces,
7893 task.mVDOperationIfaces);
7894 if (RT_FAILURE(vrc))
7895 {
7896 if (vrc == VERR_VD_INVALID_TYPE)
7897 throw setError(VBOX_E_FILE_ERROR,
7898 tr("Parameters for creating the medium storage unit '%s' are invalid%s"),
7899 location.c_str(), i_vdError(vrc).c_str());
7900 else
7901 throw setError(VBOX_E_FILE_ERROR,
7902 tr("Could not create the medium storage unit '%s'%s"),
7903 location.c_str(), i_vdError(vrc).c_str());
7904 }
7905
7906 size = VDGetFileSize(hdd, 0);
7907 logicalSize = VDGetSize(hdd, 0);
7908 unsigned uImageFlags;
7909 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
7910 if (RT_SUCCESS(vrc))
7911 variant = (MediumVariant_T)uImageFlags;
7912 }
7913 catch (HRESULT aRC) { rc = aRC; }
7914
7915 VDDestroy(hdd);
7916 }
7917 catch (HRESULT aRC) { rc = aRC; }
7918
7919 if (SUCCEEDED(rc))
7920 {
7921 /* register with mVirtualBox as the last step and move to
7922 * Created state only on success (leaving an orphan file is
7923 * better than breaking media registry consistency) */
7924 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
7925 ComObjPtr<Medium> pMedium;
7926 rc = m->pVirtualBox->i_registerMedium(this, &pMedium, treeLock);
7927 Assert(this == pMedium);
7928 }
7929
7930 // re-acquire the lock before changing state
7931 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
7932
7933 if (SUCCEEDED(rc))
7934 {
7935 m->state = MediumState_Created;
7936
7937 m->size = size;
7938 m->logicalSize = logicalSize;
7939 m->variant = variant;
7940
7941 thisLock.release();
7942 i_markRegistriesModified();
7943 if (task.isAsync())
7944 {
7945 // in asynchronous mode, save settings now
7946 m->pVirtualBox->i_saveModifiedRegistries();
7947 }
7948 }
7949 else
7950 {
7951 /* back to NotCreated on failure */
7952 m->state = MediumState_NotCreated;
7953
7954 /* reset UUID to prevent it from being reused next time */
7955 if (fGenerateUuid)
7956 unconst(m->id).clear();
7957 }
7958
7959 return rc;
7960}
7961
7962/**
7963 * Implementation code for the "create diff" task.
7964 *
7965 * This task always gets started from Medium::createDiffStorage() and can run
7966 * synchronously or asynchronously depending on the "wait" parameter passed to
7967 * that function. If we run synchronously, the caller expects the medium
7968 * registry modification to be set before returning; otherwise (in asynchronous
7969 * mode), we save the settings ourselves.
7970 *
7971 * @param task
7972 * @return
7973 */
7974HRESULT Medium::i_taskCreateDiffHandler(Medium::CreateDiffTask &task)
7975{
7976 /** @todo r=klaus The code below needs to be double checked with regard
7977 * to lock order violations, it probably causes lock order issues related
7978 * to the AutoCaller usage. */
7979 HRESULT rcTmp = S_OK;
7980
7981 const ComObjPtr<Medium> &pTarget = task.mTarget;
7982
7983 uint64_t size = 0, logicalSize = 0;
7984 MediumVariant_T variant = MediumVariant_Standard;
7985 bool fGenerateUuid = false;
7986
7987 try
7988 {
7989 if (i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX)
7990 {
7991 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7992 throw setError(VBOX_E_INVALID_OBJECT_STATE,
7993 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"),
7994 m->strLocationFull.c_str());
7995 }
7996
7997 /* Lock both in {parent,child} order. */
7998 AutoMultiWriteLock2 mediaLock(this, pTarget COMMA_LOCKVAL_SRC_POS);
7999
8000 /* The object may request a specific UUID (through a special form of
8001 * the setLocation() argument). Otherwise we have to generate it */
8002 Guid targetId = pTarget->m->id;
8003
8004 fGenerateUuid = targetId.isZero();
8005 if (fGenerateUuid)
8006 {
8007 targetId.create();
8008 /* VirtualBox::i_registerMedium() will need UUID */
8009 unconst(pTarget->m->id) = targetId;
8010 }
8011
8012 Guid id = m->id;
8013
8014 Utf8Str targetFormat(pTarget->m->strFormat);
8015 Utf8Str targetLocation(pTarget->m->strLocationFull);
8016 uint64_t capabilities = pTarget->m->formatObj->i_getCapabilities();
8017 ComAssertThrow(capabilities & MediumFormatCapabilities_CreateDynamic, E_FAIL);
8018
8019 Assert(pTarget->m->state == MediumState_Creating);
8020 Assert(m->state == MediumState_LockedRead);
8021
8022 PVBOXHDD hdd;
8023 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
8024 ComAssertRCThrow(vrc, E_FAIL);
8025
8026 /* the two media are now protected by their non-default states;
8027 * unlock the media before the potentially lengthy operation */
8028 mediaLock.release();
8029
8030 try
8031 {
8032 /* Open all media in the target chain but the last. */
8033 MediumLockList::Base::const_iterator targetListBegin =
8034 task.mpMediumLockList->GetBegin();
8035 MediumLockList::Base::const_iterator targetListEnd =
8036 task.mpMediumLockList->GetEnd();
8037 for (MediumLockList::Base::const_iterator it = targetListBegin;
8038 it != targetListEnd;
8039 ++it)
8040 {
8041 const MediumLock &mediumLock = *it;
8042 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
8043
8044 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
8045
8046 /* Skip over the target diff medium */
8047 if (pMedium->m->state == MediumState_Creating)
8048 continue;
8049
8050 /* sanity check */
8051 Assert(pMedium->m->state == MediumState_LockedRead);
8052
8053 /* Open all media in appropriate mode. */
8054 vrc = VDOpen(hdd,
8055 pMedium->m->strFormat.c_str(),
8056 pMedium->m->strLocationFull.c_str(),
8057 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
8058 pMedium->m->vdImageIfaces);
8059 if (RT_FAILURE(vrc))
8060 throw setError(VBOX_E_FILE_ERROR,
8061 tr("Could not open the medium storage unit '%s'%s"),
8062 pMedium->m->strLocationFull.c_str(),
8063 i_vdError(vrc).c_str());
8064 }
8065
8066 /* ensure the target directory exists */
8067 if (capabilities & MediumFormatCapabilities_File)
8068 {
8069 HRESULT rc = VirtualBox::i_ensureFilePathExists(targetLocation,
8070 !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
8071 if (FAILED(rc))
8072 throw rc;
8073 }
8074
8075 vrc = VDCreateDiff(hdd,
8076 targetFormat.c_str(),
8077 targetLocation.c_str(),
8078 (task.mVariant & ~MediumVariant_NoCreateDir) | VD_IMAGE_FLAGS_DIFF,
8079 NULL,
8080 targetId.raw(),
8081 id.raw(),
8082 VD_OPEN_FLAGS_NORMAL | m->uOpenFlagsDef,
8083 pTarget->m->vdImageIfaces,
8084 task.mVDOperationIfaces);
8085 if (RT_FAILURE(vrc))
8086 {
8087 if (vrc == VERR_VD_INVALID_TYPE)
8088 throw setError(VBOX_E_FILE_ERROR,
8089 tr("Parameters for creating the differencing medium storage unit '%s' are invalid%s"),
8090 targetLocation.c_str(), i_vdError(vrc).c_str());
8091 else
8092 throw setError(VBOX_E_FILE_ERROR,
8093 tr("Could not create the differencing medium storage unit '%s'%s"),
8094 targetLocation.c_str(), i_vdError(vrc).c_str());
8095 }
8096
8097 size = VDGetFileSize(hdd, VD_LAST_IMAGE);
8098 logicalSize = VDGetSize(hdd, VD_LAST_IMAGE);
8099 unsigned uImageFlags;
8100 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
8101 if (RT_SUCCESS(vrc))
8102 variant = (MediumVariant_T)uImageFlags;
8103 }
8104 catch (HRESULT aRC) { rcTmp = aRC; }
8105
8106 VDDestroy(hdd);
8107 }
8108 catch (HRESULT aRC) { rcTmp = aRC; }
8109
8110 MultiResult mrc(rcTmp);
8111
8112 if (SUCCEEDED(mrc))
8113 {
8114 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
8115
8116 Assert(pTarget->m->pParent.isNull());
8117
8118 /* associate child with the parent, maximum depth was checked above */
8119 pTarget->i_setParent(this);
8120
8121 /* diffs for immutable media are auto-reset by default */
8122 bool fAutoReset;
8123 {
8124 ComObjPtr<Medium> pBase = i_getBase();
8125 AutoReadLock block(pBase COMMA_LOCKVAL_SRC_POS);
8126 fAutoReset = (pBase->m->type == MediumType_Immutable);
8127 }
8128 {
8129 AutoWriteLock tlock(pTarget COMMA_LOCKVAL_SRC_POS);
8130 pTarget->m->autoReset = fAutoReset;
8131 }
8132
8133 /* register with mVirtualBox as the last step and move to
8134 * Created state only on success (leaving an orphan file is
8135 * better than breaking media registry consistency) */
8136 ComObjPtr<Medium> pMedium;
8137 mrc = m->pVirtualBox->i_registerMedium(pTarget, &pMedium, treeLock);
8138 Assert(pTarget == pMedium);
8139
8140 if (FAILED(mrc))
8141 /* break the parent association on failure to register */
8142 i_deparent();
8143 }
8144
8145 AutoMultiWriteLock2 mediaLock(this, pTarget COMMA_LOCKVAL_SRC_POS);
8146
8147 if (SUCCEEDED(mrc))
8148 {
8149 pTarget->m->state = MediumState_Created;
8150
8151 pTarget->m->size = size;
8152 pTarget->m->logicalSize = logicalSize;
8153 pTarget->m->variant = variant;
8154 }
8155 else
8156 {
8157 /* back to NotCreated on failure */
8158 pTarget->m->state = MediumState_NotCreated;
8159
8160 pTarget->m->autoReset = false;
8161
8162 /* reset UUID to prevent it from being reused next time */
8163 if (fGenerateUuid)
8164 unconst(pTarget->m->id).clear();
8165 }
8166
8167 // deregister the task registered in createDiffStorage()
8168 Assert(m->numCreateDiffTasks != 0);
8169 --m->numCreateDiffTasks;
8170
8171 mediaLock.release();
8172 i_markRegistriesModified();
8173 if (task.isAsync())
8174 {
8175 // in asynchronous mode, save settings now
8176 m->pVirtualBox->i_saveModifiedRegistries();
8177 }
8178
8179 /* Note that in sync mode, it's the caller's responsibility to
8180 * unlock the medium. */
8181
8182 return mrc;
8183}
8184
8185/**
8186 * Implementation code for the "merge" task.
8187 *
8188 * This task always gets started from Medium::mergeTo() and can run
8189 * synchronously or asynchronously depending on the "wait" parameter passed to
8190 * that function. If we run synchronously, the caller expects the medium
8191 * registry modification to be set before returning; otherwise (in asynchronous
8192 * mode), we save the settings ourselves.
8193 *
8194 * @param task
8195 * @return
8196 */
8197HRESULT Medium::i_taskMergeHandler(Medium::MergeTask &task)
8198{
8199 /** @todo r=klaus The code below needs to be double checked with regard
8200 * to lock order violations, it probably causes lock order issues related
8201 * to the AutoCaller usage. */
8202 HRESULT rcTmp = S_OK;
8203
8204 const ComObjPtr<Medium> &pTarget = task.mTarget;
8205
8206 try
8207 {
8208 if (!task.mParentForTarget.isNull())
8209 if (task.mParentForTarget->i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX)
8210 {
8211 AutoReadLock plock(task.mParentForTarget COMMA_LOCKVAL_SRC_POS);
8212 throw setError(VBOX_E_INVALID_OBJECT_STATE,
8213 tr("Cannot merge image for medium '%s', because it exceeds the medium tree depth limit. Please merge some images which you no longer need"),
8214 task.mParentForTarget->m->strLocationFull.c_str());
8215 }
8216
8217 PVBOXHDD hdd;
8218 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
8219 ComAssertRCThrow(vrc, E_FAIL);
8220
8221 try
8222 {
8223 // Similar code appears in SessionMachine::onlineMergeMedium, so
8224 // if you make any changes below check whether they are applicable
8225 // in that context as well.
8226
8227 unsigned uTargetIdx = VD_LAST_IMAGE;
8228 unsigned uSourceIdx = VD_LAST_IMAGE;
8229 /* Open all media in the chain. */
8230 MediumLockList::Base::iterator lockListBegin =
8231 task.mpMediumLockList->GetBegin();
8232 MediumLockList::Base::iterator lockListEnd =
8233 task.mpMediumLockList->GetEnd();
8234 unsigned i = 0;
8235 for (MediumLockList::Base::iterator it = lockListBegin;
8236 it != lockListEnd;
8237 ++it)
8238 {
8239 MediumLock &mediumLock = *it;
8240 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
8241
8242 if (pMedium == this)
8243 uSourceIdx = i;
8244 else if (pMedium == pTarget)
8245 uTargetIdx = i;
8246
8247 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
8248
8249 /*
8250 * complex sanity (sane complexity)
8251 *
8252 * The current medium must be in the Deleting (medium is merged)
8253 * or LockedRead (parent medium) state if it is not the target.
8254 * If it is the target it must be in the LockedWrite state.
8255 */
8256 Assert( ( pMedium != pTarget
8257 && ( pMedium->m->state == MediumState_Deleting
8258 || pMedium->m->state == MediumState_LockedRead))
8259 || ( pMedium == pTarget
8260 && pMedium->m->state == MediumState_LockedWrite));
8261 /*
8262 * Medium must be the target, in the LockedRead state
8263 * or Deleting state where it is not allowed to be attached
8264 * to a virtual machine.
8265 */
8266 Assert( pMedium == pTarget
8267 || pMedium->m->state == MediumState_LockedRead
8268 || ( pMedium->m->backRefs.size() == 0
8269 && pMedium->m->state == MediumState_Deleting));
8270 /* The source medium must be in Deleting state. */
8271 Assert( pMedium != this
8272 || pMedium->m->state == MediumState_Deleting);
8273
8274 unsigned uOpenFlags = VD_OPEN_FLAGS_NORMAL;
8275
8276 if ( pMedium->m->state == MediumState_LockedRead
8277 || pMedium->m->state == MediumState_Deleting)
8278 uOpenFlags = VD_OPEN_FLAGS_READONLY;
8279 if (pMedium->m->type == MediumType_Shareable)
8280 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
8281
8282 /* Open the medium */
8283 vrc = VDOpen(hdd,
8284 pMedium->m->strFormat.c_str(),
8285 pMedium->m->strLocationFull.c_str(),
8286 uOpenFlags | m->uOpenFlagsDef,
8287 pMedium->m->vdImageIfaces);
8288 if (RT_FAILURE(vrc))
8289 throw vrc;
8290
8291 i++;
8292 }
8293
8294 ComAssertThrow( uSourceIdx != VD_LAST_IMAGE
8295 && uTargetIdx != VD_LAST_IMAGE, E_FAIL);
8296
8297 vrc = VDMerge(hdd, uSourceIdx, uTargetIdx,
8298 task.mVDOperationIfaces);
8299 if (RT_FAILURE(vrc))
8300 throw vrc;
8301
8302 /* update parent UUIDs */
8303 if (!task.mfMergeForward)
8304 {
8305 /* we need to update UUIDs of all source's children
8306 * which cannot be part of the container at once so
8307 * add each one in there individually */
8308 if (task.mpChildrenToReparent)
8309 {
8310 MediumLockList::Base::iterator childrenBegin = task.mpChildrenToReparent->GetBegin();
8311 MediumLockList::Base::iterator childrenEnd = task.mpChildrenToReparent->GetEnd();
8312 for (MediumLockList::Base::iterator it = childrenBegin;
8313 it != childrenEnd;
8314 ++it)
8315 {
8316 Medium *pMedium = it->GetMedium();
8317 /* VD_OPEN_FLAGS_INFO since UUID is wrong yet */
8318 vrc = VDOpen(hdd,
8319 pMedium->m->strFormat.c_str(),
8320 pMedium->m->strLocationFull.c_str(),
8321 VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
8322 pMedium->m->vdImageIfaces);
8323 if (RT_FAILURE(vrc))
8324 throw vrc;
8325
8326 vrc = VDSetParentUuid(hdd, VD_LAST_IMAGE,
8327 pTarget->m->id.raw());
8328 if (RT_FAILURE(vrc))
8329 throw vrc;
8330
8331 vrc = VDClose(hdd, false /* fDelete */);
8332 if (RT_FAILURE(vrc))
8333 throw vrc;
8334 }
8335 }
8336 }
8337 }
8338 catch (HRESULT aRC) { rcTmp = aRC; }
8339 catch (int aVRC)
8340 {
8341 rcTmp = setError(VBOX_E_FILE_ERROR,
8342 tr("Could not merge the medium '%s' to '%s'%s"),
8343 m->strLocationFull.c_str(),
8344 pTarget->m->strLocationFull.c_str(),
8345 i_vdError(aVRC).c_str());
8346 }
8347
8348 VDDestroy(hdd);
8349 }
8350 catch (HRESULT aRC) { rcTmp = aRC; }
8351
8352 ErrorInfoKeeper eik;
8353 MultiResult mrc(rcTmp);
8354 HRESULT rc2;
8355
8356 if (SUCCEEDED(mrc))
8357 {
8358 /* all media but the target were successfully deleted by
8359 * VDMerge; reparent the last one and uninitialize deleted media. */
8360
8361 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
8362
8363 if (task.mfMergeForward)
8364 {
8365 /* first, unregister the target since it may become a base
8366 * medium which needs re-registration */
8367 rc2 = m->pVirtualBox->i_unregisterMedium(pTarget);
8368 AssertComRC(rc2);
8369
8370 /* then, reparent it and disconnect the deleted branch at both ends
8371 * (chain->parent() is source's parent). Depth check above. */
8372 pTarget->i_deparent();
8373 pTarget->i_setParent(task.mParentForTarget);
8374 if (task.mParentForTarget)
8375 i_deparent();
8376
8377 /* then, register again */
8378 ComObjPtr<Medium> pMedium;
8379 rc2 = m->pVirtualBox->i_registerMedium(pTarget, &pMedium,
8380 treeLock);
8381 AssertComRC(rc2);
8382 }
8383 else
8384 {
8385 Assert(pTarget->i_getChildren().size() == 1);
8386 Medium *targetChild = pTarget->i_getChildren().front();
8387
8388 /* disconnect the deleted branch at the elder end */
8389 targetChild->i_deparent();
8390
8391 /* reparent source's children and disconnect the deleted
8392 * branch at the younger end */
8393 if (task.mpChildrenToReparent)
8394 {
8395 /* obey {parent,child} lock order */
8396 AutoWriteLock sourceLock(this COMMA_LOCKVAL_SRC_POS);
8397
8398 MediumLockList::Base::iterator childrenBegin = task.mpChildrenToReparent->GetBegin();
8399 MediumLockList::Base::iterator childrenEnd = task.mpChildrenToReparent->GetEnd();
8400 for (MediumLockList::Base::iterator it = childrenBegin;
8401 it != childrenEnd;
8402 ++it)
8403 {
8404 Medium *pMedium = it->GetMedium();
8405 AutoWriteLock childLock(pMedium COMMA_LOCKVAL_SRC_POS);
8406
8407 pMedium->i_deparent(); // removes pMedium from source
8408 // no depth check, reduces depth
8409 pMedium->i_setParent(pTarget);
8410 }
8411 }
8412 }
8413
8414 /* unregister and uninitialize all media removed by the merge */
8415 MediumLockList::Base::iterator lockListBegin =
8416 task.mpMediumLockList->GetBegin();
8417 MediumLockList::Base::iterator lockListEnd =
8418 task.mpMediumLockList->GetEnd();
8419 for (MediumLockList::Base::iterator it = lockListBegin;
8420 it != lockListEnd;
8421 )
8422 {
8423 MediumLock &mediumLock = *it;
8424 /* Create a real copy of the medium pointer, as the medium
8425 * lock deletion below would invalidate the referenced object. */
8426 const ComObjPtr<Medium> pMedium = mediumLock.GetMedium();
8427
8428 /* The target and all media not merged (readonly) are skipped */
8429 if ( pMedium == pTarget
8430 || pMedium->m->state == MediumState_LockedRead)
8431 {
8432 ++it;
8433 continue;
8434 }
8435
8436 rc2 = pMedium->m->pVirtualBox->i_unregisterMedium(pMedium);
8437 AssertComRC(rc2);
8438
8439 /* now, uninitialize the deleted medium (note that
8440 * due to the Deleting state, uninit() will not touch
8441 * the parent-child relationship so we need to
8442 * uninitialize each disk individually) */
8443
8444 /* note that the operation initiator medium (which is
8445 * normally also the source medium) is a special case
8446 * -- there is one more caller added by Task to it which
8447 * we must release. Also, if we are in sync mode, the
8448 * caller may still hold an AutoCaller instance for it
8449 * and therefore we cannot uninit() it (it's therefore
8450 * the caller's responsibility) */
8451 if (pMedium == this)
8452 {
8453 Assert(i_getChildren().size() == 0);
8454 Assert(m->backRefs.size() == 0);
8455 task.mMediumCaller.release();
8456 }
8457
8458 /* Delete the medium lock list entry, which also releases the
8459 * caller added by MergeChain before uninit() and updates the
8460 * iterator to point to the right place. */
8461 rc2 = task.mpMediumLockList->RemoveByIterator(it);
8462 AssertComRC(rc2);
8463
8464 if (task.isAsync() || pMedium != this)
8465 {
8466 treeLock.release();
8467 pMedium->uninit();
8468 treeLock.acquire();
8469 }
8470 }
8471 }
8472
8473 i_markRegistriesModified();
8474 if (task.isAsync())
8475 {
8476 // in asynchronous mode, save settings now
8477 eik.restore();
8478 m->pVirtualBox->i_saveModifiedRegistries();
8479 eik.fetch();
8480 }
8481
8482 if (FAILED(mrc))
8483 {
8484 /* Here we come if either VDMerge() failed (in which case we
8485 * assume that it tried to do everything to make a further
8486 * retry possible -- e.g. not deleted intermediate media
8487 * and so on) or VirtualBox::saveRegistries() failed (where we
8488 * should have the original tree but with intermediate storage
8489 * units deleted by VDMerge()). We have to only restore states
8490 * (through the MergeChain dtor) unless we are run synchronously
8491 * in which case it's the responsibility of the caller as stated
8492 * in the mergeTo() docs. The latter also implies that we
8493 * don't own the merge chain, so release it in this case. */
8494 if (task.isAsync())
8495 i_cancelMergeTo(task.mpChildrenToReparent, task.mpMediumLockList);
8496 }
8497
8498 return mrc;
8499}
8500
8501/**
8502 * Implementation code for the "clone" task.
8503 *
8504 * This only gets started from Medium::CloneTo() and always runs asynchronously.
8505 * As a result, we always save the VirtualBox.xml file when we're done here.
8506 *
8507 * @param task
8508 * @return
8509 */
8510HRESULT Medium::i_taskCloneHandler(Medium::CloneTask &task)
8511{
8512 /** @todo r=klaus The code below needs to be double checked with regard
8513 * to lock order violations, it probably causes lock order issues related
8514 * to the AutoCaller usage. */
8515 HRESULT rcTmp = S_OK;
8516
8517 const ComObjPtr<Medium> &pTarget = task.mTarget;
8518 const ComObjPtr<Medium> &pParent = task.mParent;
8519
8520 bool fCreatingTarget = false;
8521
8522 uint64_t size = 0, logicalSize = 0;
8523 MediumVariant_T variant = MediumVariant_Standard;
8524 bool fGenerateUuid = false;
8525
8526 try
8527 {
8528 if (!pParent.isNull())
8529 {
8530
8531 if (pParent->i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX)
8532 {
8533 AutoReadLock plock(pParent COMMA_LOCKVAL_SRC_POS);
8534 throw setError(VBOX_E_INVALID_OBJECT_STATE,
8535 tr("Cannot clone image for medium '%s', because it exceeds the medium tree depth limit. Please merge some images which you no longer need"),
8536 pParent->m->strLocationFull.c_str());
8537 }
8538 }
8539
8540 /* Lock all in {parent,child} order. The lock is also used as a
8541 * signal from the task initiator (which releases it only after
8542 * RTThreadCreate()) that we can start the job. */
8543 AutoMultiWriteLock3 thisLock(this, pTarget, pParent COMMA_LOCKVAL_SRC_POS);
8544
8545 fCreatingTarget = pTarget->m->state == MediumState_Creating;
8546
8547 /* The object may request a specific UUID (through a special form of
8548 * the setLocation() argument). Otherwise we have to generate it */
8549 Guid targetId = pTarget->m->id;
8550
8551 fGenerateUuid = targetId.isZero();
8552 if (fGenerateUuid)
8553 {
8554 targetId.create();
8555 /* VirtualBox::registerMedium() will need UUID */
8556 unconst(pTarget->m->id) = targetId;
8557 }
8558
8559 PVBOXHDD hdd;
8560 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
8561 ComAssertRCThrow(vrc, E_FAIL);
8562
8563 try
8564 {
8565 /* Open all media in the source chain. */
8566 MediumLockList::Base::const_iterator sourceListBegin =
8567 task.mpSourceMediumLockList->GetBegin();
8568 MediumLockList::Base::const_iterator sourceListEnd =
8569 task.mpSourceMediumLockList->GetEnd();
8570 for (MediumLockList::Base::const_iterator it = sourceListBegin;
8571 it != sourceListEnd;
8572 ++it)
8573 {
8574 const MediumLock &mediumLock = *it;
8575 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
8576 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
8577
8578 /* sanity check */
8579 Assert(pMedium->m->state == MediumState_LockedRead);
8580
8581 /** Open all media in read-only mode. */
8582 vrc = VDOpen(hdd,
8583 pMedium->m->strFormat.c_str(),
8584 pMedium->m->strLocationFull.c_str(),
8585 VD_OPEN_FLAGS_READONLY | m->uOpenFlagsDef,
8586 pMedium->m->vdImageIfaces);
8587 if (RT_FAILURE(vrc))
8588 throw setError(VBOX_E_FILE_ERROR,
8589 tr("Could not open the medium storage unit '%s'%s"),
8590 pMedium->m->strLocationFull.c_str(),
8591 i_vdError(vrc).c_str());
8592 }
8593
8594 Utf8Str targetFormat(pTarget->m->strFormat);
8595 Utf8Str targetLocation(pTarget->m->strLocationFull);
8596 uint64_t capabilities = pTarget->m->formatObj->i_getCapabilities();
8597
8598 Assert( pTarget->m->state == MediumState_Creating
8599 || pTarget->m->state == MediumState_LockedWrite);
8600 Assert(m->state == MediumState_LockedRead);
8601 Assert( pParent.isNull()
8602 || pParent->m->state == MediumState_LockedRead);
8603
8604 /* unlock before the potentially lengthy operation */
8605 thisLock.release();
8606
8607 /* ensure the target directory exists */
8608 if (capabilities & MediumFormatCapabilities_File)
8609 {
8610 HRESULT rc = VirtualBox::i_ensureFilePathExists(targetLocation,
8611 !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
8612 if (FAILED(rc))
8613 throw rc;
8614 }
8615
8616 PVBOXHDD targetHdd;
8617 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &targetHdd);
8618 ComAssertRCThrow(vrc, E_FAIL);
8619
8620 try
8621 {
8622 /* Open all media in the target chain. */
8623 MediumLockList::Base::const_iterator targetListBegin =
8624 task.mpTargetMediumLockList->GetBegin();
8625 MediumLockList::Base::const_iterator targetListEnd =
8626 task.mpTargetMediumLockList->GetEnd();
8627 for (MediumLockList::Base::const_iterator it = targetListBegin;
8628 it != targetListEnd;
8629 ++it)
8630 {
8631 const MediumLock &mediumLock = *it;
8632 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
8633
8634 /* If the target medium is not created yet there's no
8635 * reason to open it. */
8636 if (pMedium == pTarget && fCreatingTarget)
8637 continue;
8638
8639 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
8640
8641 /* sanity check */
8642 Assert( pMedium->m->state == MediumState_LockedRead
8643 || pMedium->m->state == MediumState_LockedWrite);
8644
8645 unsigned uOpenFlags = VD_OPEN_FLAGS_NORMAL;
8646 if (pMedium->m->state != MediumState_LockedWrite)
8647 uOpenFlags = VD_OPEN_FLAGS_READONLY;
8648 if (pMedium->m->type == MediumType_Shareable)
8649 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
8650
8651 /* Open all media in appropriate mode. */
8652 vrc = VDOpen(targetHdd,
8653 pMedium->m->strFormat.c_str(),
8654 pMedium->m->strLocationFull.c_str(),
8655 uOpenFlags | m->uOpenFlagsDef,
8656 pMedium->m->vdImageIfaces);
8657 if (RT_FAILURE(vrc))
8658 throw setError(VBOX_E_FILE_ERROR,
8659 tr("Could not open the medium storage unit '%s'%s"),
8660 pMedium->m->strLocationFull.c_str(),
8661 i_vdError(vrc).c_str());
8662 }
8663
8664 /* target isn't locked, but no changing data is accessed */
8665 if (task.midxSrcImageSame == UINT32_MAX)
8666 {
8667 vrc = VDCopy(hdd,
8668 VD_LAST_IMAGE,
8669 targetHdd,
8670 targetFormat.c_str(),
8671 (fCreatingTarget) ? targetLocation.c_str() : (char *)NULL,
8672 false /* fMoveByRename */,
8673 0 /* cbSize */,
8674 task.mVariant & ~MediumVariant_NoCreateDir,
8675 targetId.raw(),
8676 VD_OPEN_FLAGS_NORMAL | m->uOpenFlagsDef,
8677 NULL /* pVDIfsOperation */,
8678 pTarget->m->vdImageIfaces,
8679 task.mVDOperationIfaces);
8680 }
8681 else
8682 {
8683 vrc = VDCopyEx(hdd,
8684 VD_LAST_IMAGE,
8685 targetHdd,
8686 targetFormat.c_str(),
8687 (fCreatingTarget) ? targetLocation.c_str() : (char *)NULL,
8688 false /* fMoveByRename */,
8689 0 /* cbSize */,
8690 task.midxSrcImageSame,
8691 task.midxDstImageSame,
8692 task.mVariant & ~MediumVariant_NoCreateDir,
8693 targetId.raw(),
8694 VD_OPEN_FLAGS_NORMAL | m->uOpenFlagsDef,
8695 NULL /* pVDIfsOperation */,
8696 pTarget->m->vdImageIfaces,
8697 task.mVDOperationIfaces);
8698 }
8699 if (RT_FAILURE(vrc))
8700 throw setError(VBOX_E_FILE_ERROR,
8701 tr("Could not create the clone medium '%s'%s"),
8702 targetLocation.c_str(), i_vdError(vrc).c_str());
8703
8704 size = VDGetFileSize(targetHdd, VD_LAST_IMAGE);
8705 logicalSize = VDGetSize(targetHdd, VD_LAST_IMAGE);
8706 unsigned uImageFlags;
8707 vrc = VDGetImageFlags(targetHdd, 0, &uImageFlags);
8708 if (RT_SUCCESS(vrc))
8709 variant = (MediumVariant_T)uImageFlags;
8710 }
8711 catch (HRESULT aRC) { rcTmp = aRC; }
8712
8713 VDDestroy(targetHdd);
8714 }
8715 catch (HRESULT aRC) { rcTmp = aRC; }
8716
8717 VDDestroy(hdd);
8718 }
8719 catch (HRESULT aRC) { rcTmp = aRC; }
8720
8721 ErrorInfoKeeper eik;
8722 MultiResult mrc(rcTmp);
8723
8724 /* Only do the parent changes for newly created media. */
8725 if (SUCCEEDED(mrc) && fCreatingTarget)
8726 {
8727 /* we set m->pParent & children() */
8728 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
8729
8730 Assert(pTarget->m->pParent.isNull());
8731
8732 if (pParent)
8733 {
8734 /* Associate the clone with the parent and deassociate
8735 * from VirtualBox. Depth check above. */
8736 pTarget->i_setParent(pParent);
8737
8738 /* register with mVirtualBox as the last step and move to
8739 * Created state only on success (leaving an orphan file is
8740 * better than breaking media registry consistency) */
8741 eik.restore();
8742 ComObjPtr<Medium> pMedium;
8743 mrc = pParent->m->pVirtualBox->i_registerMedium(pTarget, &pMedium,
8744 treeLock);
8745 Assert( FAILED(mrc)
8746 || pTarget == pMedium);
8747 eik.fetch();
8748
8749 if (FAILED(mrc))
8750 /* break parent association on failure to register */
8751 pTarget->i_deparent(); // removes target from parent
8752 }
8753 else
8754 {
8755 /* just register */
8756 eik.restore();
8757 ComObjPtr<Medium> pMedium;
8758 mrc = m->pVirtualBox->i_registerMedium(pTarget, &pMedium,
8759 treeLock);
8760 Assert( FAILED(mrc)
8761 || pTarget == pMedium);
8762 eik.fetch();
8763 }
8764 }
8765
8766 if (fCreatingTarget)
8767 {
8768 AutoWriteLock mLock(pTarget COMMA_LOCKVAL_SRC_POS);
8769
8770 if (SUCCEEDED(mrc))
8771 {
8772 pTarget->m->state = MediumState_Created;
8773
8774 pTarget->m->size = size;
8775 pTarget->m->logicalSize = logicalSize;
8776 pTarget->m->variant = variant;
8777 }
8778 else
8779 {
8780 /* back to NotCreated on failure */
8781 pTarget->m->state = MediumState_NotCreated;
8782
8783 /* reset UUID to prevent it from being reused next time */
8784 if (fGenerateUuid)
8785 unconst(pTarget->m->id).clear();
8786 }
8787 }
8788
8789 /* Copy any filter related settings over to the target. */
8790 if (SUCCEEDED(mrc))
8791 {
8792 /* Copy any filter related settings over. */
8793 ComObjPtr<Medium> pBase = i_getBase();
8794 ComObjPtr<Medium> pTargetBase = pTarget->i_getBase();
8795 std::vector<com::Utf8Str> aFilterPropNames;
8796 std::vector<com::Utf8Str> aFilterPropValues;
8797 mrc = pBase->i_getFilterProperties(aFilterPropNames, aFilterPropValues);
8798 if (SUCCEEDED(mrc))
8799 {
8800 /* Go through the properties and add them to the target medium. */
8801 for (unsigned idx = 0; idx < aFilterPropNames.size(); idx++)
8802 {
8803 mrc = pTargetBase->i_setPropertyDirect(aFilterPropNames[idx], aFilterPropValues[idx]);
8804 if (FAILED(mrc)) break;
8805 }
8806
8807 // now, at the end of this task (always asynchronous), save the settings
8808 if (SUCCEEDED(mrc))
8809 {
8810 // save the settings
8811 i_markRegistriesModified();
8812 /* collect multiple errors */
8813 eik.restore();
8814 m->pVirtualBox->i_saveModifiedRegistries();
8815 eik.fetch();
8816 }
8817 }
8818 }
8819
8820 /* Everything is explicitly unlocked when the task exits,
8821 * as the task destruction also destroys the source chain. */
8822
8823 /* Make sure the source chain is released early. It could happen
8824 * that we get a deadlock in Appliance::Import when Medium::Close
8825 * is called & the source chain is released at the same time. */
8826 task.mpSourceMediumLockList->Clear();
8827
8828 return mrc;
8829}
8830
8831/**
8832 * Implementation code for the "move" task.
8833 *
8834 * This only gets started from Medium::SetLocation() and always
8835 * runs asynchronously.
8836 *
8837 * @param task
8838 * @return
8839 */
8840HRESULT Medium::i_taskMoveHandler(Medium::MoveTask &task)
8841{
8842
8843 HRESULT rcOut = S_OK;
8844
8845 /* pTarget is equal "this" in our case */
8846 const ComObjPtr<Medium> &pTarget = task.mMedium;
8847
8848 uint64_t size = 0, logicalSize = 0;
8849 MediumVariant_T variant = MediumVariant_Standard;
8850
8851 /*
8852 * it's exactly moving, not cloning
8853 */
8854 if (!i_isMoveOperation(pTarget))
8855 {
8856 HRESULT rc = setError(VBOX_E_FILE_ERROR,
8857 tr("Wrong preconditions for moving the medium %s"),
8858 pTarget->m->strLocationFull.c_str());
8859 return rc;
8860 }
8861
8862 try
8863 {
8864 /* Lock all in {parent,child} order. The lock is also used as a
8865 * signal from the task initiator (which releases it only after
8866 * RTThreadCreate()) that we can start the job. */
8867
8868 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
8869
8870 PVBOXHDD hdd;
8871 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
8872 ComAssertRCThrow(vrc, E_FAIL);
8873
8874 try
8875 {
8876 /* Open all media in the source chain. */
8877 MediumLockList::Base::const_iterator sourceListBegin =
8878 task.mpMediumLockList->GetBegin();
8879 MediumLockList::Base::const_iterator sourceListEnd =
8880 task.mpMediumLockList->GetEnd();
8881 for (MediumLockList::Base::const_iterator it = sourceListBegin;
8882 it != sourceListEnd;
8883 ++it)
8884 {
8885 const MediumLock &mediumLock = *it;
8886 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
8887 AutoWriteLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
8888
8889 /* sanity check */
8890 Assert(pMedium->m->state == MediumState_LockedWrite);
8891
8892 vrc = VDOpen(hdd,
8893 pMedium->m->strFormat.c_str(),
8894 pMedium->m->strLocationFull.c_str(),
8895 VD_OPEN_FLAGS_NORMAL,
8896 pMedium->m->vdImageIfaces);
8897 if (RT_FAILURE(vrc))
8898 throw setError(VBOX_E_FILE_ERROR,
8899 tr("Could not open the medium storage unit '%s'%s"),
8900 pMedium->m->strLocationFull.c_str(),
8901 i_vdError(vrc).c_str());
8902 }
8903
8904 /* we can directly use pTarget->m->"variables" but for better reading we use local copies */
8905 Guid targetId = pTarget->m->id;
8906 Utf8Str targetFormat(pTarget->m->strFormat);
8907 uint64_t targetCapabilities = pTarget->m->formatObj->i_getCapabilities();
8908
8909 /*
8910 * change target location
8911 * m->strNewLocationFull has been set already together with m->fMoveThisMedium in
8912 * i_preparationForMoving()
8913 */
8914 Utf8Str targetLocation = i_getNewLocationForMoving();
8915
8916 /* unlock before the potentially lengthy operation */
8917 thisLock.release();
8918
8919 /* ensure the target directory exists */
8920 if (targetCapabilities & MediumFormatCapabilities_File)
8921 {
8922 HRESULT rc = VirtualBox::i_ensureFilePathExists(targetLocation,
8923 !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
8924 if (FAILED(rc))
8925 throw rc;
8926 }
8927
8928 try
8929 {
8930 vrc = VDCopy(hdd,
8931 VD_LAST_IMAGE,
8932 hdd,
8933 targetFormat.c_str(),
8934 targetLocation.c_str(),
8935 true /* fMoveByRename */,
8936 0 /* cbSize */,
8937 VD_IMAGE_FLAGS_NONE,
8938 targetId.raw(),
8939 VD_OPEN_FLAGS_NORMAL,
8940 NULL /* pVDIfsOperation */,
8941 NULL,
8942 NULL);
8943 if (RT_FAILURE(vrc))
8944 throw setError(VBOX_E_FILE_ERROR,
8945 tr("Could not move medium '%s'%s"),
8946 targetLocation.c_str(), i_vdError(vrc).c_str());
8947 size = VDGetFileSize(hdd, VD_LAST_IMAGE);
8948 logicalSize = VDGetSize(hdd, VD_LAST_IMAGE);
8949 unsigned uImageFlags;
8950 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
8951 if (RT_SUCCESS(vrc))
8952 variant = (MediumVariant_T)uImageFlags;
8953
8954 /*
8955 * set current location, because VDCopy\VDCopyEx doesn't do it.
8956 * also reset moving flag
8957 */
8958 i_resetMoveOperationData();
8959 m->strLocationFull = targetLocation;
8960
8961 }
8962 catch (HRESULT aRC) { rcOut = aRC; }
8963
8964 }
8965 catch (HRESULT aRC) { rcOut = aRC; }
8966
8967 VDDestroy(hdd);
8968 }
8969 catch (HRESULT aRC) { rcOut = aRC; }
8970
8971 ErrorInfoKeeper eik;
8972 MultiResult mrc(rcOut);
8973
8974 // now, at the end of this task (always asynchronous), save the settings
8975 if (SUCCEEDED(mrc))
8976 {
8977 // save the settings
8978 i_markRegistriesModified();
8979 /* collect multiple errors */
8980 eik.restore();
8981 m->pVirtualBox->i_saveModifiedRegistries();
8982 eik.fetch();
8983 }
8984
8985 /* Everything is explicitly unlocked when the task exits,
8986 * as the task destruction also destroys the source chain. */
8987
8988 task.mpMediumLockList->Clear();
8989
8990 return mrc;
8991}
8992
8993/**
8994 * Implementation code for the "delete" task.
8995 *
8996 * This task always gets started from Medium::deleteStorage() and can run
8997 * synchronously or asynchronously depending on the "wait" parameter passed to
8998 * that function.
8999 *
9000 * @param task
9001 * @return
9002 */
9003HRESULT Medium::i_taskDeleteHandler(Medium::DeleteTask &task)
9004{
9005 NOREF(task);
9006 HRESULT rc = S_OK;
9007
9008 try
9009 {
9010 /* The lock is also used as a signal from the task initiator (which
9011 * releases it only after RTThreadCreate()) that we can start the job */
9012 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
9013
9014 PVBOXHDD hdd;
9015 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
9016 ComAssertRCThrow(vrc, E_FAIL);
9017
9018 Utf8Str format(m->strFormat);
9019 Utf8Str location(m->strLocationFull);
9020
9021 /* unlock before the potentially lengthy operation */
9022 Assert(m->state == MediumState_Deleting);
9023 thisLock.release();
9024
9025 try
9026 {
9027 vrc = VDOpen(hdd,
9028 format.c_str(),
9029 location.c_str(),
9030 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
9031 m->vdImageIfaces);
9032 if (RT_SUCCESS(vrc))
9033 vrc = VDClose(hdd, true /* fDelete */);
9034
9035 if (RT_FAILURE(vrc))
9036 throw setError(VBOX_E_FILE_ERROR,
9037 tr("Could not delete the medium storage unit '%s'%s"),
9038 location.c_str(), i_vdError(vrc).c_str());
9039
9040 }
9041 catch (HRESULT aRC) { rc = aRC; }
9042
9043 VDDestroy(hdd);
9044 }
9045 catch (HRESULT aRC) { rc = aRC; }
9046
9047 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
9048
9049 /* go to the NotCreated state even on failure since the storage
9050 * may have been already partially deleted and cannot be used any
9051 * more. One will be able to manually re-open the storage if really
9052 * needed to re-register it. */
9053 m->state = MediumState_NotCreated;
9054
9055 /* Reset UUID to prevent Create* from reusing it again */
9056 unconst(m->id).clear();
9057
9058 return rc;
9059}
9060
9061/**
9062 * Implementation code for the "reset" task.
9063 *
9064 * This always gets started asynchronously from Medium::Reset().
9065 *
9066 * @param task
9067 * @return
9068 */
9069HRESULT Medium::i_taskResetHandler(Medium::ResetTask &task)
9070{
9071 HRESULT rc = S_OK;
9072
9073 uint64_t size = 0, logicalSize = 0;
9074 MediumVariant_T variant = MediumVariant_Standard;
9075
9076 try
9077 {
9078 /* The lock is also used as a signal from the task initiator (which
9079 * releases it only after RTThreadCreate()) that we can start the job */
9080 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
9081
9082 /// @todo Below we use a pair of delete/create operations to reset
9083 /// the diff contents but the most efficient way will of course be
9084 /// to add a VDResetDiff() API call
9085
9086 PVBOXHDD hdd;
9087 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
9088 ComAssertRCThrow(vrc, E_FAIL);
9089
9090 Guid id = m->id;
9091 Utf8Str format(m->strFormat);
9092 Utf8Str location(m->strLocationFull);
9093
9094 Medium *pParent = m->pParent;
9095 Guid parentId = pParent->m->id;
9096 Utf8Str parentFormat(pParent->m->strFormat);
9097 Utf8Str parentLocation(pParent->m->strLocationFull);
9098
9099 Assert(m->state == MediumState_LockedWrite);
9100
9101 /* unlock before the potentially lengthy operation */
9102 thisLock.release();
9103
9104 try
9105 {
9106 /* Open all media in the target chain but the last. */
9107 MediumLockList::Base::const_iterator targetListBegin =
9108 task.mpMediumLockList->GetBegin();
9109 MediumLockList::Base::const_iterator targetListEnd =
9110 task.mpMediumLockList->GetEnd();
9111 for (MediumLockList::Base::const_iterator it = targetListBegin;
9112 it != targetListEnd;
9113 ++it)
9114 {
9115 const MediumLock &mediumLock = *it;
9116 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
9117
9118 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
9119
9120 /* sanity check, "this" is checked above */
9121 Assert( pMedium == this
9122 || pMedium->m->state == MediumState_LockedRead);
9123
9124 /* Open all media in appropriate mode. */
9125 vrc = VDOpen(hdd,
9126 pMedium->m->strFormat.c_str(),
9127 pMedium->m->strLocationFull.c_str(),
9128 VD_OPEN_FLAGS_READONLY | m->uOpenFlagsDef,
9129 pMedium->m->vdImageIfaces);
9130 if (RT_FAILURE(vrc))
9131 throw setError(VBOX_E_FILE_ERROR,
9132 tr("Could not open the medium storage unit '%s'%s"),
9133 pMedium->m->strLocationFull.c_str(),
9134 i_vdError(vrc).c_str());
9135
9136 /* Done when we hit the media which should be reset */
9137 if (pMedium == this)
9138 break;
9139 }
9140
9141 /* first, delete the storage unit */
9142 vrc = VDClose(hdd, true /* fDelete */);
9143 if (RT_FAILURE(vrc))
9144 throw setError(VBOX_E_FILE_ERROR,
9145 tr("Could not delete the medium storage unit '%s'%s"),
9146 location.c_str(), i_vdError(vrc).c_str());
9147
9148 /* next, create it again */
9149 vrc = VDOpen(hdd,
9150 parentFormat.c_str(),
9151 parentLocation.c_str(),
9152 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
9153 m->vdImageIfaces);
9154 if (RT_FAILURE(vrc))
9155 throw setError(VBOX_E_FILE_ERROR,
9156 tr("Could not open the medium storage unit '%s'%s"),
9157 parentLocation.c_str(), i_vdError(vrc).c_str());
9158
9159 vrc = VDCreateDiff(hdd,
9160 format.c_str(),
9161 location.c_str(),
9162 /// @todo use the same medium variant as before
9163 VD_IMAGE_FLAGS_NONE,
9164 NULL,
9165 id.raw(),
9166 parentId.raw(),
9167 VD_OPEN_FLAGS_NORMAL,
9168 m->vdImageIfaces,
9169 task.mVDOperationIfaces);
9170 if (RT_FAILURE(vrc))
9171 {
9172 if (vrc == VERR_VD_INVALID_TYPE)
9173 throw setError(VBOX_E_FILE_ERROR,
9174 tr("Parameters for creating the differencing medium storage unit '%s' are invalid%s"),
9175 location.c_str(), i_vdError(vrc).c_str());
9176 else
9177 throw setError(VBOX_E_FILE_ERROR,
9178 tr("Could not create the differencing medium storage unit '%s'%s"),
9179 location.c_str(), i_vdError(vrc).c_str());
9180 }
9181
9182 size = VDGetFileSize(hdd, VD_LAST_IMAGE);
9183 logicalSize = VDGetSize(hdd, VD_LAST_IMAGE);
9184 unsigned uImageFlags;
9185 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
9186 if (RT_SUCCESS(vrc))
9187 variant = (MediumVariant_T)uImageFlags;
9188 }
9189 catch (HRESULT aRC) { rc = aRC; }
9190
9191 VDDestroy(hdd);
9192 }
9193 catch (HRESULT aRC) { rc = aRC; }
9194
9195 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
9196
9197 m->size = size;
9198 m->logicalSize = logicalSize;
9199 m->variant = variant;
9200
9201 /* Everything is explicitly unlocked when the task exits,
9202 * as the task destruction also destroys the media chain. */
9203
9204 return rc;
9205}
9206
9207/**
9208 * Implementation code for the "compact" task.
9209 *
9210 * @param task
9211 * @return
9212 */
9213HRESULT Medium::i_taskCompactHandler(Medium::CompactTask &task)
9214{
9215 HRESULT rc = S_OK;
9216
9217 /* Lock all in {parent,child} order. The lock is also used as a
9218 * signal from the task initiator (which releases it only after
9219 * RTThreadCreate()) that we can start the job. */
9220 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
9221
9222 try
9223 {
9224 PVBOXHDD hdd;
9225 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
9226 ComAssertRCThrow(vrc, E_FAIL);
9227
9228 try
9229 {
9230 /* Open all media in the chain. */
9231 MediumLockList::Base::const_iterator mediumListBegin =
9232 task.mpMediumLockList->GetBegin();
9233 MediumLockList::Base::const_iterator mediumListEnd =
9234 task.mpMediumLockList->GetEnd();
9235 MediumLockList::Base::const_iterator mediumListLast =
9236 mediumListEnd;
9237 --mediumListLast;
9238 for (MediumLockList::Base::const_iterator it = mediumListBegin;
9239 it != mediumListEnd;
9240 ++it)
9241 {
9242 const MediumLock &mediumLock = *it;
9243 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
9244 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
9245
9246 /* sanity check */
9247 if (it == mediumListLast)
9248 Assert(pMedium->m->state == MediumState_LockedWrite);
9249 else
9250 Assert(pMedium->m->state == MediumState_LockedRead);
9251
9252 /* Open all media but last in read-only mode. Do not handle
9253 * shareable media, as compaction and sharing are mutually
9254 * exclusive. */
9255 vrc = VDOpen(hdd,
9256 pMedium->m->strFormat.c_str(),
9257 pMedium->m->strLocationFull.c_str(),
9258 m->uOpenFlagsDef | (it == mediumListLast ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY),
9259 pMedium->m->vdImageIfaces);
9260 if (RT_FAILURE(vrc))
9261 throw setError(VBOX_E_FILE_ERROR,
9262 tr("Could not open the medium storage unit '%s'%s"),
9263 pMedium->m->strLocationFull.c_str(),
9264 i_vdError(vrc).c_str());
9265 }
9266
9267 Assert(m->state == MediumState_LockedWrite);
9268
9269 Utf8Str location(m->strLocationFull);
9270
9271 /* unlock before the potentially lengthy operation */
9272 thisLock.release();
9273
9274 vrc = VDCompact(hdd, VD_LAST_IMAGE, task.mVDOperationIfaces);
9275 if (RT_FAILURE(vrc))
9276 {
9277 if (vrc == VERR_NOT_SUPPORTED)
9278 throw setError(VBOX_E_NOT_SUPPORTED,
9279 tr("Compacting is not yet supported for medium '%s'"),
9280 location.c_str());
9281 else if (vrc == VERR_NOT_IMPLEMENTED)
9282 throw setError(E_NOTIMPL,
9283 tr("Compacting is not implemented, medium '%s'"),
9284 location.c_str());
9285 else
9286 throw setError(VBOX_E_FILE_ERROR,
9287 tr("Could not compact medium '%s'%s"),
9288 location.c_str(),
9289 i_vdError(vrc).c_str());
9290 }
9291 }
9292 catch (HRESULT aRC) { rc = aRC; }
9293
9294 VDDestroy(hdd);
9295 }
9296 catch (HRESULT aRC) { rc = aRC; }
9297
9298 /* Everything is explicitly unlocked when the task exits,
9299 * as the task destruction also destroys the media chain. */
9300
9301 return rc;
9302}
9303
9304/**
9305 * Implementation code for the "resize" task.
9306 *
9307 * @param task
9308 * @return
9309 */
9310HRESULT Medium::i_taskResizeHandler(Medium::ResizeTask &task)
9311{
9312 HRESULT rc = S_OK;
9313
9314 uint64_t size = 0, logicalSize = 0;
9315
9316 try
9317 {
9318 /* The lock is also used as a signal from the task initiator (which
9319 * releases it only after RTThreadCreate()) that we can start the job */
9320 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
9321
9322 PVBOXHDD hdd;
9323 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
9324 ComAssertRCThrow(vrc, E_FAIL);
9325
9326 try
9327 {
9328 /* Open all media in the chain. */
9329 MediumLockList::Base::const_iterator mediumListBegin =
9330 task.mpMediumLockList->GetBegin();
9331 MediumLockList::Base::const_iterator mediumListEnd =
9332 task.mpMediumLockList->GetEnd();
9333 MediumLockList::Base::const_iterator mediumListLast =
9334 mediumListEnd;
9335 --mediumListLast;
9336 for (MediumLockList::Base::const_iterator it = mediumListBegin;
9337 it != mediumListEnd;
9338 ++it)
9339 {
9340 const MediumLock &mediumLock = *it;
9341 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
9342 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
9343
9344 /* sanity check */
9345 if (it == mediumListLast)
9346 Assert(pMedium->m->state == MediumState_LockedWrite);
9347 else
9348 Assert(pMedium->m->state == MediumState_LockedRead);
9349
9350 /* Open all media but last in read-only mode. Do not handle
9351 * shareable media, as compaction and sharing are mutually
9352 * exclusive. */
9353 vrc = VDOpen(hdd,
9354 pMedium->m->strFormat.c_str(),
9355 pMedium->m->strLocationFull.c_str(),
9356 m->uOpenFlagsDef | (it == mediumListLast ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY),
9357 pMedium->m->vdImageIfaces);
9358 if (RT_FAILURE(vrc))
9359 throw setError(VBOX_E_FILE_ERROR,
9360 tr("Could not open the medium storage unit '%s'%s"),
9361 pMedium->m->strLocationFull.c_str(),
9362 i_vdError(vrc).c_str());
9363 }
9364
9365 Assert(m->state == MediumState_LockedWrite);
9366
9367 Utf8Str location(m->strLocationFull);
9368
9369 /* unlock before the potentially lengthy operation */
9370 thisLock.release();
9371
9372 VDGEOMETRY geo = {0, 0, 0}; /* auto */
9373 vrc = VDResize(hdd, task.mSize, &geo, &geo, task.mVDOperationIfaces);
9374 if (RT_FAILURE(vrc))
9375 {
9376 if (vrc == VERR_NOT_SUPPORTED)
9377 throw setError(VBOX_E_NOT_SUPPORTED,
9378 tr("Resizing to new size %llu is not yet supported for medium '%s'"),
9379 task.mSize, location.c_str());
9380 else if (vrc == VERR_NOT_IMPLEMENTED)
9381 throw setError(E_NOTIMPL,
9382 tr("Resiting is not implemented, medium '%s'"),
9383 location.c_str());
9384 else
9385 throw setError(VBOX_E_FILE_ERROR,
9386 tr("Could not resize medium '%s'%s"),
9387 location.c_str(),
9388 i_vdError(vrc).c_str());
9389 }
9390 size = VDGetFileSize(hdd, VD_LAST_IMAGE);
9391 logicalSize = VDGetSize(hdd, VD_LAST_IMAGE);
9392 }
9393 catch (HRESULT aRC) { rc = aRC; }
9394
9395 VDDestroy(hdd);
9396 }
9397 catch (HRESULT aRC) { rc = aRC; }
9398
9399 if (SUCCEEDED(rc))
9400 {
9401 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
9402 m->size = size;
9403 m->logicalSize = logicalSize;
9404 }
9405
9406 /* Everything is explicitly unlocked when the task exits,
9407 * as the task destruction also destroys the media chain. */
9408
9409 return rc;
9410}
9411
9412/**
9413 * Implementation code for the "export" task.
9414 *
9415 * This only gets started from Medium::exportFile() and always runs
9416 * asynchronously. It doesn't touch anything configuration related, so
9417 * we never save the VirtualBox.xml file here.
9418 *
9419 * @param task
9420 * @return
9421 */
9422HRESULT Medium::i_taskExportHandler(Medium::ExportTask &task)
9423{
9424 HRESULT rc = S_OK;
9425
9426 try
9427 {
9428 /* Lock all in {parent,child} order. The lock is also used as a
9429 * signal from the task initiator (which releases it only after
9430 * RTThreadCreate()) that we can start the job. */
9431 ComObjPtr<Medium> pBase = i_getBase();
9432 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
9433
9434 PVBOXHDD hdd;
9435 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
9436 ComAssertRCThrow(vrc, E_FAIL);
9437
9438 try
9439 {
9440 settings::StringsMap::iterator itKeyStore = pBase->m->mapProperties.find("CRYPT/KeyStore");
9441 if (itKeyStore != pBase->m->mapProperties.end())
9442 {
9443 settings::StringsMap::iterator itKeyId = pBase->m->mapProperties.find("CRYPT/KeyId");
9444
9445#ifdef VBOX_WITH_EXTPACK
9446 static const Utf8Str strExtPackPuel("Oracle VM VirtualBox Extension Pack");
9447 static const char *s_pszVDPlugin = "VDPluginCrypt";
9448 ExtPackManager *pExtPackManager = m->pVirtualBox->i_getExtPackManager();
9449 if (pExtPackManager->i_isExtPackUsable(strExtPackPuel.c_str()))
9450 {
9451 /* Load the plugin */
9452 Utf8Str strPlugin;
9453 rc = pExtPackManager->i_getLibraryPathForExtPack(s_pszVDPlugin, &strExtPackPuel, &strPlugin);
9454 if (SUCCEEDED(rc))
9455 {
9456 vrc = VDPluginLoadFromFilename(strPlugin.c_str());
9457 if (RT_FAILURE(vrc))
9458 throw setError(VBOX_E_NOT_SUPPORTED,
9459 tr("Retrieving encryption settings of the image failed because the encryption plugin could not be loaded (%s)"),
9460 i_vdError(vrc).c_str());
9461 }
9462 else
9463 throw setError(VBOX_E_NOT_SUPPORTED,
9464 tr("Encryption is not supported because the extension pack '%s' is missing the encryption plugin (old extension pack installed?)"),
9465 strExtPackPuel.c_str());
9466 }
9467 else
9468 throw setError(VBOX_E_NOT_SUPPORTED,
9469 tr("Encryption is not supported because the extension pack '%s' is missing"),
9470 strExtPackPuel.c_str());
9471#else
9472 throw setError(VBOX_E_NOT_SUPPORTED,
9473 tr("Encryption is not supported because extension pack support is not built in"));
9474#endif
9475
9476 if (itKeyId == pBase->m->mapProperties.end())
9477 throw setError(VBOX_E_INVALID_OBJECT_STATE,
9478 tr("Image '%s' is configured for encryption but doesn't has a key identifier set"),
9479 pBase->m->strLocationFull.c_str());
9480
9481 /* Find the proper secret key in the key store. */
9482 if (!task.m_pSecretKeyStore)
9483 throw setError(VBOX_E_INVALID_OBJECT_STATE,
9484 tr("Image '%s' is configured for encryption but there is no key store to retrieve the password from"),
9485 pBase->m->strLocationFull.c_str());
9486
9487 SecretKey *pKey = NULL;
9488 vrc = task.m_pSecretKeyStore->retainSecretKey(itKeyId->second, &pKey);
9489 if (RT_FAILURE(vrc))
9490 throw setError(VBOX_E_INVALID_OBJECT_STATE,
9491 tr("Failed to retrieve the secret key with ID \"%s\" from the store (%Rrc)"),
9492 itKeyId->second.c_str(), vrc);
9493
9494 Medium::CryptoFilterSettings CryptoSettingsRead;
9495 i_taskEncryptSettingsSetup(&CryptoSettingsRead, NULL, itKeyStore->second.c_str(), (const char *)pKey->getKeyBuffer(),
9496 false /* fCreateKeyStore */);
9497 vrc = VDFilterAdd(hdd, "CRYPT", VD_FILTER_FLAGS_READ, CryptoSettingsRead.vdFilterIfaces);
9498 if (vrc == VERR_VD_PASSWORD_INCORRECT)
9499 {
9500 task.m_pSecretKeyStore->releaseSecretKey(itKeyId->second);
9501 throw setError(VBOX_E_PASSWORD_INCORRECT,
9502 tr("The password to decrypt the image is incorrect"));
9503 }
9504 else if (RT_FAILURE(vrc))
9505 {
9506 task.m_pSecretKeyStore->releaseSecretKey(itKeyId->second);
9507 throw setError(VBOX_E_INVALID_OBJECT_STATE,
9508 tr("Failed to load the decryption filter: %s"),
9509 i_vdError(vrc).c_str());
9510 }
9511
9512 task.m_pSecretKeyStore->releaseSecretKey(itKeyId->second);
9513 }
9514
9515 /* Open all media in the source chain. */
9516 MediumLockList::Base::const_iterator sourceListBegin =
9517 task.mpSourceMediumLockList->GetBegin();
9518 MediumLockList::Base::const_iterator sourceListEnd =
9519 task.mpSourceMediumLockList->GetEnd();
9520 for (MediumLockList::Base::const_iterator it = sourceListBegin;
9521 it != sourceListEnd;
9522 ++it)
9523 {
9524 const MediumLock &mediumLock = *it;
9525 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
9526 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
9527
9528 /* sanity check */
9529 Assert(pMedium->m->state == MediumState_LockedRead);
9530
9531 /* Open all media in read-only mode. */
9532 vrc = VDOpen(hdd,
9533 pMedium->m->strFormat.c_str(),
9534 pMedium->m->strLocationFull.c_str(),
9535 VD_OPEN_FLAGS_READONLY | m->uOpenFlagsDef,
9536 pMedium->m->vdImageIfaces);
9537 if (RT_FAILURE(vrc))
9538 throw setError(VBOX_E_FILE_ERROR,
9539 tr("Could not open the medium storage unit '%s'%s"),
9540 pMedium->m->strLocationFull.c_str(),
9541 i_vdError(vrc).c_str());
9542 }
9543
9544 Utf8Str targetFormat(task.mFormat->i_getId());
9545 Utf8Str targetLocation(task.mFilename);
9546 uint64_t capabilities = task.mFormat->i_getCapabilities();
9547
9548 Assert(m->state == MediumState_LockedRead);
9549
9550 /* unlock before the potentially lengthy operation */
9551 thisLock.release();
9552
9553 /* ensure the target directory exists */
9554 if (capabilities & MediumFormatCapabilities_File)
9555 {
9556 rc = VirtualBox::i_ensureFilePathExists(targetLocation,
9557 !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
9558 if (FAILED(rc))
9559 throw rc;
9560 }
9561
9562 PVBOXHDD targetHdd;
9563 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &targetHdd);
9564 ComAssertRCThrow(vrc, E_FAIL);
9565
9566 try
9567 {
9568 vrc = VDCopy(hdd,
9569 VD_LAST_IMAGE,
9570 targetHdd,
9571 targetFormat.c_str(),
9572 targetLocation.c_str(),
9573 false /* fMoveByRename */,
9574 0 /* cbSize */,
9575 task.mVariant & ~MediumVariant_NoCreateDir,
9576 NULL /* pDstUuid */,
9577 VD_OPEN_FLAGS_NORMAL | VD_OPEN_FLAGS_SEQUENTIAL,
9578 NULL /* pVDIfsOperation */,
9579 task.mVDImageIfaces,
9580 task.mVDOperationIfaces);
9581 if (RT_FAILURE(vrc))
9582 throw setError(VBOX_E_FILE_ERROR,
9583 tr("Could not create the exported medium '%s'%s"),
9584 targetLocation.c_str(), i_vdError(vrc).c_str());
9585 }
9586 catch (HRESULT aRC) { rc = aRC; }
9587
9588 VDDestroy(targetHdd);
9589 }
9590 catch (HRESULT aRC) { rc = aRC; }
9591
9592 VDDestroy(hdd);
9593 }
9594 catch (HRESULT aRC) { rc = aRC; }
9595
9596 /* Everything is explicitly unlocked when the task exits,
9597 * as the task destruction also destroys the source chain. */
9598
9599 /* Make sure the source chain is released early, otherwise it can
9600 * lead to deadlocks with concurrent IAppliance activities. */
9601 task.mpSourceMediumLockList->Clear();
9602
9603 return rc;
9604}
9605
9606/**
9607 * Implementation code for the "import" task.
9608 *
9609 * This only gets started from Medium::importFile() and always runs
9610 * asynchronously. It potentially touches the media registry, so we
9611 * always save the VirtualBox.xml file when we're done here.
9612 *
9613 * @param task
9614 * @return
9615 */
9616HRESULT Medium::i_taskImportHandler(Medium::ImportTask &task)
9617{
9618 /** @todo r=klaus The code below needs to be double checked with regard
9619 * to lock order violations, it probably causes lock order issues related
9620 * to the AutoCaller usage. */
9621 HRESULT rcTmp = S_OK;
9622
9623 const ComObjPtr<Medium> &pParent = task.mParent;
9624
9625 bool fCreatingTarget = false;
9626
9627 uint64_t size = 0, logicalSize = 0;
9628 MediumVariant_T variant = MediumVariant_Standard;
9629 bool fGenerateUuid = false;
9630
9631 try
9632 {
9633 if (!pParent.isNull())
9634 if (pParent->i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX)
9635 {
9636 AutoReadLock plock(pParent COMMA_LOCKVAL_SRC_POS);
9637 throw setError(VBOX_E_INVALID_OBJECT_STATE,
9638 tr("Cannot import image for medium '%s', because it exceeds the medium tree depth limit. Please merge some images which you no longer need"),
9639 pParent->m->strLocationFull.c_str());
9640 }
9641
9642 /* Lock all in {parent,child} order. The lock is also used as a
9643 * signal from the task initiator (which releases it only after
9644 * RTThreadCreate()) that we can start the job. */
9645 AutoMultiWriteLock2 thisLock(this, pParent COMMA_LOCKVAL_SRC_POS);
9646
9647 fCreatingTarget = m->state == MediumState_Creating;
9648
9649 /* The object may request a specific UUID (through a special form of
9650 * the setLocation() argument). Otherwise we have to generate it */
9651 Guid targetId = m->id;
9652
9653 fGenerateUuid = targetId.isZero();
9654 if (fGenerateUuid)
9655 {
9656 targetId.create();
9657 /* VirtualBox::i_registerMedium() will need UUID */
9658 unconst(m->id) = targetId;
9659 }
9660
9661
9662 PVBOXHDD hdd;
9663 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
9664 ComAssertRCThrow(vrc, E_FAIL);
9665
9666 try
9667 {
9668 /* Open source medium. */
9669 vrc = VDOpen(hdd,
9670 task.mFormat->i_getId().c_str(),
9671 task.mFilename.c_str(),
9672 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SEQUENTIAL | m->uOpenFlagsDef,
9673 task.mVDImageIfaces);
9674 if (RT_FAILURE(vrc))
9675 throw setError(VBOX_E_FILE_ERROR,
9676 tr("Could not open the medium storage unit '%s'%s"),
9677 task.mFilename.c_str(),
9678 i_vdError(vrc).c_str());
9679
9680 Utf8Str targetFormat(m->strFormat);
9681 Utf8Str targetLocation(m->strLocationFull);
9682 uint64_t capabilities = task.mFormat->i_getCapabilities();
9683
9684 Assert( m->state == MediumState_Creating
9685 || m->state == MediumState_LockedWrite);
9686 Assert( pParent.isNull()
9687 || pParent->m->state == MediumState_LockedRead);
9688
9689 /* unlock before the potentially lengthy operation */
9690 thisLock.release();
9691
9692 /* ensure the target directory exists */
9693 if (capabilities & MediumFormatCapabilities_File)
9694 {
9695 HRESULT rc = VirtualBox::i_ensureFilePathExists(targetLocation,
9696 !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
9697 if (FAILED(rc))
9698 throw rc;
9699 }
9700
9701 PVBOXHDD targetHdd;
9702 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &targetHdd);
9703 ComAssertRCThrow(vrc, E_FAIL);
9704
9705 try
9706 {
9707 /* Open all media in the target chain. */
9708 MediumLockList::Base::const_iterator targetListBegin =
9709 task.mpTargetMediumLockList->GetBegin();
9710 MediumLockList::Base::const_iterator targetListEnd =
9711 task.mpTargetMediumLockList->GetEnd();
9712 for (MediumLockList::Base::const_iterator it = targetListBegin;
9713 it != targetListEnd;
9714 ++it)
9715 {
9716 const MediumLock &mediumLock = *it;
9717 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
9718
9719 /* If the target medium is not created yet there's no
9720 * reason to open it. */
9721 if (pMedium == this && fCreatingTarget)
9722 continue;
9723
9724 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
9725
9726 /* sanity check */
9727 Assert( pMedium->m->state == MediumState_LockedRead
9728 || pMedium->m->state == MediumState_LockedWrite);
9729
9730 unsigned uOpenFlags = VD_OPEN_FLAGS_NORMAL;
9731 if (pMedium->m->state != MediumState_LockedWrite)
9732 uOpenFlags = VD_OPEN_FLAGS_READONLY;
9733 if (pMedium->m->type == MediumType_Shareable)
9734 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
9735
9736 /* Open all media in appropriate mode. */
9737 vrc = VDOpen(targetHdd,
9738 pMedium->m->strFormat.c_str(),
9739 pMedium->m->strLocationFull.c_str(),
9740 uOpenFlags | m->uOpenFlagsDef,
9741 pMedium->m->vdImageIfaces);
9742 if (RT_FAILURE(vrc))
9743 throw setError(VBOX_E_FILE_ERROR,
9744 tr("Could not open the medium storage unit '%s'%s"),
9745 pMedium->m->strLocationFull.c_str(),
9746 i_vdError(vrc).c_str());
9747 }
9748
9749 vrc = VDCopy(hdd,
9750 VD_LAST_IMAGE,
9751 targetHdd,
9752 targetFormat.c_str(),
9753 (fCreatingTarget) ? targetLocation.c_str() : (char *)NULL,
9754 false /* fMoveByRename */,
9755 0 /* cbSize */,
9756 task.mVariant & ~MediumVariant_NoCreateDir,
9757 targetId.raw(),
9758 VD_OPEN_FLAGS_NORMAL,
9759 NULL /* pVDIfsOperation */,
9760 m->vdImageIfaces,
9761 task.mVDOperationIfaces);
9762 if (RT_FAILURE(vrc))
9763 throw setError(VBOX_E_FILE_ERROR,
9764 tr("Could not create the imported medium '%s'%s"),
9765 targetLocation.c_str(), i_vdError(vrc).c_str());
9766
9767 size = VDGetFileSize(targetHdd, VD_LAST_IMAGE);
9768 logicalSize = VDGetSize(targetHdd, VD_LAST_IMAGE);
9769 unsigned uImageFlags;
9770 vrc = VDGetImageFlags(targetHdd, 0, &uImageFlags);
9771 if (RT_SUCCESS(vrc))
9772 variant = (MediumVariant_T)uImageFlags;
9773 }
9774 catch (HRESULT aRC) { rcTmp = aRC; }
9775
9776 VDDestroy(targetHdd);
9777 }
9778 catch (HRESULT aRC) { rcTmp = aRC; }
9779
9780 VDDestroy(hdd);
9781 }
9782 catch (HRESULT aRC) { rcTmp = aRC; }
9783
9784 ErrorInfoKeeper eik;
9785 MultiResult mrc(rcTmp);
9786
9787 /* Only do the parent changes for newly created media. */
9788 if (SUCCEEDED(mrc) && fCreatingTarget)
9789 {
9790 /* we set m->pParent & children() */
9791 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
9792
9793 Assert(m->pParent.isNull());
9794
9795 if (pParent)
9796 {
9797 /* Associate the imported medium with the parent and deassociate
9798 * from VirtualBox. Depth check above. */
9799 i_setParent(pParent);
9800
9801 /* register with mVirtualBox as the last step and move to
9802 * Created state only on success (leaving an orphan file is
9803 * better than breaking media registry consistency) */
9804 eik.restore();
9805 ComObjPtr<Medium> pMedium;
9806 mrc = pParent->m->pVirtualBox->i_registerMedium(this, &pMedium,
9807 treeLock);
9808 Assert(this == pMedium);
9809 eik.fetch();
9810
9811 if (FAILED(mrc))
9812 /* break parent association on failure to register */
9813 this->i_deparent(); // removes target from parent
9814 }
9815 else
9816 {
9817 /* just register */
9818 eik.restore();
9819 ComObjPtr<Medium> pMedium;
9820 mrc = m->pVirtualBox->i_registerMedium(this, &pMedium, treeLock);
9821 Assert(this == pMedium);
9822 eik.fetch();
9823 }
9824 }
9825
9826 if (fCreatingTarget)
9827 {
9828 AutoWriteLock mLock(this COMMA_LOCKVAL_SRC_POS);
9829
9830 if (SUCCEEDED(mrc))
9831 {
9832 m->state = MediumState_Created;
9833
9834 m->size = size;
9835 m->logicalSize = logicalSize;
9836 m->variant = variant;
9837 }
9838 else
9839 {
9840 /* back to NotCreated on failure */
9841 m->state = MediumState_NotCreated;
9842
9843 /* reset UUID to prevent it from being reused next time */
9844 if (fGenerateUuid)
9845 unconst(m->id).clear();
9846 }
9847 }
9848
9849 // now, at the end of this task (always asynchronous), save the settings
9850 {
9851 // save the settings
9852 i_markRegistriesModified();
9853 /* collect multiple errors */
9854 eik.restore();
9855 m->pVirtualBox->i_saveModifiedRegistries();
9856 eik.fetch();
9857 }
9858
9859 /* Everything is explicitly unlocked when the task exits,
9860 * as the task destruction also destroys the target chain. */
9861
9862 /* Make sure the target chain is released early, otherwise it can
9863 * lead to deadlocks with concurrent IAppliance activities. */
9864 task.mpTargetMediumLockList->Clear();
9865
9866 return mrc;
9867}
9868
9869/**
9870 * Sets up the encryption settings for a filter.
9871 */
9872void Medium::i_taskEncryptSettingsSetup(CryptoFilterSettings *pSettings, const char *pszCipher,
9873 const char *pszKeyStore, const char *pszPassword,
9874 bool fCreateKeyStore)
9875{
9876 pSettings->pszCipher = pszCipher;
9877 pSettings->pszPassword = pszPassword;
9878 pSettings->pszKeyStoreLoad = pszKeyStore;
9879 pSettings->fCreateKeyStore = fCreateKeyStore;
9880 pSettings->pbDek = NULL;
9881 pSettings->cbDek = 0;
9882 pSettings->vdFilterIfaces = NULL;
9883
9884 pSettings->vdIfCfg.pfnAreKeysValid = i_vdCryptoConfigAreKeysValid;
9885 pSettings->vdIfCfg.pfnQuerySize = i_vdCryptoConfigQuerySize;
9886 pSettings->vdIfCfg.pfnQuery = i_vdCryptoConfigQuery;
9887 pSettings->vdIfCfg.pfnQueryBytes = NULL;
9888
9889 pSettings->vdIfCrypto.pfnKeyRetain = i_vdCryptoKeyRetain;
9890 pSettings->vdIfCrypto.pfnKeyRelease = i_vdCryptoKeyRelease;
9891 pSettings->vdIfCrypto.pfnKeyStorePasswordRetain = i_vdCryptoKeyStorePasswordRetain;
9892 pSettings->vdIfCrypto.pfnKeyStorePasswordRelease = i_vdCryptoKeyStorePasswordRelease;
9893 pSettings->vdIfCrypto.pfnKeyStoreSave = i_vdCryptoKeyStoreSave;
9894 pSettings->vdIfCrypto.pfnKeyStoreReturnParameters = i_vdCryptoKeyStoreReturnParameters;
9895
9896 int vrc = VDInterfaceAdd(&pSettings->vdIfCfg.Core,
9897 "Medium::vdInterfaceCfgCrypto",
9898 VDINTERFACETYPE_CONFIG, pSettings,
9899 sizeof(VDINTERFACECONFIG), &pSettings->vdFilterIfaces);
9900 AssertRC(vrc);
9901
9902 vrc = VDInterfaceAdd(&pSettings->vdIfCrypto.Core,
9903 "Medium::vdInterfaceCrypto",
9904 VDINTERFACETYPE_CRYPTO, pSettings,
9905 sizeof(VDINTERFACECRYPTO), &pSettings->vdFilterIfaces);
9906 AssertRC(vrc);
9907}
9908
9909/**
9910 * Implementation code for the "encrypt" task.
9911 *
9912 * @param task
9913 * @return
9914 */
9915HRESULT Medium::i_taskEncryptHandler(Medium::EncryptTask &task)
9916{
9917 HRESULT rc = S_OK;
9918
9919 /* Lock all in {parent,child} order. The lock is also used as a
9920 * signal from the task initiator (which releases it only after
9921 * RTThreadCreate()) that we can start the job. */
9922 ComObjPtr<Medium> pBase = i_getBase();
9923 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
9924
9925 try
9926 {
9927# ifdef VBOX_WITH_EXTPACK
9928 static const Utf8Str strExtPackPuel("Oracle VM VirtualBox Extension Pack");
9929 static const char *s_pszVDPlugin = "VDPluginCrypt";
9930 ExtPackManager *pExtPackManager = m->pVirtualBox->i_getExtPackManager();
9931 if (pExtPackManager->i_isExtPackUsable(strExtPackPuel.c_str()))
9932 {
9933 /* Load the plugin */
9934 Utf8Str strPlugin;
9935 rc = pExtPackManager->i_getLibraryPathForExtPack(s_pszVDPlugin, &strExtPackPuel, &strPlugin);
9936 if (SUCCEEDED(rc))
9937 {
9938 int vrc = VDPluginLoadFromFilename(strPlugin.c_str());
9939 if (RT_FAILURE(vrc))
9940 throw setError(VBOX_E_NOT_SUPPORTED,
9941 tr("Encrypting the image failed because the encryption plugin could not be loaded (%s)"),
9942 i_vdError(vrc).c_str());
9943 }
9944 else
9945 throw setError(VBOX_E_NOT_SUPPORTED,
9946 tr("Encryption is not supported because the extension pack '%s' is missing the encryption plugin (old extension pack installed?)"),
9947 strExtPackPuel.c_str());
9948 }
9949 else
9950 throw setError(VBOX_E_NOT_SUPPORTED,
9951 tr("Encryption is not supported because the extension pack '%s' is missing"),
9952 strExtPackPuel.c_str());
9953
9954 PVBOXHDD pDisk = NULL;
9955 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &pDisk);
9956 ComAssertRCThrow(vrc, E_FAIL);
9957
9958 Medium::CryptoFilterSettings CryptoSettingsRead;
9959 Medium::CryptoFilterSettings CryptoSettingsWrite;
9960
9961 void *pvBuf = NULL;
9962 const char *pszPasswordNew = NULL;
9963 try
9964 {
9965 /* Set up disk encryption filters. */
9966 if (task.mstrCurrentPassword.isEmpty())
9967 {
9968 /*
9969 * Query whether the medium property indicating that encryption is
9970 * configured is existing.
9971 */
9972 settings::StringsMap::iterator it = pBase->m->mapProperties.find("CRYPT/KeyStore");
9973 if (it != pBase->m->mapProperties.end())
9974 throw setError(VBOX_E_PASSWORD_INCORRECT,
9975 tr("The password given for the encrypted image is incorrect"));
9976 }
9977 else
9978 {
9979 settings::StringsMap::iterator it = pBase->m->mapProperties.find("CRYPT/KeyStore");
9980 if (it == pBase->m->mapProperties.end())
9981 throw setError(VBOX_E_INVALID_OBJECT_STATE,
9982 tr("The image is not configured for encryption"));
9983
9984 i_taskEncryptSettingsSetup(&CryptoSettingsRead, NULL, it->second.c_str(), task.mstrCurrentPassword.c_str(),
9985 false /* fCreateKeyStore */);
9986 vrc = VDFilterAdd(pDisk, "CRYPT", VD_FILTER_FLAGS_READ, CryptoSettingsRead.vdFilterIfaces);
9987 if (vrc == VERR_VD_PASSWORD_INCORRECT)
9988 throw setError(VBOX_E_PASSWORD_INCORRECT,
9989 tr("The password to decrypt the image is incorrect"));
9990 else if (RT_FAILURE(vrc))
9991 throw setError(VBOX_E_INVALID_OBJECT_STATE,
9992 tr("Failed to load the decryption filter: %s"),
9993 i_vdError(vrc).c_str());
9994 }
9995
9996 if (task.mstrCipher.isNotEmpty())
9997 {
9998 if ( task.mstrNewPassword.isEmpty()
9999 && task.mstrNewPasswordId.isEmpty()
10000 && task.mstrCurrentPassword.isNotEmpty())
10001 {
10002 /* An empty password and password ID will default to the current password. */
10003 pszPasswordNew = task.mstrCurrentPassword.c_str();
10004 }
10005 else if (task.mstrNewPassword.isEmpty())
10006 throw setError(VBOX_E_OBJECT_NOT_FOUND,
10007 tr("A password must be given for the image encryption"));
10008 else if (task.mstrNewPasswordId.isEmpty())
10009 throw setError(VBOX_E_INVALID_OBJECT_STATE,
10010 tr("A valid identifier for the password must be given"));
10011 else
10012 pszPasswordNew = task.mstrNewPassword.c_str();
10013
10014 i_taskEncryptSettingsSetup(&CryptoSettingsWrite, task.mstrCipher.c_str(), NULL,
10015 pszPasswordNew, true /* fCreateKeyStore */);
10016 vrc = VDFilterAdd(pDisk, "CRYPT", VD_FILTER_FLAGS_WRITE, CryptoSettingsWrite.vdFilterIfaces);
10017 if (RT_FAILURE(vrc))
10018 throw setError(VBOX_E_INVALID_OBJECT_STATE,
10019 tr("Failed to load the encryption filter: %s"),
10020 i_vdError(vrc).c_str());
10021 }
10022 else if (task.mstrNewPasswordId.isNotEmpty() || task.mstrNewPassword.isNotEmpty())
10023 throw setError(VBOX_E_INVALID_OBJECT_STATE,
10024 tr("The password and password identifier must be empty if the output should be unencrypted"));
10025
10026 /* Open all media in the chain. */
10027 MediumLockList::Base::const_iterator mediumListBegin =
10028 task.mpMediumLockList->GetBegin();
10029 MediumLockList::Base::const_iterator mediumListEnd =
10030 task.mpMediumLockList->GetEnd();
10031 MediumLockList::Base::const_iterator mediumListLast =
10032 mediumListEnd;
10033 --mediumListLast;
10034 for (MediumLockList::Base::const_iterator it = mediumListBegin;
10035 it != mediumListEnd;
10036 ++it)
10037 {
10038 const MediumLock &mediumLock = *it;
10039 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
10040 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
10041
10042 Assert(pMedium->m->state == MediumState_LockedWrite);
10043
10044 /* Open all media but last in read-only mode. Do not handle
10045 * shareable media, as compaction and sharing are mutually
10046 * exclusive. */
10047 vrc = VDOpen(pDisk,
10048 pMedium->m->strFormat.c_str(),
10049 pMedium->m->strLocationFull.c_str(),
10050 m->uOpenFlagsDef | (it == mediumListLast ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY),
10051 pMedium->m->vdImageIfaces);
10052 if (RT_FAILURE(vrc))
10053 throw setError(VBOX_E_FILE_ERROR,
10054 tr("Could not open the medium storage unit '%s'%s"),
10055 pMedium->m->strLocationFull.c_str(),
10056 i_vdError(vrc).c_str());
10057 }
10058
10059 Assert(m->state == MediumState_LockedWrite);
10060
10061 Utf8Str location(m->strLocationFull);
10062
10063 /* unlock before the potentially lengthy operation */
10064 thisLock.release();
10065
10066 vrc = VDPrepareWithFilters(pDisk, task.mVDOperationIfaces);
10067 if (RT_FAILURE(vrc))
10068 throw setError(VBOX_E_FILE_ERROR,
10069 tr("Could not prepare disk images for encryption (%Rrc): %s"),
10070 vrc, i_vdError(vrc).c_str());
10071
10072 thisLock.acquire();
10073 /* If everything went well set the new key store. */
10074 settings::StringsMap::iterator it = pBase->m->mapProperties.find("CRYPT/KeyStore");
10075 if (it != pBase->m->mapProperties.end())
10076 pBase->m->mapProperties.erase(it);
10077
10078 /* Delete KeyId if encryption is removed or the password did change. */
10079 if ( task.mstrNewPasswordId.isNotEmpty()
10080 || task.mstrCipher.isEmpty())
10081 {
10082 it = pBase->m->mapProperties.find("CRYPT/KeyId");
10083 if (it != pBase->m->mapProperties.end())
10084 pBase->m->mapProperties.erase(it);
10085 }
10086
10087 if (CryptoSettingsWrite.pszKeyStore)
10088 {
10089 pBase->m->mapProperties["CRYPT/KeyStore"] = Utf8Str(CryptoSettingsWrite.pszKeyStore);
10090 if (task.mstrNewPasswordId.isNotEmpty())
10091 pBase->m->mapProperties["CRYPT/KeyId"] = task.mstrNewPasswordId;
10092 }
10093
10094 if (CryptoSettingsRead.pszCipherReturned)
10095 RTStrFree(CryptoSettingsRead.pszCipherReturned);
10096
10097 if (CryptoSettingsWrite.pszCipherReturned)
10098 RTStrFree(CryptoSettingsWrite.pszCipherReturned);
10099
10100 thisLock.release();
10101 pBase->i_markRegistriesModified();
10102 m->pVirtualBox->i_saveModifiedRegistries();
10103 }
10104 catch (HRESULT aRC) { rc = aRC; }
10105
10106 if (pvBuf)
10107 RTMemFree(pvBuf);
10108
10109 VDDestroy(pDisk);
10110# else
10111 throw setError(VBOX_E_NOT_SUPPORTED,
10112 tr("Encryption is not supported because extension pack support is not built in"));
10113# endif
10114 }
10115 catch (HRESULT aRC) { rc = aRC; }
10116
10117 /* Everything is explicitly unlocked when the task exits,
10118 * as the task destruction also destroys the media chain. */
10119
10120 return rc;
10121}
10122
10123/* 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