VirtualBox

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

Last change on this file since 61549 was 61541, checked in by vboxsync, 9 years ago

bugref:8344. Better processing of source and destination filename. Check RAW type and CD, DVD type

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