VirtualBox

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

Last change on this file since 61655 was 61645, checked in by vboxsync, 9 years ago

bugref:8344. added logic when new location consists of only a filename without extension.

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