VirtualBox

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

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

Main: Use setErrorBoth when we've got a VBox status code handy. (The COM status codes aren't too specfic and this may help us decode error messages and provide an alternative to strstr for API clients. setErrorBoth isn't new, btw.)

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

© 2023 Oracle
ContactPrivacy policyTerms of Use