VirtualBox

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

最後變更 在這個檔案從76215是 76215,由 vboxsync 提交於 6 年 前

Main: bugref:6913: Added generation of medium events

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

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