VirtualBox

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

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

Main/MediumImpl: Remove the TCP/NET interface implementation and reuse the VD provided one

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

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