VirtualBox

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

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

Main: bugref:6913: Added OnStorageControllerChanged to IVirtualBox events and fixed generation the medium events

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