VirtualBox

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

Last change on this file since 72883 was 72883, checked in by vboxsync, 6 years ago

Main/Medium: add API flag for creating formatted floppy images
Frontends/VBoxManage: matching CLI code

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

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