VirtualBox

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

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

Main: bugref:6913: don't try to notify changes related to NULL medium objects

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 376.1 KB
Line 
1/* $Id: MediumImpl.cpp 76239 2018-12-14 18:54:20Z 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.215389.xyz. 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 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 * @param aNotify Notify about mediums which metadatа are changed
4923 * during execution of the function.
4924 *
4925 * @note Locks this object and @a aTarget for writing.
4926 */
4927HRESULT Medium::i_createDiffStorage(ComObjPtr<Medium> &aTarget,
4928 MediumVariant_T aVariant,
4929 MediumLockList *aMediumLockList,
4930 ComObjPtr<Progress> *aProgress,
4931 bool aWait,
4932 bool aNotify)
4933{
4934 AssertReturn(!aTarget.isNull(), E_FAIL);
4935 AssertReturn(aMediumLockList, E_FAIL);
4936 AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
4937
4938 AutoCaller autoCaller(this);
4939 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4940
4941 AutoCaller targetCaller(aTarget);
4942 if (FAILED(targetCaller.rc())) return targetCaller.rc();
4943
4944 HRESULT rc = S_OK;
4945 ComObjPtr<Progress> pProgress;
4946 Medium::Task *pTask = NULL;
4947
4948 try
4949 {
4950 AutoMultiWriteLock2 alock(this, aTarget COMMA_LOCKVAL_SRC_POS);
4951
4952 ComAssertThrow( m->type != MediumType_Writethrough
4953 && m->type != MediumType_Shareable
4954 && m->type != MediumType_Readonly, E_FAIL);
4955 ComAssertThrow(m->state == MediumState_LockedRead, E_FAIL);
4956
4957 if (aTarget->m->state != MediumState_NotCreated)
4958 throw aTarget->i_setStateError();
4959
4960 /* Check that the medium is not attached to the current state of
4961 * any VM referring to it. */
4962 for (BackRefList::const_iterator it = m->backRefs.begin();
4963 it != m->backRefs.end();
4964 ++it)
4965 {
4966 if (it->fInCurState)
4967 {
4968 /* Note: when a VM snapshot is being taken, all normal media
4969 * attached to the VM in the current state will be, as an
4970 * exception, also associated with the snapshot which is about
4971 * to create (see SnapshotMachine::init()) before deassociating
4972 * them from the current state (which takes place only on
4973 * success in Machine::fixupHardDisks()), so that the size of
4974 * snapshotIds will be 1 in this case. The extra condition is
4975 * used to filter out this legal situation. */
4976 if (it->llSnapshotIds.size() == 0)
4977 throw setError(VBOX_E_INVALID_OBJECT_STATE,
4978 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"),
4979 m->strLocationFull.c_str(), it->machineId.raw());
4980
4981 Assert(it->llSnapshotIds.size() == 1);
4982 }
4983 }
4984
4985 if (aProgress != NULL)
4986 {
4987 /* use the existing progress object... */
4988 pProgress = *aProgress;
4989
4990 /* ...but create a new one if it is null */
4991 if (pProgress.isNull())
4992 {
4993 pProgress.createObject();
4994 rc = pProgress->init(m->pVirtualBox,
4995 static_cast<IMedium*>(this),
4996 BstrFmt(tr("Creating differencing medium storage unit '%s'"),
4997 aTarget->m->strLocationFull.c_str()).raw(),
4998 TRUE /* aCancelable */);
4999 if (FAILED(rc))
5000 throw rc;
5001 }
5002 }
5003
5004 /* setup task object to carry out the operation sync/async */
5005 pTask = new Medium::CreateDiffTask(this, pProgress, aTarget, aVariant,
5006 aMediumLockList,
5007 aWait /* fKeepMediumLockList */,
5008 aNotify);
5009 rc = pTask->rc();
5010 AssertComRC(rc);
5011 if (FAILED(rc))
5012 throw rc;
5013
5014 /* register a task (it will deregister itself when done) */
5015 ++m->numCreateDiffTasks;
5016 Assert(m->numCreateDiffTasks != 0); /* overflow? */
5017
5018 aTarget->m->state = MediumState_Creating;
5019 }
5020 catch (HRESULT aRC) { rc = aRC; }
5021
5022 if (SUCCEEDED(rc))
5023 {
5024 if (aWait)
5025 {
5026 rc = pTask->runNow();
5027
5028 delete pTask;
5029 }
5030 else
5031 rc = pTask->createThread();
5032
5033 if (SUCCEEDED(rc) && aProgress != NULL)
5034 *aProgress = pProgress;
5035 }
5036 else if (pTask != NULL)
5037 delete pTask;
5038
5039 return rc;
5040}
5041
5042/**
5043 * Returns a preferred format for differencing media.
5044 */
5045Utf8Str Medium::i_getPreferredDiffFormat()
5046{
5047 AutoCaller autoCaller(this);
5048 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
5049
5050 /* check that our own format supports diffs */
5051 if (!(m->formatObj->i_getCapabilities() & MediumFormatCapabilities_Differencing))
5052 {
5053 /* use the default format if not */
5054 Utf8Str tmp;
5055 m->pVirtualBox->i_getDefaultHardDiskFormat(tmp);
5056 return tmp;
5057 }
5058
5059 /* m->strFormat is const, no need to lock */
5060 return m->strFormat;
5061}
5062
5063/**
5064 * Returns a preferred variant for differencing media.
5065 */
5066MediumVariant_T Medium::i_getPreferredDiffVariant()
5067{
5068 AutoCaller autoCaller(this);
5069 AssertComRCReturn(autoCaller.rc(), MediumVariant_Standard);
5070
5071 /* check that our own format supports diffs */
5072 if (!(m->formatObj->i_getCapabilities() & MediumFormatCapabilities_Differencing))
5073 return MediumVariant_Standard;
5074
5075 /* m->variant is const, no need to lock */
5076 ULONG mediumVariantFlags = (ULONG)m->variant;
5077 mediumVariantFlags &= ~(MediumVariant_Fixed | MediumVariant_VmdkStreamOptimized);
5078 mediumVariantFlags |= MediumVariant_Diff;
5079 return (MediumVariant_T)mediumVariantFlags;
5080}
5081
5082/**
5083 * Implementation for the public Medium::Close() with the exception of calling
5084 * VirtualBox::saveRegistries(), in case someone wants to call this for several
5085 * media.
5086 *
5087 * After this returns with success, uninit() has been called on the medium, and
5088 * the object is no longer usable ("not ready" state).
5089 *
5090 * @param autoCaller AutoCaller instance which must have been created on the caller's
5091 * stack for this medium. This gets released hereupon
5092 * which the Medium instance gets uninitialized.
5093 * @return
5094 */
5095HRESULT Medium::i_close(AutoCaller &autoCaller)
5096{
5097 // must temporarily drop the caller, need the tree lock first
5098 autoCaller.release();
5099
5100 // we're accessing parent/child and backrefs, so lock the tree first, then ourselves
5101 AutoMultiWriteLock2 multilock(&m->pVirtualBox->i_getMediaTreeLockHandle(),
5102 this->lockHandle()
5103 COMMA_LOCKVAL_SRC_POS);
5104
5105 autoCaller.add();
5106 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5107
5108 LogFlowFunc(("ENTER for %s\n", i_getLocationFull().c_str()));
5109
5110 bool wasCreated = true;
5111
5112 switch (m->state)
5113 {
5114 case MediumState_NotCreated:
5115 wasCreated = false;
5116 break;
5117 case MediumState_Created:
5118 case MediumState_Inaccessible:
5119 break;
5120 default:
5121 return i_setStateError();
5122 }
5123
5124 if (m->backRefs.size() != 0)
5125 return setError(VBOX_E_OBJECT_IN_USE,
5126 tr("Medium '%s' cannot be closed because it is still attached to %d virtual machines"),
5127 m->strLocationFull.c_str(), m->backRefs.size());
5128
5129 // perform extra media-dependent close checks
5130 HRESULT rc = i_canClose();
5131 if (FAILED(rc)) return rc;
5132
5133 m->fClosing = true;
5134
5135 if (wasCreated)
5136 {
5137 // remove from the list of known media before performing actual
5138 // uninitialization (to keep the media registry consistent on
5139 // failure to do so)
5140 rc = i_unregisterWithVirtualBox();
5141 if (FAILED(rc)) return rc;
5142
5143 multilock.release();
5144 // Release the AutoCaller now, as otherwise uninit() will simply hang.
5145 // Needs to be done before mark the registries as modified and saving
5146 // the registry, as otherwise there may be a deadlock with someone else
5147 // closing this object while we're in i_saveModifiedRegistries(), which
5148 // needs the media tree lock, which the other thread holds until after
5149 // uninit() below.
5150 autoCaller.release();
5151 i_markRegistriesModified();
5152 m->pVirtualBox->i_saveModifiedRegistries();
5153 }
5154 else
5155 {
5156 multilock.release();
5157 // release the AutoCaller, as otherwise uninit() will simply hang
5158 autoCaller.release();
5159 }
5160
5161 // Keep the locks held until after uninit, as otherwise the consistency
5162 // of the medium tree cannot be guaranteed.
5163 uninit();
5164
5165 LogFlowFuncLeave();
5166
5167 return rc;
5168}
5169
5170/**
5171 * Deletes the medium storage unit.
5172 *
5173 * If @a aProgress is not NULL but the object it points to is @c null then a new
5174 * progress object will be created and assigned to @a *aProgress on success,
5175 * otherwise the existing progress object is used. If Progress is NULL, then no
5176 * progress object is created/used at all.
5177 *
5178 * When @a aWait is @c false, this method will create a thread to perform the
5179 * delete operation asynchronously and will return immediately. Otherwise, it
5180 * will perform the operation on the calling thread and will not return to the
5181 * caller until the operation is completed. Note that @a aProgress cannot be
5182 * NULL when @a aWait is @c false (this method will assert in this case).
5183 *
5184 * @param aProgress Where to find/store a Progress object to track operation
5185 * completion.
5186 * @param aWait @c true if this method should block instead of creating
5187 * an asynchronous thread.
5188 * @param aNotify Notify about mediums which metadatа are changed
5189 * during execution of the function.
5190 *
5191 * @note Locks mVirtualBox and this object for writing. Locks medium tree for
5192 * writing.
5193 */
5194HRESULT Medium::i_deleteStorage(ComObjPtr<Progress> *aProgress,
5195 bool aWait, bool aNotify)
5196{
5197 AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
5198
5199 HRESULT rc = S_OK;
5200 ComObjPtr<Progress> pProgress;
5201 Medium::Task *pTask = NULL;
5202
5203 try
5204 {
5205 /* we're accessing the media tree, and canClose() needs it too */
5206 AutoWriteLock treelock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
5207
5208 AutoCaller autoCaller(this);
5209 AssertComRCThrowRC(autoCaller.rc());
5210
5211 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5212
5213 LogFlowThisFunc(("aWait=%RTbool locationFull=%s\n", aWait, i_getLocationFull().c_str() ));
5214
5215 if ( !(m->formatObj->i_getCapabilities() & ( MediumFormatCapabilities_CreateDynamic
5216 | MediumFormatCapabilities_CreateFixed)))
5217 throw setError(VBOX_E_NOT_SUPPORTED,
5218 tr("Medium format '%s' does not support storage deletion"),
5219 m->strFormat.c_str());
5220
5221 /* Wait for a concurrently running Medium::i_queryInfo to complete. */
5222 /** @todo r=klaus would be great if this could be moved to the async
5223 * part of the operation as it can take quite a while */
5224 if (m->queryInfoRunning)
5225 {
5226 while (m->queryInfoRunning)
5227 {
5228 alock.release();
5229 autoCaller.release();
5230 treelock.release();
5231 /* Must not hold the media tree lock or the object lock, as
5232 * Medium::i_queryInfo needs this lock and thus we would run
5233 * into a deadlock here. */
5234 Assert(!m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
5235 Assert(!isWriteLockOnCurrentThread());
5236 {
5237 AutoReadLock qlock(m->queryInfoSem COMMA_LOCKVAL_SRC_POS);
5238 }
5239 treelock.acquire();
5240 autoCaller.add();
5241 AssertComRCThrowRC(autoCaller.rc());
5242 alock.acquire();
5243 }
5244 }
5245
5246 /* Note that we are fine with Inaccessible state too: a) for symmetry
5247 * with create calls and b) because it doesn't really harm to try, if
5248 * it is really inaccessible, the delete operation will fail anyway.
5249 * Accepting Inaccessible state is especially important because all
5250 * registered media are initially Inaccessible upon VBoxSVC startup
5251 * until COMGETTER(RefreshState) is called. Accept Deleting state
5252 * because some callers need to put the medium in this state early
5253 * to prevent races. */
5254 switch (m->state)
5255 {
5256 case MediumState_Created:
5257 case MediumState_Deleting:
5258 case MediumState_Inaccessible:
5259 break;
5260 default:
5261 throw i_setStateError();
5262 }
5263
5264 if (m->backRefs.size() != 0)
5265 {
5266 Utf8Str strMachines;
5267 for (BackRefList::const_iterator it = m->backRefs.begin();
5268 it != m->backRefs.end();
5269 ++it)
5270 {
5271 const BackRef &b = *it;
5272 if (strMachines.length())
5273 strMachines.append(", ");
5274 strMachines.append(b.machineId.toString().c_str());
5275 }
5276#ifdef DEBUG
5277 i_dumpBackRefs();
5278#endif
5279 throw setError(VBOX_E_OBJECT_IN_USE,
5280 tr("Cannot delete storage: medium '%s' is still attached to the following %d virtual machine(s): %s"),
5281 m->strLocationFull.c_str(),
5282 m->backRefs.size(),
5283 strMachines.c_str());
5284 }
5285
5286 rc = i_canClose();
5287 if (FAILED(rc))
5288 throw rc;
5289
5290 /* go to Deleting state, so that the medium is not actually locked */
5291 if (m->state != MediumState_Deleting)
5292 {
5293 rc = i_markForDeletion();
5294 if (FAILED(rc))
5295 throw rc;
5296 }
5297
5298 /* Build the medium lock list. */
5299 MediumLockList *pMediumLockList(new MediumLockList());
5300 alock.release();
5301 autoCaller.release();
5302 treelock.release();
5303 rc = i_createMediumLockList(true /* fFailIfInaccessible */,
5304 this /* pToLockWrite */,
5305 false /* fMediumLockWriteAll */,
5306 NULL,
5307 *pMediumLockList);
5308 treelock.acquire();
5309 autoCaller.add();
5310 AssertComRCThrowRC(autoCaller.rc());
5311 alock.acquire();
5312 if (FAILED(rc))
5313 {
5314 delete pMediumLockList;
5315 throw rc;
5316 }
5317
5318 alock.release();
5319 autoCaller.release();
5320 treelock.release();
5321 rc = pMediumLockList->Lock();
5322 treelock.acquire();
5323 autoCaller.add();
5324 AssertComRCThrowRC(autoCaller.rc());
5325 alock.acquire();
5326 if (FAILED(rc))
5327 {
5328 delete pMediumLockList;
5329 throw setError(rc,
5330 tr("Failed to lock media when deleting '%s'"),
5331 i_getLocationFull().c_str());
5332 }
5333
5334 /* try to remove from the list of known media before performing
5335 * actual deletion (we favor the consistency of the media registry
5336 * which would have been broken if unregisterWithVirtualBox() failed
5337 * after we successfully deleted the storage) */
5338 rc = i_unregisterWithVirtualBox();
5339 if (FAILED(rc))
5340 throw rc;
5341 // no longer need lock
5342 alock.release();
5343 autoCaller.release();
5344 treelock.release();
5345 i_markRegistriesModified();
5346
5347 if (aProgress != NULL)
5348 {
5349 /* use the existing progress object... */
5350 pProgress = *aProgress;
5351
5352 /* ...but create a new one if it is null */
5353 if (pProgress.isNull())
5354 {
5355 pProgress.createObject();
5356 rc = pProgress->init(m->pVirtualBox,
5357 static_cast<IMedium*>(this),
5358 BstrFmt(tr("Deleting medium storage unit '%s'"), m->strLocationFull.c_str()).raw(),
5359 FALSE /* aCancelable */);
5360 if (FAILED(rc))
5361 throw rc;
5362 }
5363 }
5364
5365 /* setup task object to carry out the operation sync/async */
5366 pTask = new Medium::DeleteTask(this, pProgress, pMediumLockList, false, aNotify);
5367 rc = pTask->rc();
5368 AssertComRC(rc);
5369 if (FAILED(rc))
5370 throw rc;
5371 }
5372 catch (HRESULT aRC) { rc = aRC; }
5373
5374 if (SUCCEEDED(rc))
5375 {
5376 if (aWait)
5377 {
5378 rc = pTask->runNow();
5379
5380 delete pTask;
5381 }
5382 else
5383 rc = pTask->createThread();
5384
5385 if (SUCCEEDED(rc) && aProgress != NULL)
5386 *aProgress = pProgress;
5387
5388 }
5389 else
5390 {
5391 if (pTask)
5392 delete pTask;
5393
5394 /* Undo deleting state if necessary. */
5395 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5396 /* Make sure that any error signalled by unmarkForDeletion() is not
5397 * ending up in the error list (if the caller uses MultiResult). It
5398 * usually is spurious, as in most cases the medium hasn't been marked
5399 * for deletion when the error was thrown above. */
5400 ErrorInfoKeeper eik;
5401 i_unmarkForDeletion();
5402 }
5403
5404 return rc;
5405}
5406
5407/**
5408 * Mark a medium for deletion.
5409 *
5410 * @note Caller must hold the write lock on this medium!
5411 */
5412HRESULT Medium::i_markForDeletion()
5413{
5414 ComAssertRet(isWriteLockOnCurrentThread(), E_FAIL);
5415 switch (m->state)
5416 {
5417 case MediumState_Created:
5418 case MediumState_Inaccessible:
5419 m->preLockState = m->state;
5420 m->state = MediumState_Deleting;
5421 return S_OK;
5422 default:
5423 return i_setStateError();
5424 }
5425}
5426
5427/**
5428 * Removes the "mark for deletion".
5429 *
5430 * @note Caller must hold the write lock on this medium!
5431 */
5432HRESULT Medium::i_unmarkForDeletion()
5433{
5434 ComAssertRet(isWriteLockOnCurrentThread(), E_FAIL);
5435 switch (m->state)
5436 {
5437 case MediumState_Deleting:
5438 m->state = m->preLockState;
5439 return S_OK;
5440 default:
5441 return i_setStateError();
5442 }
5443}
5444
5445/**
5446 * Mark a medium for deletion which is in locked state.
5447 *
5448 * @note Caller must hold the write lock on this medium!
5449 */
5450HRESULT Medium::i_markLockedForDeletion()
5451{
5452 ComAssertRet(isWriteLockOnCurrentThread(), E_FAIL);
5453 if ( ( m->state == MediumState_LockedRead
5454 || m->state == MediumState_LockedWrite)
5455 && m->preLockState == MediumState_Created)
5456 {
5457 m->preLockState = MediumState_Deleting;
5458 return S_OK;
5459 }
5460 else
5461 return i_setStateError();
5462}
5463
5464/**
5465 * Removes the "mark for deletion" for a medium in locked state.
5466 *
5467 * @note Caller must hold the write lock on this medium!
5468 */
5469HRESULT Medium::i_unmarkLockedForDeletion()
5470{
5471 ComAssertRet(isWriteLockOnCurrentThread(), E_FAIL);
5472 if ( ( m->state == MediumState_LockedRead
5473 || m->state == MediumState_LockedWrite)
5474 && m->preLockState == MediumState_Deleting)
5475 {
5476 m->preLockState = MediumState_Created;
5477 return S_OK;
5478 }
5479 else
5480 return i_setStateError();
5481}
5482
5483/**
5484 * Queries the preferred merge direction from this to the other medium, i.e.
5485 * the one which requires the least amount of I/O and therefore time and
5486 * disk consumption.
5487 *
5488 * @returns Status code.
5489 * @retval E_FAIL in case determining the merge direction fails for some reason,
5490 * for example if getting the size of the media fails. There is no
5491 * error set though and the caller is free to continue to find out
5492 * what was going wrong later. Leaves fMergeForward unset.
5493 * @retval VBOX_E_INVALID_OBJECT_STATE if both media are not related to each other
5494 * An error is set.
5495 * @param pOther The other medium to merge with.
5496 * @param fMergeForward Resulting preferred merge direction (out).
5497 */
5498HRESULT Medium::i_queryPreferredMergeDirection(const ComObjPtr<Medium> &pOther,
5499 bool &fMergeForward)
5500{
5501 AssertReturn(pOther != NULL, E_FAIL);
5502 AssertReturn(pOther != this, E_FAIL);
5503
5504 HRESULT rc = S_OK;
5505 bool fThisParent = false; /**<< Flag whether this medium is the parent of pOther. */
5506
5507 try
5508 {
5509 // locking: we need the tree lock first because we access parent pointers
5510 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
5511
5512 AutoCaller autoCaller(this);
5513 AssertComRCThrowRC(autoCaller.rc());
5514
5515 AutoCaller otherCaller(pOther);
5516 AssertComRCThrowRC(otherCaller.rc());
5517
5518 /* more sanity checking and figuring out the current merge direction */
5519 ComObjPtr<Medium> pMedium = i_getParent();
5520 while (!pMedium.isNull() && pMedium != pOther)
5521 pMedium = pMedium->i_getParent();
5522 if (pMedium == pOther)
5523 fThisParent = false;
5524 else
5525 {
5526 pMedium = pOther->i_getParent();
5527 while (!pMedium.isNull() && pMedium != this)
5528 pMedium = pMedium->i_getParent();
5529 if (pMedium == this)
5530 fThisParent = true;
5531 else
5532 {
5533 Utf8Str tgtLoc;
5534 {
5535 AutoReadLock alock(pOther COMMA_LOCKVAL_SRC_POS);
5536 tgtLoc = pOther->i_getLocationFull();
5537 }
5538
5539 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5540 throw setError(VBOX_E_INVALID_OBJECT_STATE,
5541 tr("Media '%s' and '%s' are unrelated"),
5542 m->strLocationFull.c_str(), tgtLoc.c_str());
5543 }
5544 }
5545
5546 /*
5547 * Figure out the preferred merge direction. The current way is to
5548 * get the current sizes of file based images and select the merge
5549 * direction depending on the size.
5550 *
5551 * Can't use the VD API to get current size here as the media might
5552 * be write locked by a running VM. Resort to RTFileQuerySize().
5553 */
5554 int vrc = VINF_SUCCESS;
5555 uint64_t cbMediumThis = 0;
5556 uint64_t cbMediumOther = 0;
5557
5558 if (i_isMediumFormatFile() && pOther->i_isMediumFormatFile())
5559 {
5560 vrc = RTFileQuerySize(this->i_getLocationFull().c_str(), &cbMediumThis);
5561 if (RT_SUCCESS(vrc))
5562 {
5563 vrc = RTFileQuerySize(pOther->i_getLocationFull().c_str(),
5564 &cbMediumOther);
5565 }
5566
5567 if (RT_FAILURE(vrc))
5568 rc = E_FAIL;
5569 else
5570 {
5571 /*
5572 * Check which merge direction might be more optimal.
5573 * This method is not bullet proof of course as there might
5574 * be overlapping blocks in the images so the file size is
5575 * not the best indicator but it is good enough for our purpose
5576 * and everything else is too complicated, especially when the
5577 * media are used by a running VM.
5578 */
5579
5580 uint32_t mediumVariants = MediumVariant_Fixed | MediumVariant_VmdkStreamOptimized;
5581 uint32_t mediumCaps = MediumFormatCapabilities_CreateDynamic | MediumFormatCapabilities_File;
5582
5583 bool fDynamicOther = pOther->i_getMediumFormat()->i_getCapabilities() & mediumCaps
5584 && pOther->i_getVariant() & ~mediumVariants;
5585 bool fDynamicThis = i_getMediumFormat()->i_getCapabilities() & mediumCaps
5586 && i_getVariant() & ~mediumVariants;
5587 bool fMergeIntoThis = (fDynamicThis && !fDynamicOther)
5588 || (fDynamicThis == fDynamicOther && cbMediumThis > cbMediumOther);
5589 fMergeForward = fMergeIntoThis != fThisParent;
5590 }
5591 }
5592 }
5593 catch (HRESULT aRC) { rc = aRC; }
5594
5595 return rc;
5596}
5597
5598/**
5599 * Prepares this (source) medium, target medium and all intermediate media
5600 * for the merge operation.
5601 *
5602 * This method is to be called prior to calling the #mergeTo() to perform
5603 * necessary consistency checks and place involved media to appropriate
5604 * states. If #mergeTo() is not called or fails, the state modifications
5605 * performed by this method must be undone by #i_cancelMergeTo().
5606 *
5607 * See #mergeTo() for more information about merging.
5608 *
5609 * @param pTarget Target medium.
5610 * @param aMachineId Allowed machine attachment. NULL means do not check.
5611 * @param aSnapshotId Allowed snapshot attachment. NULL or empty UUID means
5612 * do not check.
5613 * @param fLockMedia Flag whether to lock the medium lock list or not.
5614 * If set to false and the medium lock list locking fails
5615 * later you must call #i_cancelMergeTo().
5616 * @param fMergeForward Resulting merge direction (out).
5617 * @param pParentForTarget New parent for target medium after merge (out).
5618 * @param aChildrenToReparent Medium lock list containing all children of the
5619 * source which will have to be reparented to the target
5620 * after merge (out).
5621 * @param aMediumLockList Medium locking information (out).
5622 *
5623 * @note Locks medium tree for reading. Locks this object, aTarget and all
5624 * intermediate media for writing.
5625 */
5626HRESULT Medium::i_prepareMergeTo(const ComObjPtr<Medium> &pTarget,
5627 const Guid *aMachineId,
5628 const Guid *aSnapshotId,
5629 bool fLockMedia,
5630 bool &fMergeForward,
5631 ComObjPtr<Medium> &pParentForTarget,
5632 MediumLockList * &aChildrenToReparent,
5633 MediumLockList * &aMediumLockList)
5634{
5635 AssertReturn(pTarget != NULL, E_FAIL);
5636 AssertReturn(pTarget != this, E_FAIL);
5637
5638 HRESULT rc = S_OK;
5639 fMergeForward = false;
5640 pParentForTarget.setNull();
5641 Assert(aChildrenToReparent == NULL);
5642 aChildrenToReparent = NULL;
5643 Assert(aMediumLockList == NULL);
5644 aMediumLockList = NULL;
5645
5646 try
5647 {
5648 // locking: we need the tree lock first because we access parent pointers
5649 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
5650
5651 AutoCaller autoCaller(this);
5652 AssertComRCThrowRC(autoCaller.rc());
5653
5654 AutoCaller targetCaller(pTarget);
5655 AssertComRCThrowRC(targetCaller.rc());
5656
5657 /* more sanity checking and figuring out the merge direction */
5658 ComObjPtr<Medium> pMedium = i_getParent();
5659 while (!pMedium.isNull() && pMedium != pTarget)
5660 pMedium = pMedium->i_getParent();
5661 if (pMedium == pTarget)
5662 fMergeForward = false;
5663 else
5664 {
5665 pMedium = pTarget->i_getParent();
5666 while (!pMedium.isNull() && pMedium != this)
5667 pMedium = pMedium->i_getParent();
5668 if (pMedium == this)
5669 fMergeForward = true;
5670 else
5671 {
5672 Utf8Str tgtLoc;
5673 {
5674 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
5675 tgtLoc = pTarget->i_getLocationFull();
5676 }
5677
5678 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5679 throw setError(VBOX_E_INVALID_OBJECT_STATE,
5680 tr("Media '%s' and '%s' are unrelated"),
5681 m->strLocationFull.c_str(), tgtLoc.c_str());
5682 }
5683 }
5684
5685 /* Build the lock list. */
5686 aMediumLockList = new MediumLockList();
5687 targetCaller.release();
5688 autoCaller.release();
5689 treeLock.release();
5690 if (fMergeForward)
5691 rc = pTarget->i_createMediumLockList(true /* fFailIfInaccessible */,
5692 pTarget /* pToLockWrite */,
5693 false /* fMediumLockWriteAll */,
5694 NULL,
5695 *aMediumLockList);
5696 else
5697 rc = i_createMediumLockList(true /* fFailIfInaccessible */,
5698 pTarget /* pToLockWrite */,
5699 false /* fMediumLockWriteAll */,
5700 NULL,
5701 *aMediumLockList);
5702 treeLock.acquire();
5703 autoCaller.add();
5704 AssertComRCThrowRC(autoCaller.rc());
5705 targetCaller.add();
5706 AssertComRCThrowRC(targetCaller.rc());
5707 if (FAILED(rc))
5708 throw rc;
5709
5710 /* Sanity checking, must be after lock list creation as it depends on
5711 * valid medium states. The medium objects must be accessible. Only
5712 * do this if immediate locking is requested, otherwise it fails when
5713 * we construct a medium lock list for an already running VM. Snapshot
5714 * deletion uses this to simplify its life. */
5715 if (fLockMedia)
5716 {
5717 {
5718 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5719 if (m->state != MediumState_Created)
5720 throw i_setStateError();
5721 }
5722 {
5723 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
5724 if (pTarget->m->state != MediumState_Created)
5725 throw pTarget->i_setStateError();
5726 }
5727 }
5728
5729 /* check medium attachment and other sanity conditions */
5730 if (fMergeForward)
5731 {
5732 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5733 if (i_getChildren().size() > 1)
5734 {
5735 throw setError(VBOX_E_INVALID_OBJECT_STATE,
5736 tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
5737 m->strLocationFull.c_str(), i_getChildren().size());
5738 }
5739 /* One backreference is only allowed if the machine ID is not empty
5740 * and it matches the machine the medium is attached to (including
5741 * the snapshot ID if not empty). */
5742 if ( m->backRefs.size() != 0
5743 && ( !aMachineId
5744 || m->backRefs.size() != 1
5745 || aMachineId->isZero()
5746 || *i_getFirstMachineBackrefId() != *aMachineId
5747 || ( (!aSnapshotId || !aSnapshotId->isZero())
5748 && *i_getFirstMachineBackrefSnapshotId() != *aSnapshotId)))
5749 throw setError(VBOX_E_OBJECT_IN_USE,
5750 tr("Medium '%s' is attached to %d virtual machines"),
5751 m->strLocationFull.c_str(), m->backRefs.size());
5752 if (m->type == MediumType_Immutable)
5753 throw setError(VBOX_E_INVALID_OBJECT_STATE,
5754 tr("Medium '%s' is immutable"),
5755 m->strLocationFull.c_str());
5756 if (m->type == MediumType_MultiAttach)
5757 throw setError(VBOX_E_INVALID_OBJECT_STATE,
5758 tr("Medium '%s' is multi-attach"),
5759 m->strLocationFull.c_str());
5760 }
5761 else
5762 {
5763 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
5764 if (pTarget->i_getChildren().size() > 1)
5765 {
5766 throw setError(VBOX_E_OBJECT_IN_USE,
5767 tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
5768 pTarget->m->strLocationFull.c_str(),
5769 pTarget->i_getChildren().size());
5770 }
5771 if (pTarget->m->type == MediumType_Immutable)
5772 throw setError(VBOX_E_INVALID_OBJECT_STATE,
5773 tr("Medium '%s' is immutable"),
5774 pTarget->m->strLocationFull.c_str());
5775 if (pTarget->m->type == MediumType_MultiAttach)
5776 throw setError(VBOX_E_INVALID_OBJECT_STATE,
5777 tr("Medium '%s' is multi-attach"),
5778 pTarget->m->strLocationFull.c_str());
5779 }
5780 ComObjPtr<Medium> pLast(fMergeForward ? (Medium *)pTarget : this);
5781 ComObjPtr<Medium> pLastIntermediate = pLast->i_getParent();
5782 for (pLast = pLastIntermediate;
5783 !pLast.isNull() && pLast != pTarget && pLast != this;
5784 pLast = pLast->i_getParent())
5785 {
5786 AutoReadLock alock(pLast COMMA_LOCKVAL_SRC_POS);
5787 if (pLast->i_getChildren().size() > 1)
5788 {
5789 throw setError(VBOX_E_OBJECT_IN_USE,
5790 tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
5791 pLast->m->strLocationFull.c_str(),
5792 pLast->i_getChildren().size());
5793 }
5794 if (pLast->m->backRefs.size() != 0)
5795 throw setError(VBOX_E_OBJECT_IN_USE,
5796 tr("Medium '%s' is attached to %d virtual machines"),
5797 pLast->m->strLocationFull.c_str(),
5798 pLast->m->backRefs.size());
5799
5800 }
5801
5802 /* Update medium states appropriately */
5803 {
5804 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5805
5806 if (m->state == MediumState_Created)
5807 {
5808 rc = i_markForDeletion();
5809 if (FAILED(rc))
5810 throw rc;
5811 }
5812 else
5813 {
5814 if (fLockMedia)
5815 throw i_setStateError();
5816 else if ( m->state == MediumState_LockedWrite
5817 || m->state == MediumState_LockedRead)
5818 {
5819 /* Either mark it for deletion in locked state or allow
5820 * others to have done so. */
5821 if (m->preLockState == MediumState_Created)
5822 i_markLockedForDeletion();
5823 else if (m->preLockState != MediumState_Deleting)
5824 throw i_setStateError();
5825 }
5826 else
5827 throw i_setStateError();
5828 }
5829 }
5830
5831 if (fMergeForward)
5832 {
5833 /* we will need parent to reparent target */
5834 pParentForTarget = i_getParent();
5835 }
5836 else
5837 {
5838 /* we will need to reparent children of the source */
5839 aChildrenToReparent = new MediumLockList();
5840 for (MediaList::const_iterator it = i_getChildren().begin();
5841 it != i_getChildren().end();
5842 ++it)
5843 {
5844 pMedium = *it;
5845 aChildrenToReparent->Append(pMedium, true /* fLockWrite */);
5846 }
5847 if (fLockMedia && aChildrenToReparent)
5848 {
5849 targetCaller.release();
5850 autoCaller.release();
5851 treeLock.release();
5852 rc = aChildrenToReparent->Lock();
5853 treeLock.acquire();
5854 autoCaller.add();
5855 AssertComRCThrowRC(autoCaller.rc());
5856 targetCaller.add();
5857 AssertComRCThrowRC(targetCaller.rc());
5858 if (FAILED(rc))
5859 throw rc;
5860 }
5861 }
5862 for (pLast = pLastIntermediate;
5863 !pLast.isNull() && pLast != pTarget && pLast != this;
5864 pLast = pLast->i_getParent())
5865 {
5866 AutoWriteLock alock(pLast COMMA_LOCKVAL_SRC_POS);
5867 if (pLast->m->state == MediumState_Created)
5868 {
5869 rc = pLast->i_markForDeletion();
5870 if (FAILED(rc))
5871 throw rc;
5872 }
5873 else
5874 throw pLast->i_setStateError();
5875 }
5876
5877 /* Tweak the lock list in the backward merge case, as the target
5878 * isn't marked to be locked for writing yet. */
5879 if (!fMergeForward)
5880 {
5881 MediumLockList::Base::iterator lockListBegin =
5882 aMediumLockList->GetBegin();
5883 MediumLockList::Base::iterator lockListEnd =
5884 aMediumLockList->GetEnd();
5885 ++lockListEnd;
5886 for (MediumLockList::Base::iterator it = lockListBegin;
5887 it != lockListEnd;
5888 ++it)
5889 {
5890 MediumLock &mediumLock = *it;
5891 if (mediumLock.GetMedium() == pTarget)
5892 {
5893 HRESULT rc2 = mediumLock.UpdateLock(true);
5894 AssertComRC(rc2);
5895 break;
5896 }
5897 }
5898 }
5899
5900 if (fLockMedia)
5901 {
5902 targetCaller.release();
5903 autoCaller.release();
5904 treeLock.release();
5905 rc = aMediumLockList->Lock();
5906 treeLock.acquire();
5907 autoCaller.add();
5908 AssertComRCThrowRC(autoCaller.rc());
5909 targetCaller.add();
5910 AssertComRCThrowRC(targetCaller.rc());
5911 if (FAILED(rc))
5912 {
5913 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
5914 throw setError(rc,
5915 tr("Failed to lock media when merging to '%s'"),
5916 pTarget->i_getLocationFull().c_str());
5917 }
5918 }
5919 }
5920 catch (HRESULT aRC) { rc = aRC; }
5921
5922 if (FAILED(rc))
5923 {
5924 if (aMediumLockList)
5925 {
5926 delete aMediumLockList;
5927 aMediumLockList = NULL;
5928 }
5929 if (aChildrenToReparent)
5930 {
5931 delete aChildrenToReparent;
5932 aChildrenToReparent = NULL;
5933 }
5934 }
5935
5936 return rc;
5937}
5938
5939/**
5940 * Merges this medium to the specified medium which must be either its
5941 * direct ancestor or descendant.
5942 *
5943 * Given this medium is SOURCE and the specified medium is TARGET, we will
5944 * get two variants of the merge operation:
5945 *
5946 * forward merge
5947 * ------------------------->
5948 * [Extra] <- SOURCE <- Intermediate <- TARGET
5949 * Any Del Del LockWr
5950 *
5951 *
5952 * backward merge
5953 * <-------------------------
5954 * TARGET <- Intermediate <- SOURCE <- [Extra]
5955 * LockWr Del Del LockWr
5956 *
5957 * Each diagram shows the involved media on the media chain where
5958 * SOURCE and TARGET belong. Under each medium there is a state value which
5959 * the medium must have at a time of the mergeTo() call.
5960 *
5961 * The media in the square braces may be absent (e.g. when the forward
5962 * operation takes place and SOURCE is the base medium, or when the backward
5963 * merge operation takes place and TARGET is the last child in the chain) but if
5964 * they present they are involved too as shown.
5965 *
5966 * Neither the source medium nor intermediate media may be attached to
5967 * any VM directly or in the snapshot, otherwise this method will assert.
5968 *
5969 * The #i_prepareMergeTo() method must be called prior to this method to place
5970 * all involved to necessary states and perform other consistency checks.
5971 *
5972 * If @a aWait is @c true then this method will perform the operation on the
5973 * calling thread and will not return to the caller until the operation is
5974 * completed. When this method succeeds, all intermediate medium objects in
5975 * the chain will be uninitialized, the state of the target medium (and all
5976 * involved extra media) will be restored. @a aMediumLockList will not be
5977 * deleted, whether the operation is successful or not. The caller has to do
5978 * this if appropriate. Note that this (source) medium is not uninitialized
5979 * because of possible AutoCaller instances held by the caller of this method
5980 * on the current thread. It's therefore the responsibility of the caller to
5981 * call Medium::uninit() after releasing all callers.
5982 *
5983 * If @a aWait is @c false then this method will create a thread to perform the
5984 * operation asynchronously and will return immediately. If the operation
5985 * succeeds, the thread will uninitialize the source medium object and all
5986 * intermediate medium objects in the chain, reset the state of the target
5987 * medium (and all involved extra media) and delete @a aMediumLockList.
5988 * If the operation fails, the thread will only reset the states of all
5989 * involved media and delete @a aMediumLockList.
5990 *
5991 * When this method fails (regardless of the @a aWait mode), it is a caller's
5992 * responsibility to undo state changes and delete @a aMediumLockList using
5993 * #i_cancelMergeTo().
5994 *
5995 * If @a aProgress is not NULL but the object it points to is @c null then a new
5996 * progress object will be created and assigned to @a *aProgress on success,
5997 * otherwise the existing progress object is used. If Progress is NULL, then no
5998 * progress object is created/used at all. Note that @a aProgress cannot be
5999 * NULL when @a aWait is @c false (this method will assert in this case).
6000 *
6001 * @param pTarget Target medium.
6002 * @param fMergeForward Merge direction.
6003 * @param pParentForTarget New parent for target medium after merge.
6004 * @param aChildrenToReparent List of children of the source which will have
6005 * to be reparented to the target after merge.
6006 * @param aMediumLockList Medium locking information.
6007 * @param aProgress Where to find/store a Progress object to track operation
6008 * completion.
6009 * @param aWait @c true if this method should block instead of creating
6010 * an asynchronous thread.
6011 * @param aNotify Notify about mediums which metadatа are changed
6012 * during execution of the function.
6013 *
6014 * @note Locks the tree lock for writing. Locks the media from the chain
6015 * for writing.
6016 */
6017HRESULT Medium::i_mergeTo(const ComObjPtr<Medium> &pTarget,
6018 bool fMergeForward,
6019 const ComObjPtr<Medium> &pParentForTarget,
6020 MediumLockList *aChildrenToReparent,
6021 MediumLockList *aMediumLockList,
6022 ComObjPtr<Progress> *aProgress,
6023 bool aWait, bool aNotify)
6024{
6025 AssertReturn(pTarget != NULL, E_FAIL);
6026 AssertReturn(pTarget != this, E_FAIL);
6027 AssertReturn(aMediumLockList != NULL, E_FAIL);
6028 AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
6029
6030 AutoCaller autoCaller(this);
6031 AssertComRCReturnRC(autoCaller.rc());
6032
6033 AutoCaller targetCaller(pTarget);
6034 AssertComRCReturnRC(targetCaller.rc());
6035
6036 HRESULT rc = S_OK;
6037 ComObjPtr<Progress> pProgress;
6038 Medium::Task *pTask = NULL;
6039
6040 try
6041 {
6042 if (aProgress != NULL)
6043 {
6044 /* use the existing progress object... */
6045 pProgress = *aProgress;
6046
6047 /* ...but create a new one if it is null */
6048 if (pProgress.isNull())
6049 {
6050 Utf8Str tgtName;
6051 {
6052 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
6053 tgtName = pTarget->i_getName();
6054 }
6055
6056 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6057
6058 pProgress.createObject();
6059 rc = pProgress->init(m->pVirtualBox,
6060 static_cast<IMedium*>(this),
6061 BstrFmt(tr("Merging medium '%s' to '%s'"),
6062 i_getName().c_str(),
6063 tgtName.c_str()).raw(),
6064 TRUE, /* aCancelable */
6065 2, /* Number of opearations */
6066 BstrFmt(tr("Resizing medium '%s' before merge"),
6067 tgtName.c_str()).raw()
6068 );
6069 if (FAILED(rc))
6070 throw rc;
6071 }
6072 }
6073
6074 /* setup task object to carry out the operation sync/async */
6075 pTask = new Medium::MergeTask(this, pTarget, fMergeForward,
6076 pParentForTarget, aChildrenToReparent,
6077 pProgress, aMediumLockList,
6078 aWait /* fKeepMediumLockList */,
6079 aNotify);
6080 rc = pTask->rc();
6081 AssertComRC(rc);
6082 if (FAILED(rc))
6083 throw rc;
6084 }
6085 catch (HRESULT aRC) { rc = aRC; }
6086
6087 if (SUCCEEDED(rc))
6088 {
6089 if (aWait)
6090 {
6091 rc = pTask->runNow();
6092
6093 delete pTask;
6094 }
6095 else
6096 rc = pTask->createThread();
6097
6098 if (SUCCEEDED(rc) && aProgress != NULL)
6099 *aProgress = pProgress;
6100 }
6101 else if (pTask != NULL)
6102 delete pTask;
6103
6104 return rc;
6105}
6106
6107/**
6108 * Undoes what #i_prepareMergeTo() did. Must be called if #mergeTo() is not
6109 * called or fails. Frees memory occupied by @a aMediumLockList and unlocks
6110 * the medium objects in @a aChildrenToReparent.
6111 *
6112 * @param aChildrenToReparent List of children of the source which will have
6113 * to be reparented to the target after merge.
6114 * @param aMediumLockList Medium locking information.
6115 *
6116 * @note Locks the tree lock for writing. Locks the media from the chain
6117 * for writing.
6118 */
6119void Medium::i_cancelMergeTo(MediumLockList *aChildrenToReparent,
6120 MediumLockList *aMediumLockList)
6121{
6122 AutoCaller autoCaller(this);
6123 AssertComRCReturnVoid(autoCaller.rc());
6124
6125 AssertReturnVoid(aMediumLockList != NULL);
6126
6127 /* Revert media marked for deletion to previous state. */
6128 HRESULT rc;
6129 MediumLockList::Base::const_iterator mediumListBegin =
6130 aMediumLockList->GetBegin();
6131 MediumLockList::Base::const_iterator mediumListEnd =
6132 aMediumLockList->GetEnd();
6133 for (MediumLockList::Base::const_iterator it = mediumListBegin;
6134 it != mediumListEnd;
6135 ++it)
6136 {
6137 const MediumLock &mediumLock = *it;
6138 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
6139 AutoWriteLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
6140
6141 if (pMedium->m->state == MediumState_Deleting)
6142 {
6143 rc = pMedium->i_unmarkForDeletion();
6144 AssertComRC(rc);
6145 }
6146 else if ( ( pMedium->m->state == MediumState_LockedWrite
6147 || pMedium->m->state == MediumState_LockedRead)
6148 && pMedium->m->preLockState == MediumState_Deleting)
6149 {
6150 rc = pMedium->i_unmarkLockedForDeletion();
6151 AssertComRC(rc);
6152 }
6153 }
6154
6155 /* the destructor will do the work */
6156 delete aMediumLockList;
6157
6158 /* unlock the children which had to be reparented, the destructor will do
6159 * the work */
6160 if (aChildrenToReparent)
6161 delete aChildrenToReparent;
6162}
6163
6164/**
6165 * Resizes the media.
6166 *
6167 * If @a aWait is @c true then this method will perform the operation on the
6168 * calling thread and will not return to the caller until the operation is
6169 * completed. When this method succeeds, the state of the target medium (and all
6170 * involved extra media) will be restored. @a aMediumLockList will not be
6171 * deleted, whether the operation is successful or not. The caller has to do
6172 * this if appropriate.
6173 *
6174 * If @a aWait is @c false then this method will create a thread to perform the
6175 * operation asynchronously and will return immediately. The thread will reset
6176 * the state of the target medium (and all involved extra media) and delete
6177 * @a aMediumLockList.
6178 *
6179 * When this method fails (regardless of the @a aWait mode), it is a caller's
6180 * responsibility to undo state changes and delete @a aMediumLockList.
6181 *
6182 * If @a aProgress is not NULL but the object it points to is @c null then a new
6183 * progress object will be created and assigned to @a *aProgress on success,
6184 * otherwise the existing progress object is used. If Progress is NULL, then no
6185 * progress object is created/used at all. Note that @a aProgress cannot be
6186 * NULL when @a aWait is @c false (this method will assert in this case).
6187 *
6188 * @param aLogicalSize New nominal capacity of the medium in bytes.
6189 * @param aMediumLockList Medium locking information.
6190 * @param aProgress Where to find/store a Progress object to track operation
6191 * completion.
6192 * @param aWait @c true if this method should block instead of creating
6193 * an asynchronous thread.
6194 * @param aNotify Notify about mediums which metadatа are changed
6195 * during execution of the function.
6196 *
6197 * @note Locks the media from the chain for writing.
6198 */
6199
6200HRESULT Medium::i_resize(LONG64 aLogicalSize,
6201 MediumLockList *aMediumLockList,
6202 ComObjPtr<Progress> *aProgress,
6203 bool aWait,
6204 bool aNotify)
6205{
6206 AssertReturn(aMediumLockList != NULL, E_FAIL);
6207 AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
6208
6209 AutoCaller autoCaller(this);
6210 AssertComRCReturnRC(autoCaller.rc());
6211
6212 HRESULT rc = S_OK;
6213 ComObjPtr<Progress> pProgress;
6214 Medium::Task *pTask = NULL;
6215
6216 try
6217 {
6218 if (aProgress != NULL)
6219 {
6220 /* use the existing progress object... */
6221 pProgress = *aProgress;
6222
6223 /* ...but create a new one if it is null */
6224 if (pProgress.isNull())
6225 {
6226 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6227
6228 pProgress.createObject();
6229 rc = pProgress->init(m->pVirtualBox,
6230 static_cast <IMedium *>(this),
6231 BstrFmt(tr("Resizing medium '%s'"), m->strLocationFull.c_str()).raw(),
6232 TRUE /* aCancelable */);
6233 if (FAILED(rc))
6234 throw rc;
6235 }
6236 }
6237
6238 /* setup task object to carry out the operation asynchronously */
6239 pTask = new Medium::ResizeTask(this,
6240 aLogicalSize,
6241 pProgress,
6242 aMediumLockList,
6243 aWait /* fKeepMediumLockList */,
6244 aNotify);
6245 rc = pTask->rc();
6246 AssertComRC(rc);
6247 if (FAILED(rc))
6248 throw rc;
6249 }
6250 catch (HRESULT aRC) { rc = aRC; }
6251
6252 if (SUCCEEDED(rc))
6253 {
6254 if (aWait)
6255 {
6256 rc = pTask->runNow();
6257 delete pTask;
6258 }
6259 else
6260 rc = pTask->createThread();
6261
6262 if (SUCCEEDED(rc) && aProgress != NULL)
6263 *aProgress = pProgress;
6264 }
6265 else if (pTask != NULL)
6266 delete pTask;
6267
6268 return rc;
6269}
6270
6271/**
6272 * Fix the parent UUID of all children to point to this medium as their
6273 * parent.
6274 */
6275HRESULT Medium::i_fixParentUuidOfChildren(MediumLockList *pChildrenToReparent)
6276{
6277 /** @todo r=klaus The code below needs to be double checked with regard
6278 * to lock order violations, it probably causes lock order issues related
6279 * to the AutoCaller usage. Likewise the code using this method seems
6280 * problematic. */
6281 Assert(!isWriteLockOnCurrentThread());
6282 Assert(!m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
6283 MediumLockList mediumLockList;
6284 HRESULT rc = i_createMediumLockList(true /* fFailIfInaccessible */,
6285 NULL /* pToLockWrite */,
6286 false /* fMediumLockWriteAll */,
6287 this,
6288 mediumLockList);
6289 AssertComRCReturnRC(rc);
6290
6291 try
6292 {
6293 PVDISK hdd;
6294 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
6295 ComAssertRCThrow(vrc, E_FAIL);
6296
6297 try
6298 {
6299 MediumLockList::Base::iterator lockListBegin =
6300 mediumLockList.GetBegin();
6301 MediumLockList::Base::iterator lockListEnd =
6302 mediumLockList.GetEnd();
6303 for (MediumLockList::Base::iterator it = lockListBegin;
6304 it != lockListEnd;
6305 ++it)
6306 {
6307 MediumLock &mediumLock = *it;
6308 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
6309 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
6310
6311 // open the medium
6312 vrc = VDOpen(hdd,
6313 pMedium->m->strFormat.c_str(),
6314 pMedium->m->strLocationFull.c_str(),
6315 VD_OPEN_FLAGS_READONLY | m->uOpenFlagsDef,
6316 pMedium->m->vdImageIfaces);
6317 if (RT_FAILURE(vrc))
6318 throw vrc;
6319 }
6320
6321 MediumLockList::Base::iterator childrenBegin = pChildrenToReparent->GetBegin();
6322 MediumLockList::Base::iterator childrenEnd = pChildrenToReparent->GetEnd();
6323 for (MediumLockList::Base::iterator it = childrenBegin;
6324 it != childrenEnd;
6325 ++it)
6326 {
6327 Medium *pMedium = it->GetMedium();
6328 /* VD_OPEN_FLAGS_INFO since UUID is wrong yet */
6329 vrc = VDOpen(hdd,
6330 pMedium->m->strFormat.c_str(),
6331 pMedium->m->strLocationFull.c_str(),
6332 VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
6333 pMedium->m->vdImageIfaces);
6334 if (RT_FAILURE(vrc))
6335 throw vrc;
6336
6337 vrc = VDSetParentUuid(hdd, VD_LAST_IMAGE, m->id.raw());
6338 if (RT_FAILURE(vrc))
6339 throw vrc;
6340
6341 vrc = VDClose(hdd, false /* fDelete */);
6342 if (RT_FAILURE(vrc))
6343 throw vrc;
6344 }
6345 }
6346 catch (HRESULT aRC) { rc = aRC; }
6347 catch (int aVRC)
6348 {
6349 rc = setErrorBoth(E_FAIL, aVRC,
6350 tr("Could not update medium UUID references to parent '%s' (%s)"),
6351 m->strLocationFull.c_str(),
6352 i_vdError(aVRC).c_str());
6353 }
6354
6355 VDDestroy(hdd);
6356 }
6357 catch (HRESULT aRC) { rc = aRC; }
6358
6359 return rc;
6360}
6361
6362/**
6363 *
6364 * @note Similar code exists in i_taskExportHandler.
6365 */
6366HRESULT Medium::i_addRawToFss(const char *aFilename, SecretKeyStore *pKeyStore, RTVFSFSSTREAM hVfsFssDst,
6367 const ComObjPtr<Progress> &aProgress, bool fSparse)
6368{
6369 AutoCaller autoCaller(this);
6370 HRESULT hrc = autoCaller.rc();
6371 if (SUCCEEDED(hrc))
6372 {
6373 /*
6374 * Get a readonly hdd for this medium.
6375 */
6376 MediumCryptoFilterSettings CryptoSettingsRead;
6377 MediumLockList SourceMediumLockList;
6378 PVDISK pHdd;
6379 hrc = i_openForIO(false /*fWritable*/, pKeyStore, &pHdd, &SourceMediumLockList, &CryptoSettingsRead);
6380 if (SUCCEEDED(hrc))
6381 {
6382 /*
6383 * Create a VFS file interface to the HDD and attach a progress wrapper
6384 * that monitors the progress reading of the raw image. The image will
6385 * be read twice if hVfsFssDst does sparse processing.
6386 */
6387 RTVFSFILE hVfsFileDisk = NIL_RTVFSFILE;
6388 int vrc = VDCreateVfsFileFromDisk(pHdd, 0 /*fFlags*/, &hVfsFileDisk);
6389 if (RT_SUCCESS(vrc))
6390 {
6391 RTVFSFILE hVfsFileProgress = NIL_RTVFSFILE;
6392 vrc = RTVfsCreateProgressForFile(hVfsFileDisk, aProgress->i_iprtProgressCallback, &*aProgress,
6393 RTVFSPROGRESS_F_CANCELABLE | RTVFSPROGRESS_F_FORWARD_SEEK_AS_READ,
6394 VDGetSize(pHdd, VD_LAST_IMAGE) * (fSparse ? 2 : 1) /*cbExpectedRead*/,
6395 0 /*cbExpectedWritten*/, &hVfsFileProgress);
6396 RTVfsFileRelease(hVfsFileDisk);
6397 if (RT_SUCCESS(vrc))
6398 {
6399 RTVFSOBJ hVfsObj = RTVfsObjFromFile(hVfsFileProgress);
6400 RTVfsFileRelease(hVfsFileProgress);
6401
6402 vrc = RTVfsFsStrmAdd(hVfsFssDst, aFilename, hVfsObj, 0 /*fFlags*/);
6403 RTVfsObjRelease(hVfsObj);
6404 if (RT_FAILURE(vrc))
6405 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Failed to add '%s' to output (%Rrc)"), aFilename, vrc);
6406 }
6407 else
6408 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc,
6409 tr("RTVfsCreateProgressForFile failed when processing '%s' (%Rrc)"), aFilename, vrc);
6410 }
6411 else
6412 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("VDCreateVfsFileFromDisk failed for '%s' (%Rrc)"), aFilename, vrc);
6413 VDDestroy(pHdd);
6414 }
6415 }
6416 return hrc;
6417}
6418
6419/**
6420 * Used by IAppliance to export disk images.
6421 *
6422 * @param aFilename Filename to create (UTF8).
6423 * @param aFormat Medium format for creating @a aFilename.
6424 * @param aVariant Which exact image format variant to use for the
6425 * destination image.
6426 * @param pKeyStore The optional key store for decrypting the data for
6427 * encrypted media during the export.
6428 * @param hVfsIosDst The destination I/O stream object.
6429 * @param aProgress Progress object to use.
6430 * @return
6431 *
6432 * @note The source format is defined by the Medium instance.
6433 */
6434HRESULT Medium::i_exportFile(const char *aFilename,
6435 const ComObjPtr<MediumFormat> &aFormat,
6436 MediumVariant_T aVariant,
6437 SecretKeyStore *pKeyStore,
6438 RTVFSIOSTREAM hVfsIosDst,
6439 const ComObjPtr<Progress> &aProgress)
6440{
6441 AssertPtrReturn(aFilename, E_INVALIDARG);
6442 AssertReturn(aFormat.isNotNull(), E_INVALIDARG);
6443 AssertReturn(aProgress.isNotNull(), E_INVALIDARG);
6444
6445 AutoCaller autoCaller(this);
6446 HRESULT hrc = autoCaller.rc();
6447 if (SUCCEEDED(hrc))
6448 {
6449 /*
6450 * Setup VD interfaces.
6451 */
6452 PVDINTERFACE pVDImageIfaces = m->vdImageIfaces;
6453 PVDINTERFACEIO pVfsIoIf;
6454 int vrc = VDIfCreateFromVfsStream(hVfsIosDst, RTFILE_O_WRITE, &pVfsIoIf);
6455 if (RT_SUCCESS(vrc))
6456 {
6457 vrc = VDInterfaceAdd(&pVfsIoIf->Core, "Medium::ExportTaskVfsIos", VDINTERFACETYPE_IO,
6458 pVfsIoIf, sizeof(VDINTERFACEIO), &pVDImageIfaces);
6459 if (RT_SUCCESS(vrc))
6460 {
6461 /*
6462 * Get a readonly hdd for this medium (source).
6463 */
6464 MediumCryptoFilterSettings CryptoSettingsRead;
6465 MediumLockList SourceMediumLockList;
6466 PVDISK pSrcHdd;
6467 hrc = i_openForIO(false /*fWritable*/, pKeyStore, &pSrcHdd, &SourceMediumLockList, &CryptoSettingsRead);
6468 if (SUCCEEDED(hrc))
6469 {
6470 /*
6471 * Create the target medium.
6472 */
6473 Utf8Str strDstFormat(aFormat->i_getId());
6474
6475 /* ensure the target directory exists */
6476 uint64_t fDstCapabilities = aFormat->i_getCapabilities();
6477 if (fDstCapabilities & MediumFormatCapabilities_File)
6478 {
6479 Utf8Str strDstLocation(aFilename);
6480 hrc = VirtualBox::i_ensureFilePathExists(strDstLocation.c_str(),
6481 !(aVariant & MediumVariant_NoCreateDir) /* fCreate */);
6482 }
6483 if (SUCCEEDED(hrc))
6484 {
6485 PVDISK pDstHdd;
6486 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &pDstHdd);
6487 if (RT_SUCCESS(vrc))
6488 {
6489 /*
6490 * Create an interface for getting progress callbacks.
6491 */
6492 VDINTERFACEPROGRESS ProgressIf = VDINTERFACEPROGRESS_INITALIZER(aProgress->i_vdProgressCallback);
6493 PVDINTERFACE pProgress = NULL;
6494 vrc = VDInterfaceAdd(&ProgressIf.Core, "export-progress", VDINTERFACETYPE_PROGRESS,
6495 &*aProgress, sizeof(ProgressIf), &pProgress);
6496 AssertRC(vrc);
6497
6498 /*
6499 * Do the exporting.
6500 */
6501 vrc = VDCopy(pSrcHdd,
6502 VD_LAST_IMAGE,
6503 pDstHdd,
6504 strDstFormat.c_str(),
6505 aFilename,
6506 false /* fMoveByRename */,
6507 0 /* cbSize */,
6508 aVariant & ~(MediumVariant_NoCreateDir | MediumVariant_Formatted),
6509 NULL /* pDstUuid */,
6510 VD_OPEN_FLAGS_NORMAL | VD_OPEN_FLAGS_SEQUENTIAL,
6511 pProgress,
6512 pVDImageIfaces,
6513 NULL);
6514 if (RT_SUCCESS(vrc))
6515 hrc = S_OK;
6516 else
6517 hrc = setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Could not create the exported medium '%s'%s"),
6518 aFilename, i_vdError(vrc).c_str());
6519 VDDestroy(pDstHdd);
6520 }
6521 else
6522 hrc = setErrorVrc(vrc);
6523 }
6524 }
6525 VDDestroy(pSrcHdd);
6526 }
6527 else
6528 hrc = setErrorVrc(vrc, "VDInterfaceAdd -> %Rrc", vrc);
6529 VDIfDestroyFromVfsStream(pVfsIoIf);
6530 }
6531 else
6532 hrc = setErrorVrc(vrc, "VDIfCreateFromVfsStream -> %Rrc", vrc);
6533 }
6534 return hrc;
6535}
6536
6537/**
6538 * Used by IAppliance to import disk images.
6539 *
6540 * @param aFilename Filename to read (UTF8).
6541 * @param aFormat Medium format for reading @a aFilename.
6542 * @param aVariant Which exact image format variant to use
6543 * for the destination image.
6544 * @param aVfsIosSrc Handle to the source I/O stream.
6545 * @param aParent Parent medium. May be NULL.
6546 * @param aProgress Progress object to use.
6547 * @param aNotify Notify about mediums which metadatа are changed
6548 * during execution of the function.
6549 * @return
6550 * @note The destination format is defined by the Medium instance.
6551 *
6552 * @todo The only consumer of this method (Appliance::i_importOneDiskImage) is
6553 * already on a worker thread, so perhaps consider bypassing the thread
6554 * here and run in the task synchronously? VBoxSVC has enough threads as
6555 * it is...
6556 */
6557HRESULT Medium::i_importFile(const char *aFilename,
6558 const ComObjPtr<MediumFormat> &aFormat,
6559 MediumVariant_T aVariant,
6560 RTVFSIOSTREAM aVfsIosSrc,
6561 const ComObjPtr<Medium> &aParent,
6562 const ComObjPtr<Progress> &aProgress,
6563 bool aNotify)
6564{
6565 /** @todo r=klaus The code below needs to be double checked with regard
6566 * to lock order violations, it probably causes lock order issues related
6567 * to the AutoCaller usage. */
6568 AssertPtrReturn(aFilename, E_INVALIDARG);
6569 AssertReturn(!aFormat.isNull(), E_INVALIDARG);
6570 AssertReturn(!aProgress.isNull(), E_INVALIDARG);
6571
6572 AutoCaller autoCaller(this);
6573 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6574
6575 HRESULT rc = S_OK;
6576 Medium::Task *pTask = NULL;
6577
6578 try
6579 {
6580 // locking: we need the tree lock first because we access parent pointers
6581 // and we need to write-lock the media involved
6582 uint32_t cHandles = 2;
6583 LockHandle* pHandles[3] = { &m->pVirtualBox->i_getMediaTreeLockHandle(),
6584 this->lockHandle() };
6585 /* Only add parent to the lock if it is not null */
6586 if (!aParent.isNull())
6587 pHandles[cHandles++] = aParent->lockHandle();
6588 AutoWriteLock alock(cHandles,
6589 pHandles
6590 COMMA_LOCKVAL_SRC_POS);
6591
6592 if ( m->state != MediumState_NotCreated
6593 && m->state != MediumState_Created)
6594 throw i_setStateError();
6595
6596 /* Build the target lock list. */
6597 MediumLockList *pTargetMediumLockList(new MediumLockList());
6598 alock.release();
6599 rc = i_createMediumLockList(true /* fFailIfInaccessible */,
6600 this /* pToLockWrite */,
6601 false /* fMediumLockWriteAll */,
6602 aParent,
6603 *pTargetMediumLockList);
6604 alock.acquire();
6605 if (FAILED(rc))
6606 {
6607 delete pTargetMediumLockList;
6608 throw rc;
6609 }
6610
6611 alock.release();
6612 rc = pTargetMediumLockList->Lock();
6613 alock.acquire();
6614 if (FAILED(rc))
6615 {
6616 delete pTargetMediumLockList;
6617 throw setError(rc,
6618 tr("Failed to lock target media '%s'"),
6619 i_getLocationFull().c_str());
6620 }
6621
6622 /* setup task object to carry out the operation asynchronously */
6623 pTask = new Medium::ImportTask(this, aProgress, aFilename, aFormat, aVariant,
6624 aVfsIosSrc, aParent, pTargetMediumLockList, false, aNotify);
6625 rc = pTask->rc();
6626 AssertComRC(rc);
6627 if (FAILED(rc))
6628 throw rc;
6629
6630 if (m->state == MediumState_NotCreated)
6631 m->state = MediumState_Creating;
6632 }
6633 catch (HRESULT aRC) { rc = aRC; }
6634
6635 if (SUCCEEDED(rc))
6636 rc = pTask->createThread();
6637 else if (pTask != NULL)
6638 delete pTask;
6639
6640 return rc;
6641}
6642
6643/**
6644 * Internal version of the public CloneTo API which allows to enable certain
6645 * optimizations to improve speed during VM cloning.
6646 *
6647 * @param aTarget Target medium
6648 * @param aVariant Which exact image format variant to use
6649 * for the destination image.
6650 * @param aParent Parent medium. May be NULL.
6651 * @param aProgress Progress object to use.
6652 * @param idxSrcImageSame The last image in the source chain which has the
6653 * same content as the given image in the destination
6654 * chain. Use UINT32_MAX to disable this optimization.
6655 * @param idxDstImageSame The last image in the destination chain which has the
6656 * same content as the given image in the source chain.
6657 * Use UINT32_MAX to disable this optimization.
6658 * @param aNotify Notify about mediums which metadatа are changed
6659 * during execution of the function.
6660 * @return
6661 */
6662HRESULT Medium::i_cloneToEx(const ComObjPtr<Medium> &aTarget, MediumVariant_T aVariant,
6663 const ComObjPtr<Medium> &aParent, IProgress **aProgress,
6664 uint32_t idxSrcImageSame, uint32_t idxDstImageSame, bool aNotify)
6665{
6666 /** @todo r=klaus The code below needs to be double checked with regard
6667 * to lock order violations, it probably causes lock order issues related
6668 * to the AutoCaller usage. */
6669 CheckComArgNotNull(aTarget);
6670 CheckComArgOutPointerValid(aProgress);
6671 ComAssertRet(aTarget != this, E_INVALIDARG);
6672
6673 AutoCaller autoCaller(this);
6674 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6675
6676 HRESULT rc = S_OK;
6677 ComObjPtr<Progress> pProgress;
6678 Medium::Task *pTask = NULL;
6679
6680 try
6681 {
6682 // locking: we need the tree lock first because we access parent pointers
6683 // and we need to write-lock the media involved
6684 uint32_t cHandles = 3;
6685 LockHandle* pHandles[4] = { &m->pVirtualBox->i_getMediaTreeLockHandle(),
6686 this->lockHandle(),
6687 aTarget->lockHandle() };
6688 /* Only add parent to the lock if it is not null */
6689 if (!aParent.isNull())
6690 pHandles[cHandles++] = aParent->lockHandle();
6691 AutoWriteLock alock(cHandles,
6692 pHandles
6693 COMMA_LOCKVAL_SRC_POS);
6694
6695 if ( aTarget->m->state != MediumState_NotCreated
6696 && aTarget->m->state != MediumState_Created)
6697 throw aTarget->i_setStateError();
6698
6699 /* Build the source lock list. */
6700 MediumLockList *pSourceMediumLockList(new MediumLockList());
6701 alock.release();
6702 rc = i_createMediumLockList(true /* fFailIfInaccessible */,
6703 NULL /* pToLockWrite */,
6704 false /* fMediumLockWriteAll */,
6705 NULL,
6706 *pSourceMediumLockList);
6707 alock.acquire();
6708 if (FAILED(rc))
6709 {
6710 delete pSourceMediumLockList;
6711 throw rc;
6712 }
6713
6714 /* Build the target lock list (including the to-be parent chain). */
6715 MediumLockList *pTargetMediumLockList(new MediumLockList());
6716 alock.release();
6717 rc = aTarget->i_createMediumLockList(true /* fFailIfInaccessible */,
6718 aTarget /* pToLockWrite */,
6719 false /* fMediumLockWriteAll */,
6720 aParent,
6721 *pTargetMediumLockList);
6722 alock.acquire();
6723 if (FAILED(rc))
6724 {
6725 delete pSourceMediumLockList;
6726 delete pTargetMediumLockList;
6727 throw rc;
6728 }
6729
6730 alock.release();
6731 rc = pSourceMediumLockList->Lock();
6732 alock.acquire();
6733 if (FAILED(rc))
6734 {
6735 delete pSourceMediumLockList;
6736 delete pTargetMediumLockList;
6737 throw setError(rc,
6738 tr("Failed to lock source media '%s'"),
6739 i_getLocationFull().c_str());
6740 }
6741 alock.release();
6742 rc = pTargetMediumLockList->Lock();
6743 alock.acquire();
6744 if (FAILED(rc))
6745 {
6746 delete pSourceMediumLockList;
6747 delete pTargetMediumLockList;
6748 throw setError(rc,
6749 tr("Failed to lock target media '%s'"),
6750 aTarget->i_getLocationFull().c_str());
6751 }
6752
6753 pProgress.createObject();
6754 rc = pProgress->init(m->pVirtualBox,
6755 static_cast <IMedium *>(this),
6756 BstrFmt(tr("Creating clone medium '%s'"), aTarget->m->strLocationFull.c_str()).raw(),
6757 TRUE /* aCancelable */);
6758 if (FAILED(rc))
6759 {
6760 delete pSourceMediumLockList;
6761 delete pTargetMediumLockList;
6762 throw rc;
6763 }
6764
6765 /* setup task object to carry out the operation asynchronously */
6766 pTask = new Medium::CloneTask(this, pProgress, aTarget, aVariant,
6767 aParent, idxSrcImageSame,
6768 idxDstImageSame, pSourceMediumLockList,
6769 pTargetMediumLockList, false, false, aNotify);
6770 rc = pTask->rc();
6771 AssertComRC(rc);
6772 if (FAILED(rc))
6773 throw rc;
6774
6775 if (aTarget->m->state == MediumState_NotCreated)
6776 aTarget->m->state = MediumState_Creating;
6777 }
6778 catch (HRESULT aRC) { rc = aRC; }
6779
6780 if (SUCCEEDED(rc))
6781 {
6782 rc = pTask->createThread();
6783
6784 if (SUCCEEDED(rc))
6785 pProgress.queryInterfaceTo(aProgress);
6786 }
6787 else if (pTask != NULL)
6788 delete pTask;
6789
6790 return rc;
6791}
6792
6793/**
6794 * Returns the key identifier for this medium if encryption is configured.
6795 *
6796 * @returns Key identifier or empty string if no encryption is configured.
6797 */
6798const Utf8Str& Medium::i_getKeyId()
6799{
6800 ComObjPtr<Medium> pBase = i_getBase();
6801
6802 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6803
6804 settings::StringsMap::const_iterator it = pBase->m->mapProperties.find("CRYPT/KeyId");
6805 if (it == pBase->m->mapProperties.end())
6806 return Utf8Str::Empty;
6807
6808 return it->second;
6809}
6810
6811
6812/**
6813 * Returns all filter related properties.
6814 *
6815 * @returns COM status code.
6816 * @param aReturnNames Where to store the properties names on success.
6817 * @param aReturnValues Where to store the properties values on success.
6818 */
6819HRESULT Medium::i_getFilterProperties(std::vector<com::Utf8Str> &aReturnNames,
6820 std::vector<com::Utf8Str> &aReturnValues)
6821{
6822 std::vector<com::Utf8Str> aPropNames;
6823 std::vector<com::Utf8Str> aPropValues;
6824 HRESULT hrc = getProperties(Utf8Str(""), aPropNames, aPropValues);
6825
6826 if (SUCCEEDED(hrc))
6827 {
6828 unsigned cReturnSize = 0;
6829 aReturnNames.resize(0);
6830 aReturnValues.resize(0);
6831 for (unsigned idx = 0; idx < aPropNames.size(); idx++)
6832 {
6833 if (i_isPropertyForFilter(aPropNames[idx]))
6834 {
6835 aReturnNames.resize(cReturnSize + 1);
6836 aReturnValues.resize(cReturnSize + 1);
6837 aReturnNames[cReturnSize] = aPropNames[idx];
6838 aReturnValues[cReturnSize] = aPropValues[idx];
6839 cReturnSize++;
6840 }
6841 }
6842 }
6843
6844 return hrc;
6845}
6846
6847/**
6848 * Preparation to move this medium to a new location
6849 *
6850 * @param aLocation Location of the storage unit. If the location is a FS-path,
6851 * then it can be relative to the VirtualBox home directory.
6852 *
6853 * @note Must be called from under this object's write lock.
6854 */
6855HRESULT Medium::i_preparationForMoving(const Utf8Str &aLocation)
6856{
6857 HRESULT rc = E_FAIL;
6858
6859 if (i_getLocationFull() != aLocation)
6860 {
6861 m->strNewLocationFull = aLocation;
6862 m->fMoveThisMedium = true;
6863 rc = S_OK;
6864 }
6865
6866 return rc;
6867}
6868
6869/**
6870 * Checking whether current operation "moving" or not
6871 */
6872bool Medium::i_isMoveOperation(const ComObjPtr<Medium> &aTarget) const
6873{
6874 RT_NOREF(aTarget);
6875 return (m->fMoveThisMedium == true) ? true:false;
6876}
6877
6878bool Medium::i_resetMoveOperationData()
6879{
6880 m->strNewLocationFull.setNull();
6881 m->fMoveThisMedium = false;
6882 return true;
6883}
6884
6885Utf8Str Medium::i_getNewLocationForMoving() const
6886{
6887 if (m->fMoveThisMedium == true)
6888 return m->strNewLocationFull;
6889 else
6890 return Utf8Str();
6891}
6892////////////////////////////////////////////////////////////////////////////////
6893//
6894// Private methods
6895//
6896////////////////////////////////////////////////////////////////////////////////
6897
6898/**
6899 * Queries information from the medium.
6900 *
6901 * As a result of this call, the accessibility state and data members such as
6902 * size and description will be updated with the current information.
6903 *
6904 * @note This method may block during a system I/O call that checks storage
6905 * accessibility.
6906 *
6907 * @note Caller MUST NOT hold the media tree or medium lock.
6908 *
6909 * @note Locks m->pParent for reading. Locks this object for writing.
6910 *
6911 * @param fSetImageId Whether to reset the UUID contained in the image file
6912 * to the UUID in the medium instance data (see SetIDs())
6913 * @param fSetParentId Whether to reset the parent UUID contained in the image
6914 * file to the parent UUID in the medium instance data (see
6915 * SetIDs())
6916 * @param autoCaller
6917 * @return
6918 */
6919HRESULT Medium::i_queryInfo(bool fSetImageId, bool fSetParentId, AutoCaller &autoCaller)
6920{
6921 Assert(!isWriteLockOnCurrentThread());
6922 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6923
6924 if ( ( m->state != MediumState_Created
6925 && m->state != MediumState_Inaccessible
6926 && m->state != MediumState_LockedRead)
6927 || m->fClosing)
6928 return E_FAIL;
6929
6930 HRESULT rc = S_OK;
6931
6932 int vrc = VINF_SUCCESS;
6933
6934 /* check if a blocking i_queryInfo() call is in progress on some other thread,
6935 * and wait for it to finish if so instead of querying data ourselves */
6936 if (m->queryInfoRunning)
6937 {
6938 Assert( m->state == MediumState_LockedRead
6939 || m->state == MediumState_LockedWrite);
6940
6941 while (m->queryInfoRunning)
6942 {
6943 alock.release();
6944 /* must not hold the object lock now */
6945 Assert(!isWriteLockOnCurrentThread());
6946 {
6947 AutoReadLock qlock(m->queryInfoSem COMMA_LOCKVAL_SRC_POS);
6948 }
6949 alock.acquire();
6950 }
6951
6952 return S_OK;
6953 }
6954
6955 bool success = false;
6956 Utf8Str lastAccessError;
6957
6958 /* are we dealing with a new medium constructed using the existing
6959 * location? */
6960 bool isImport = m->id.isZero();
6961 unsigned uOpenFlags = VD_OPEN_FLAGS_INFO;
6962
6963 /* Note that we don't use VD_OPEN_FLAGS_READONLY when opening new
6964 * media because that would prevent necessary modifications
6965 * when opening media of some third-party formats for the first
6966 * time in VirtualBox (such as VMDK for which VDOpen() needs to
6967 * generate an UUID if it is missing) */
6968 if ( m->hddOpenMode == OpenReadOnly
6969 || m->type == MediumType_Readonly
6970 || (!isImport && !fSetImageId && !fSetParentId)
6971 )
6972 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
6973
6974 /* Open shareable medium with the appropriate flags */
6975 if (m->type == MediumType_Shareable)
6976 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
6977
6978 /* Lock the medium, which makes the behavior much more consistent, must be
6979 * done before dropping the object lock and setting queryInfoRunning. */
6980 ComPtr<IToken> pToken;
6981 if (uOpenFlags & (VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SHAREABLE))
6982 rc = LockRead(pToken.asOutParam());
6983 else
6984 rc = LockWrite(pToken.asOutParam());
6985 if (FAILED(rc)) return rc;
6986
6987 /* Copies of the input state fields which are not read-only,
6988 * as we're dropping the lock. CAUTION: be extremely careful what
6989 * you do with the contents of this medium object, as you will
6990 * create races if there are concurrent changes. */
6991 Utf8Str format(m->strFormat);
6992 Utf8Str location(m->strLocationFull);
6993 ComObjPtr<MediumFormat> formatObj = m->formatObj;
6994
6995 /* "Output" values which can't be set because the lock isn't held
6996 * at the time the values are determined. */
6997 Guid mediumId = m->id;
6998 uint64_t mediumSize = 0;
6999 uint64_t mediumLogicalSize = 0;
7000
7001 /* Flag whether a base image has a non-zero parent UUID and thus
7002 * need repairing after it was closed again. */
7003 bool fRepairImageZeroParentUuid = false;
7004
7005 ComObjPtr<VirtualBox> pVirtualBox = m->pVirtualBox;
7006
7007 /* must be set before leaving the object lock the first time */
7008 m->queryInfoRunning = true;
7009
7010 /* must leave object lock now, because a lock from a higher lock class
7011 * is needed and also a lengthy operation is coming */
7012 alock.release();
7013 autoCaller.release();
7014
7015 /* Note that taking the queryInfoSem after leaving the object lock above
7016 * can lead to short spinning of the loops waiting for i_queryInfo() to
7017 * complete. This is unavoidable since the other order causes a lock order
7018 * violation: here it would be requesting the object lock (at the beginning
7019 * of the method), then queryInfoSem, and below the other way round. */
7020 AutoWriteLock qlock(m->queryInfoSem COMMA_LOCKVAL_SRC_POS);
7021
7022 /* take the opportunity to have a media tree lock, released initially */
7023 Assert(!isWriteLockOnCurrentThread());
7024 Assert(!pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
7025 AutoWriteLock treeLock(pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
7026 treeLock.release();
7027
7028 /* re-take the caller, but not the object lock, to keep uninit away */
7029 autoCaller.add();
7030 if (FAILED(autoCaller.rc()))
7031 {
7032 m->queryInfoRunning = false;
7033 return autoCaller.rc();
7034 }
7035
7036 try
7037 {
7038 /* skip accessibility checks for host drives */
7039 if (m->hostDrive)
7040 {
7041 success = true;
7042 throw S_OK;
7043 }
7044
7045 PVDISK hdd;
7046 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
7047 ComAssertRCThrow(vrc, E_FAIL);
7048
7049 try
7050 {
7051 /** @todo This kind of opening of media is assuming that diff
7052 * media can be opened as base media. Should be documented that
7053 * it must work for all medium format backends. */
7054 vrc = VDOpen(hdd,
7055 format.c_str(),
7056 location.c_str(),
7057 uOpenFlags | m->uOpenFlagsDef,
7058 m->vdImageIfaces);
7059 if (RT_FAILURE(vrc))
7060 {
7061 lastAccessError = Utf8StrFmt(tr("Could not open the medium '%s'%s"),
7062 location.c_str(), i_vdError(vrc).c_str());
7063 throw S_OK;
7064 }
7065
7066 if (formatObj->i_getCapabilities() & MediumFormatCapabilities_Uuid)
7067 {
7068 /* Modify the UUIDs if necessary. The associated fields are
7069 * not modified by other code, so no need to copy. */
7070 if (fSetImageId)
7071 {
7072 alock.acquire();
7073 vrc = VDSetUuid(hdd, 0, m->uuidImage.raw());
7074 alock.release();
7075 if (RT_FAILURE(vrc))
7076 {
7077 lastAccessError = Utf8StrFmt(tr("Could not update the UUID of medium '%s'%s"),
7078 location.c_str(), i_vdError(vrc).c_str());
7079 throw S_OK;
7080 }
7081 mediumId = m->uuidImage;
7082 }
7083 if (fSetParentId)
7084 {
7085 alock.acquire();
7086 vrc = VDSetParentUuid(hdd, 0, m->uuidParentImage.raw());
7087 alock.release();
7088 if (RT_FAILURE(vrc))
7089 {
7090 lastAccessError = Utf8StrFmt(tr("Could not update the parent UUID of medium '%s'%s"),
7091 location.c_str(), i_vdError(vrc).c_str());
7092 throw S_OK;
7093 }
7094 }
7095 /* zap the information, these are no long-term members */
7096 alock.acquire();
7097 unconst(m->uuidImage).clear();
7098 unconst(m->uuidParentImage).clear();
7099 alock.release();
7100
7101 /* check the UUID */
7102 RTUUID uuid;
7103 vrc = VDGetUuid(hdd, 0, &uuid);
7104 ComAssertRCThrow(vrc, E_FAIL);
7105
7106 if (isImport)
7107 {
7108 mediumId = uuid;
7109
7110 if (mediumId.isZero() && (m->hddOpenMode == OpenReadOnly))
7111 // only when importing a VDMK that has no UUID, create one in memory
7112 mediumId.create();
7113 }
7114 else
7115 {
7116 Assert(!mediumId.isZero());
7117
7118 if (mediumId != uuid)
7119 {
7120 /** @todo r=klaus this always refers to VirtualBox.xml as the medium registry, even for new VMs */
7121 lastAccessError = Utf8StrFmt(
7122 tr("UUID {%RTuuid} of the medium '%s' does not match the value {%RTuuid} stored in the media registry ('%s')"),
7123 &uuid,
7124 location.c_str(),
7125 mediumId.raw(),
7126 pVirtualBox->i_settingsFilePath().c_str());
7127 throw S_OK;
7128 }
7129 }
7130 }
7131 else
7132 {
7133 /* the backend does not support storing UUIDs within the
7134 * underlying storage so use what we store in XML */
7135
7136 if (fSetImageId)
7137 {
7138 /* set the UUID if an API client wants to change it */
7139 alock.acquire();
7140 mediumId = m->uuidImage;
7141 alock.release();
7142 }
7143 else if (isImport)
7144 {
7145 /* generate an UUID for an imported UUID-less medium */
7146 mediumId.create();
7147 }
7148 }
7149
7150 /* set the image uuid before the below parent uuid handling code
7151 * might place it somewhere in the media tree, so that the medium
7152 * UUID is valid at this point */
7153 alock.acquire();
7154 if (isImport || fSetImageId)
7155 unconst(m->id) = mediumId;
7156 alock.release();
7157
7158 /* get the medium variant */
7159 unsigned uImageFlags;
7160 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
7161 ComAssertRCThrow(vrc, E_FAIL);
7162 alock.acquire();
7163 m->variant = (MediumVariant_T)uImageFlags;
7164 alock.release();
7165
7166 /* check/get the parent uuid and update corresponding state */
7167 if (uImageFlags & VD_IMAGE_FLAGS_DIFF)
7168 {
7169 RTUUID parentId;
7170 vrc = VDGetParentUuid(hdd, 0, &parentId);
7171 ComAssertRCThrow(vrc, E_FAIL);
7172
7173 /* streamOptimized VMDK images are only accepted as base
7174 * images, as this allows automatic repair of OVF appliances.
7175 * Since such images don't support random writes they will not
7176 * be created for diff images. Only an overly smart user might
7177 * manually create this case. Too bad for him. */
7178 if ( (isImport || fSetParentId)
7179 && !(uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED))
7180 {
7181 /* the parent must be known to us. Note that we freely
7182 * call locking methods of mVirtualBox and parent, as all
7183 * relevant locks must be already held. There may be no
7184 * concurrent access to the just opened medium on other
7185 * threads yet (and init() will fail if this method reports
7186 * MediumState_Inaccessible) */
7187
7188 ComObjPtr<Medium> pParent;
7189 if (RTUuidIsNull(&parentId))
7190 rc = VBOX_E_OBJECT_NOT_FOUND;
7191 else
7192 rc = pVirtualBox->i_findHardDiskById(Guid(parentId), false /* aSetError */, &pParent);
7193 if (FAILED(rc))
7194 {
7195 if (fSetImageId && !fSetParentId)
7196 {
7197 /* If the image UUID gets changed for an existing
7198 * image then the parent UUID can be stale. In such
7199 * cases clear the parent information. The parent
7200 * information may/will be re-set later if the
7201 * API client wants to adjust a complete medium
7202 * hierarchy one by one. */
7203 rc = S_OK;
7204 alock.acquire();
7205 RTUuidClear(&parentId);
7206 vrc = VDSetParentUuid(hdd, 0, &parentId);
7207 alock.release();
7208 ComAssertRCThrow(vrc, E_FAIL);
7209 }
7210 else
7211 {
7212 lastAccessError = Utf8StrFmt(tr("Parent medium with UUID {%RTuuid} of the medium '%s' is not found in the media registry ('%s')"),
7213 &parentId, location.c_str(),
7214 pVirtualBox->i_settingsFilePath().c_str());
7215 throw S_OK;
7216 }
7217 }
7218
7219 /* must drop the caller before taking the tree lock */
7220 autoCaller.release();
7221 /* we set m->pParent & children() */
7222 treeLock.acquire();
7223 autoCaller.add();
7224 if (FAILED(autoCaller.rc()))
7225 throw autoCaller.rc();
7226
7227 if (m->pParent)
7228 i_deparent();
7229
7230 if (!pParent.isNull())
7231 if (pParent->i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX)
7232 {
7233 AutoReadLock plock(pParent COMMA_LOCKVAL_SRC_POS);
7234 throw setError(VBOX_E_INVALID_OBJECT_STATE,
7235 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"),
7236 pParent->m->strLocationFull.c_str());
7237 }
7238 i_setParent(pParent);
7239
7240 treeLock.release();
7241 }
7242 else
7243 {
7244 /* must drop the caller before taking the tree lock */
7245 autoCaller.release();
7246 /* we access m->pParent */
7247 treeLock.acquire();
7248 autoCaller.add();
7249 if (FAILED(autoCaller.rc()))
7250 throw autoCaller.rc();
7251
7252 /* check that parent UUIDs match. Note that there's no need
7253 * for the parent's AutoCaller (our lifetime is bound to
7254 * it) */
7255
7256 if (m->pParent.isNull())
7257 {
7258 /* Due to a bug in VDCopy() in VirtualBox 3.0.0-3.0.14
7259 * and 3.1.0-3.1.8 there are base images out there
7260 * which have a non-zero parent UUID. No point in
7261 * complaining about them, instead automatically
7262 * repair the problem. Later we can bring back the
7263 * error message, but we should wait until really
7264 * most users have repaired their images, either with
7265 * VBoxFixHdd or this way. */
7266#if 1
7267 fRepairImageZeroParentUuid = true;
7268#else /* 0 */
7269 lastAccessError = Utf8StrFmt(
7270 tr("Medium type of '%s' is differencing but it is not associated with any parent medium in the media registry ('%s')"),
7271 location.c_str(),
7272 pVirtualBox->settingsFilePath().c_str());
7273 treeLock.release();
7274 throw S_OK;
7275#endif /* 0 */
7276 }
7277
7278 {
7279 autoCaller.release();
7280 AutoReadLock parentLock(m->pParent COMMA_LOCKVAL_SRC_POS);
7281 autoCaller.add();
7282 if (FAILED(autoCaller.rc()))
7283 throw autoCaller.rc();
7284
7285 if ( !fRepairImageZeroParentUuid
7286 && m->pParent->i_getState() != MediumState_Inaccessible
7287 && m->pParent->i_getId() != parentId)
7288 {
7289 /** @todo r=klaus this always refers to VirtualBox.xml as the medium registry, even for new VMs */
7290 lastAccessError = Utf8StrFmt(
7291 tr("Parent UUID {%RTuuid} of the medium '%s' does not match UUID {%RTuuid} of its parent medium stored in the media registry ('%s')"),
7292 &parentId, location.c_str(),
7293 m->pParent->i_getId().raw(),
7294 pVirtualBox->i_settingsFilePath().c_str());
7295 parentLock.release();
7296 treeLock.release();
7297 throw S_OK;
7298 }
7299 }
7300
7301 /// @todo NEWMEDIA what to do if the parent is not
7302 /// accessible while the diff is? Probably nothing. The
7303 /// real code will detect the mismatch anyway.
7304
7305 treeLock.release();
7306 }
7307 }
7308
7309 mediumSize = VDGetFileSize(hdd, 0);
7310 mediumLogicalSize = VDGetSize(hdd, 0);
7311
7312 success = true;
7313 }
7314 catch (HRESULT aRC)
7315 {
7316 rc = aRC;
7317 }
7318
7319 vrc = VDDestroy(hdd);
7320 if (RT_FAILURE(vrc))
7321 {
7322 lastAccessError = Utf8StrFmt(tr("Could not update and close the medium '%s'%s"),
7323 location.c_str(), i_vdError(vrc).c_str());
7324 success = false;
7325 throw S_OK;
7326 }
7327 }
7328 catch (HRESULT aRC)
7329 {
7330 rc = aRC;
7331 }
7332
7333 autoCaller.release();
7334 treeLock.acquire();
7335 autoCaller.add();
7336 if (FAILED(autoCaller.rc()))
7337 {
7338 m->queryInfoRunning = false;
7339 return autoCaller.rc();
7340 }
7341 alock.acquire();
7342
7343 if (success)
7344 {
7345 m->size = mediumSize;
7346 m->logicalSize = mediumLogicalSize;
7347 m->strLastAccessError.setNull();
7348 }
7349 else
7350 {
7351 m->strLastAccessError = lastAccessError;
7352 Log1WarningFunc(("'%s' is not accessible (error='%s', rc=%Rhrc, vrc=%Rrc)\n",
7353 location.c_str(), m->strLastAccessError.c_str(), rc, vrc));
7354 }
7355
7356 /* Set the proper state according to the result of the check */
7357 if (success)
7358 m->preLockState = MediumState_Created;
7359 else
7360 m->preLockState = MediumState_Inaccessible;
7361
7362 /* unblock anyone waiting for the i_queryInfo results */
7363 qlock.release();
7364 m->queryInfoRunning = false;
7365
7366 pToken->Abandon();
7367 pToken.setNull();
7368
7369 if (FAILED(rc))
7370 return rc;
7371
7372 /* If this is a base image which incorrectly has a parent UUID set,
7373 * repair the image now by zeroing the parent UUID. This is only done
7374 * when we have structural information from a config file, on import
7375 * this is not possible. If someone would accidentally call openMedium
7376 * with a diff image before the base is registered this would destroy
7377 * the diff. Not acceptable. */
7378 do
7379 {
7380 if (fRepairImageZeroParentUuid)
7381 {
7382 rc = LockWrite(pToken.asOutParam());
7383 if (FAILED(rc))
7384 break;
7385
7386 alock.release();
7387
7388 try
7389 {
7390 PVDISK hdd;
7391 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
7392 ComAssertRCThrow(vrc, E_FAIL);
7393
7394 try
7395 {
7396 vrc = VDOpen(hdd,
7397 format.c_str(),
7398 location.c_str(),
7399 (uOpenFlags & ~VD_OPEN_FLAGS_READONLY) | m->uOpenFlagsDef,
7400 m->vdImageIfaces);
7401 if (RT_FAILURE(vrc))
7402 throw S_OK;
7403
7404 RTUUID zeroParentUuid;
7405 RTUuidClear(&zeroParentUuid);
7406 vrc = VDSetParentUuid(hdd, 0, &zeroParentUuid);
7407 ComAssertRCThrow(vrc, E_FAIL);
7408 }
7409 catch (HRESULT aRC)
7410 {
7411 rc = aRC;
7412 }
7413
7414 VDDestroy(hdd);
7415 }
7416 catch (HRESULT aRC)
7417 {
7418 rc = aRC;
7419 }
7420
7421 pToken->Abandon();
7422 pToken.setNull();
7423 if (FAILED(rc))
7424 break;
7425 }
7426 } while(0);
7427
7428 return rc;
7429}
7430
7431/**
7432 * Performs extra checks if the medium can be closed and returns S_OK in
7433 * this case. Otherwise, returns a respective error message. Called by
7434 * Close() under the medium tree lock and the medium lock.
7435 *
7436 * @note Also reused by Medium::Reset().
7437 *
7438 * @note Caller must hold the media tree write lock!
7439 */
7440HRESULT Medium::i_canClose()
7441{
7442 Assert(m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
7443
7444 if (i_getChildren().size() != 0)
7445 return setError(VBOX_E_OBJECT_IN_USE,
7446 tr("Cannot close medium '%s' because it has %d child media"),
7447 m->strLocationFull.c_str(), i_getChildren().size());
7448
7449 return S_OK;
7450}
7451
7452/**
7453 * Unregisters this medium with mVirtualBox. Called by close() under the medium tree lock.
7454 *
7455 * @note Caller must have locked the media tree lock for writing!
7456 */
7457HRESULT Medium::i_unregisterWithVirtualBox()
7458{
7459 /* Note that we need to de-associate ourselves from the parent to let
7460 * VirtualBox::i_unregisterMedium() properly save the registry */
7461
7462 /* we modify m->pParent and access children */
7463 Assert(m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
7464
7465 Medium *pParentBackup = m->pParent;
7466 AssertReturn(i_getChildren().size() == 0, E_FAIL);
7467 if (m->pParent)
7468 i_deparent();
7469
7470 HRESULT rc = m->pVirtualBox->i_unregisterMedium(this);
7471 if (FAILED(rc))
7472 {
7473 if (pParentBackup)
7474 {
7475 // re-associate with the parent as we are still relatives in the registry
7476 i_setParent(pParentBackup);
7477 }
7478 }
7479
7480 return rc;
7481}
7482
7483/**
7484 * Like SetProperty but do not trigger a settings store. Only for internal use!
7485 */
7486HRESULT Medium::i_setPropertyDirect(const Utf8Str &aName, const Utf8Str &aValue)
7487{
7488 AutoCaller autoCaller(this);
7489 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7490
7491 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
7492
7493 switch (m->state)
7494 {
7495 case MediumState_Created:
7496 case MediumState_Inaccessible:
7497 break;
7498 default:
7499 return i_setStateError();
7500 }
7501
7502 m->mapProperties[aName] = aValue;
7503
7504 return S_OK;
7505}
7506
7507/**
7508 * Sets the extended error info according to the current media state.
7509 *
7510 * @note Must be called from under this object's write or read lock.
7511 */
7512HRESULT Medium::i_setStateError()
7513{
7514 HRESULT rc = E_FAIL;
7515
7516 switch (m->state)
7517 {
7518 case MediumState_NotCreated:
7519 {
7520 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
7521 tr("Storage for the medium '%s' is not created"),
7522 m->strLocationFull.c_str());
7523 break;
7524 }
7525 case MediumState_Created:
7526 {
7527 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
7528 tr("Storage for the medium '%s' is already created"),
7529 m->strLocationFull.c_str());
7530 break;
7531 }
7532 case MediumState_LockedRead:
7533 {
7534 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
7535 tr("Medium '%s' is locked for reading by another task"),
7536 m->strLocationFull.c_str());
7537 break;
7538 }
7539 case MediumState_LockedWrite:
7540 {
7541 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
7542 tr("Medium '%s' is locked for writing by another task"),
7543 m->strLocationFull.c_str());
7544 break;
7545 }
7546 case MediumState_Inaccessible:
7547 {
7548 /* be in sync with Console::powerUpThread() */
7549 if (!m->strLastAccessError.isEmpty())
7550 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
7551 tr("Medium '%s' is not accessible. %s"),
7552 m->strLocationFull.c_str(), m->strLastAccessError.c_str());
7553 else
7554 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
7555 tr("Medium '%s' is not accessible"),
7556 m->strLocationFull.c_str());
7557 break;
7558 }
7559 case MediumState_Creating:
7560 {
7561 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
7562 tr("Storage for the medium '%s' is being created"),
7563 m->strLocationFull.c_str());
7564 break;
7565 }
7566 case MediumState_Deleting:
7567 {
7568 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
7569 tr("Storage for the medium '%s' is being deleted"),
7570 m->strLocationFull.c_str());
7571 break;
7572 }
7573 default:
7574 {
7575 AssertFailed();
7576 break;
7577 }
7578 }
7579
7580 return rc;
7581}
7582
7583/**
7584 * Sets the value of m->strLocationFull. The given location must be a fully
7585 * qualified path; relative paths are not supported here.
7586 *
7587 * As a special exception, if the specified location is a file path that ends with '/'
7588 * then the file name part will be generated by this method automatically in the format
7589 * '{\<uuid\>}.\<ext\>' where \<uuid\> is a fresh UUID that this method will generate
7590 * and assign to this medium, and \<ext\> is the default extension for this
7591 * medium's storage format. Note that this procedure requires the media state to
7592 * be NotCreated and will return a failure otherwise.
7593 *
7594 * @param aLocation Location of the storage unit. If the location is a FS-path,
7595 * then it can be relative to the VirtualBox home directory.
7596 * @param aFormat Optional fallback format if it is an import and the format
7597 * cannot be determined.
7598 *
7599 * @note Must be called from under this object's write lock.
7600 */
7601HRESULT Medium::i_setLocation(const Utf8Str &aLocation,
7602 const Utf8Str &aFormat /* = Utf8Str::Empty */)
7603{
7604 AssertReturn(!aLocation.isEmpty(), E_FAIL);
7605
7606 AutoCaller autoCaller(this);
7607 AssertComRCReturnRC(autoCaller.rc());
7608
7609 /* formatObj may be null only when initializing from an existing path and
7610 * no format is known yet */
7611 AssertReturn( (!m->strFormat.isEmpty() && !m->formatObj.isNull())
7612 || ( getObjectState().getState() == ObjectState::InInit
7613 && m->state != MediumState_NotCreated
7614 && m->id.isZero()
7615 && m->strFormat.isEmpty()
7616 && m->formatObj.isNull()),
7617 E_FAIL);
7618
7619 /* are we dealing with a new medium constructed using the existing
7620 * location? */
7621 bool isImport = m->strFormat.isEmpty();
7622
7623 if ( isImport
7624 || ( (m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File)
7625 && !m->hostDrive))
7626 {
7627 Guid id;
7628
7629 Utf8Str locationFull(aLocation);
7630
7631 if (m->state == MediumState_NotCreated)
7632 {
7633 /* must be a file (formatObj must be already known) */
7634 Assert(m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File);
7635
7636 if (RTPathFilename(aLocation.c_str()) == NULL)
7637 {
7638 /* no file name is given (either an empty string or ends with a
7639 * slash), generate a new UUID + file name if the state allows
7640 * this */
7641
7642 ComAssertMsgRet(!m->formatObj->i_getFileExtensions().empty(),
7643 ("Must be at least one extension if it is MediumFormatCapabilities_File\n"),
7644 E_FAIL);
7645
7646 Utf8Str strExt = m->formatObj->i_getFileExtensions().front();
7647 ComAssertMsgRet(!strExt.isEmpty(),
7648 ("Default extension must not be empty\n"),
7649 E_FAIL);
7650
7651 id.create();
7652
7653 locationFull = Utf8StrFmt("%s{%RTuuid}.%s",
7654 aLocation.c_str(), id.raw(), strExt.c_str());
7655 }
7656 }
7657
7658 // we must always have full paths now (if it refers to a file)
7659 if ( ( m->formatObj.isNull()
7660 || m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File)
7661 && !RTPathStartsWithRoot(locationFull.c_str()))
7662 return setError(VBOX_E_FILE_ERROR,
7663 tr("The given path '%s' is not fully qualified"),
7664 locationFull.c_str());
7665
7666 /* detect the backend from the storage unit if importing */
7667 if (isImport)
7668 {
7669 VDTYPE enmType = VDTYPE_INVALID;
7670 char *backendName = NULL;
7671
7672 int vrc = VINF_SUCCESS;
7673
7674 /* is it a file? */
7675 {
7676 RTFILE file;
7677 vrc = RTFileOpen(&file, locationFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
7678 if (RT_SUCCESS(vrc))
7679 RTFileClose(file);
7680 }
7681 if (RT_SUCCESS(vrc))
7682 {
7683 vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
7684 locationFull.c_str(), &backendName, &enmType);
7685 }
7686 else if ( vrc != VERR_FILE_NOT_FOUND
7687 && vrc != VERR_PATH_NOT_FOUND
7688 && vrc != VERR_ACCESS_DENIED
7689 && locationFull != aLocation)
7690 {
7691 /* assume it's not a file, restore the original location */
7692 locationFull = aLocation;
7693 vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
7694 locationFull.c_str(), &backendName, &enmType);
7695 }
7696
7697 if (RT_FAILURE(vrc))
7698 {
7699 if (vrc == VERR_ACCESS_DENIED)
7700 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
7701 tr("Permission problem accessing the file for the medium '%s' (%Rrc)"),
7702 locationFull.c_str(), vrc);
7703 else if (vrc == VERR_FILE_NOT_FOUND || vrc == VERR_PATH_NOT_FOUND)
7704 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
7705 tr("Could not find file for the medium '%s' (%Rrc)"),
7706 locationFull.c_str(), vrc);
7707 else if (aFormat.isEmpty())
7708 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
7709 tr("Could not get the storage format of the medium '%s' (%Rrc)"),
7710 locationFull.c_str(), vrc);
7711 else
7712 {
7713 HRESULT rc = i_setFormat(aFormat);
7714 /* setFormat() must not fail since we've just used the backend so
7715 * the format object must be there */
7716 AssertComRCReturnRC(rc);
7717 }
7718 }
7719 else if ( enmType == VDTYPE_INVALID
7720 || m->devType != i_convertToDeviceType(enmType))
7721 {
7722 /*
7723 * The user tried to use a image as a device which is not supported
7724 * by the backend.
7725 */
7726 return setError(E_FAIL,
7727 tr("The medium '%s' can't be used as the requested device type"),
7728 locationFull.c_str());
7729 }
7730 else
7731 {
7732 ComAssertRet(backendName != NULL && *backendName != '\0', E_FAIL);
7733
7734 HRESULT rc = i_setFormat(backendName);
7735 RTStrFree(backendName);
7736
7737 /* setFormat() must not fail since we've just used the backend so
7738 * the format object must be there */
7739 AssertComRCReturnRC(rc);
7740 }
7741 }
7742
7743 m->strLocationFull = locationFull;
7744
7745 /* is it still a file? */
7746 if ( (m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File)
7747 && (m->state == MediumState_NotCreated)
7748 )
7749 /* assign a new UUID (this UUID will be used when calling
7750 * VDCreateBase/VDCreateDiff as a wanted UUID). Note that we
7751 * also do that if we didn't generate it to make sure it is
7752 * either generated by us or reset to null */
7753 unconst(m->id) = id;
7754 }
7755 else
7756 m->strLocationFull = aLocation;
7757
7758 return S_OK;
7759}
7760
7761/**
7762 * Checks that the format ID is valid and sets it on success.
7763 *
7764 * Note that this method will caller-reference the format object on success!
7765 * This reference must be released somewhere to let the MediumFormat object be
7766 * uninitialized.
7767 *
7768 * @note Must be called from under this object's write lock.
7769 */
7770HRESULT Medium::i_setFormat(const Utf8Str &aFormat)
7771{
7772 /* get the format object first */
7773 {
7774 SystemProperties *pSysProps = m->pVirtualBox->i_getSystemProperties();
7775 AutoReadLock propsLock(pSysProps COMMA_LOCKVAL_SRC_POS);
7776
7777 unconst(m->formatObj) = pSysProps->i_mediumFormat(aFormat);
7778 if (m->formatObj.isNull())
7779 return setError(E_INVALIDARG,
7780 tr("Invalid medium storage format '%s'"),
7781 aFormat.c_str());
7782
7783 /* get properties (preinsert them as keys in the map). Note that the
7784 * map doesn't grow over the object life time since the set of
7785 * properties is meant to be constant. */
7786
7787 Assert(m->mapProperties.empty());
7788
7789 for (MediumFormat::PropertyArray::const_iterator it = m->formatObj->i_getProperties().begin();
7790 it != m->formatObj->i_getProperties().end();
7791 ++it)
7792 {
7793 m->mapProperties.insert(std::make_pair(it->strName, Utf8Str::Empty));
7794 }
7795 }
7796
7797 unconst(m->strFormat) = aFormat;
7798
7799 return S_OK;
7800}
7801
7802/**
7803 * Converts the Medium device type to the VD type.
7804 */
7805VDTYPE Medium::i_convertDeviceType()
7806{
7807 VDTYPE enmType;
7808
7809 switch (m->devType)
7810 {
7811 case DeviceType_HardDisk:
7812 enmType = VDTYPE_HDD;
7813 break;
7814 case DeviceType_DVD:
7815 enmType = VDTYPE_OPTICAL_DISC;
7816 break;
7817 case DeviceType_Floppy:
7818 enmType = VDTYPE_FLOPPY;
7819 break;
7820 default:
7821 ComAssertFailedRet(VDTYPE_INVALID);
7822 }
7823
7824 return enmType;
7825}
7826
7827/**
7828 * Converts from the VD type to the medium type.
7829 */
7830DeviceType_T Medium::i_convertToDeviceType(VDTYPE enmType)
7831{
7832 DeviceType_T devType;
7833
7834 switch (enmType)
7835 {
7836 case VDTYPE_HDD:
7837 devType = DeviceType_HardDisk;
7838 break;
7839 case VDTYPE_OPTICAL_DISC:
7840 devType = DeviceType_DVD;
7841 break;
7842 case VDTYPE_FLOPPY:
7843 devType = DeviceType_Floppy;
7844 break;
7845 default:
7846 ComAssertFailedRet(DeviceType_Null);
7847 }
7848
7849 return devType;
7850}
7851
7852/**
7853 * Internal method which checks whether a property name is for a filter plugin.
7854 */
7855bool Medium::i_isPropertyForFilter(const com::Utf8Str &aName)
7856{
7857 /* If the name contains "/" use the part before as a filter name and lookup the filter. */
7858 size_t offSlash;
7859 if ((offSlash = aName.find("/", 0)) != aName.npos)
7860 {
7861 com::Utf8Str strFilter;
7862 com::Utf8Str strKey;
7863
7864 HRESULT rc = strFilter.assignEx(aName, 0, offSlash);
7865 if (FAILED(rc))
7866 return false;
7867
7868 rc = strKey.assignEx(aName, offSlash + 1, aName.length() - offSlash - 1); /* Skip slash */
7869 if (FAILED(rc))
7870 return false;
7871
7872 VDFILTERINFO FilterInfo;
7873 int vrc = VDFilterInfoOne(strFilter.c_str(), &FilterInfo);
7874 if (RT_SUCCESS(vrc))
7875 {
7876 /* Check that the property exists. */
7877 PCVDCONFIGINFO paConfig = FilterInfo.paConfigInfo;
7878 while (paConfig->pszKey)
7879 {
7880 if (strKey.equals(paConfig->pszKey))
7881 return true;
7882 paConfig++;
7883 }
7884 }
7885 }
7886
7887 return false;
7888}
7889
7890/**
7891 * Returns the last error message collected by the i_vdErrorCall callback and
7892 * resets it.
7893 *
7894 * The error message is returned prepended with a dot and a space, like this:
7895 * <code>
7896 * ". <error_text> (%Rrc)"
7897 * </code>
7898 * to make it easily appendable to a more general error message. The @c %Rrc
7899 * format string is given @a aVRC as an argument.
7900 *
7901 * If there is no last error message collected by i_vdErrorCall or if it is a
7902 * null or empty string, then this function returns the following text:
7903 * <code>
7904 * " (%Rrc)"
7905 * </code>
7906 *
7907 * @note Doesn't do any object locking; it is assumed that the caller makes sure
7908 * the callback isn't called by more than one thread at a time.
7909 *
7910 * @param aVRC VBox error code to use when no error message is provided.
7911 */
7912Utf8Str Medium::i_vdError(int aVRC)
7913{
7914 Utf8Str error;
7915
7916 if (m->vdError.isEmpty())
7917 error = Utf8StrFmt(" (%Rrc)", aVRC);
7918 else
7919 error = Utf8StrFmt(".\n%s", m->vdError.c_str());
7920
7921 m->vdError.setNull();
7922
7923 return error;
7924}
7925
7926/**
7927 * Error message callback.
7928 *
7929 * Puts the reported error message to the m->vdError field.
7930 *
7931 * @note Doesn't do any object locking; it is assumed that the caller makes sure
7932 * the callback isn't called by more than one thread at a time.
7933 *
7934 * @param pvUser The opaque data passed on container creation.
7935 * @param rc The VBox error code.
7936 * @param SRC_POS Use RT_SRC_POS.
7937 * @param pszFormat Error message format string.
7938 * @param va Error message arguments.
7939 */
7940/*static*/
7941DECLCALLBACK(void) Medium::i_vdErrorCall(void *pvUser, int rc, RT_SRC_POS_DECL,
7942 const char *pszFormat, va_list va)
7943{
7944 NOREF(pszFile); NOREF(iLine); NOREF(pszFunction); /* RT_SRC_POS_DECL */
7945
7946 Medium *that = static_cast<Medium*>(pvUser);
7947 AssertReturnVoid(that != NULL);
7948
7949 if (that->m->vdError.isEmpty())
7950 that->m->vdError =
7951 Utf8StrFmt("%s (%Rrc)", Utf8Str(pszFormat, va).c_str(), rc);
7952 else
7953 that->m->vdError =
7954 Utf8StrFmt("%s.\n%s (%Rrc)", that->m->vdError.c_str(),
7955 Utf8Str(pszFormat, va).c_str(), rc);
7956}
7957
7958/* static */
7959DECLCALLBACK(bool) Medium::i_vdConfigAreKeysValid(void *pvUser,
7960 const char * /* pszzValid */)
7961{
7962 Medium *that = static_cast<Medium*>(pvUser);
7963 AssertReturn(that != NULL, false);
7964
7965 /* we always return true since the only keys we have are those found in
7966 * VDBACKENDINFO */
7967 return true;
7968}
7969
7970/* static */
7971DECLCALLBACK(int) Medium::i_vdConfigQuerySize(void *pvUser,
7972 const char *pszName,
7973 size_t *pcbValue)
7974{
7975 AssertReturn(VALID_PTR(pcbValue), VERR_INVALID_POINTER);
7976
7977 Medium *that = static_cast<Medium*>(pvUser);
7978 AssertReturn(that != NULL, VERR_GENERAL_FAILURE);
7979
7980 settings::StringsMap::const_iterator it = that->m->mapProperties.find(Utf8Str(pszName));
7981 if (it == that->m->mapProperties.end())
7982 return VERR_CFGM_VALUE_NOT_FOUND;
7983
7984 /* we interpret null values as "no value" in Medium */
7985 if (it->second.isEmpty())
7986 return VERR_CFGM_VALUE_NOT_FOUND;
7987
7988 *pcbValue = it->second.length() + 1 /* include terminator */;
7989
7990 return VINF_SUCCESS;
7991}
7992
7993/* static */
7994DECLCALLBACK(int) Medium::i_vdConfigQuery(void *pvUser,
7995 const char *pszName,
7996 char *pszValue,
7997 size_t cchValue)
7998{
7999 AssertReturn(VALID_PTR(pszValue), VERR_INVALID_POINTER);
8000
8001 Medium *that = static_cast<Medium*>(pvUser);
8002 AssertReturn(that != NULL, VERR_GENERAL_FAILURE);
8003
8004 settings::StringsMap::const_iterator it = that->m->mapProperties.find(Utf8Str(pszName));
8005 if (it == that->m->mapProperties.end())
8006 return VERR_CFGM_VALUE_NOT_FOUND;
8007
8008 /* we interpret null values as "no value" in Medium */
8009 if (it->second.isEmpty())
8010 return VERR_CFGM_VALUE_NOT_FOUND;
8011
8012 const Utf8Str &value = it->second;
8013 if (value.length() >= cchValue)
8014 return VERR_CFGM_NOT_ENOUGH_SPACE;
8015
8016 memcpy(pszValue, value.c_str(), value.length() + 1);
8017
8018 return VINF_SUCCESS;
8019}
8020
8021DECLCALLBACK(int) Medium::i_vdTcpSocketCreate(uint32_t fFlags, PVDSOCKET pSock)
8022{
8023 PVDSOCKETINT pSocketInt = NULL;
8024
8025 if ((fFlags & VD_INTERFACETCPNET_CONNECT_EXTENDED_SELECT) != 0)
8026 return VERR_NOT_SUPPORTED;
8027
8028 pSocketInt = (PVDSOCKETINT)RTMemAllocZ(sizeof(VDSOCKETINT));
8029 if (!pSocketInt)
8030 return VERR_NO_MEMORY;
8031
8032 pSocketInt->hSocket = NIL_RTSOCKET;
8033 *pSock = pSocketInt;
8034 return VINF_SUCCESS;
8035}
8036
8037DECLCALLBACK(int) Medium::i_vdTcpSocketDestroy(VDSOCKET Sock)
8038{
8039 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
8040
8041 if (pSocketInt->hSocket != NIL_RTSOCKET)
8042 RTTcpClientCloseEx(pSocketInt->hSocket, false /*fGracefulShutdown*/);
8043
8044 RTMemFree(pSocketInt);
8045
8046 return VINF_SUCCESS;
8047}
8048
8049DECLCALLBACK(int) Medium::i_vdTcpClientConnect(VDSOCKET Sock, const char *pszAddress, uint32_t uPort,
8050 RTMSINTERVAL cMillies)
8051{
8052 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
8053
8054 return RTTcpClientConnectEx(pszAddress, uPort, &pSocketInt->hSocket, cMillies, NULL);
8055}
8056
8057DECLCALLBACK(int) Medium::i_vdTcpClientClose(VDSOCKET Sock)
8058{
8059 int rc = VINF_SUCCESS;
8060 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
8061
8062 rc = RTTcpClientCloseEx(pSocketInt->hSocket, false /*fGracefulShutdown*/);
8063 pSocketInt->hSocket = NIL_RTSOCKET;
8064 return rc;
8065}
8066
8067DECLCALLBACK(bool) Medium::i_vdTcpIsClientConnected(VDSOCKET Sock)
8068{
8069 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
8070 return pSocketInt->hSocket != NIL_RTSOCKET;
8071}
8072
8073DECLCALLBACK(int) Medium::i_vdTcpSelectOne(VDSOCKET Sock, RTMSINTERVAL cMillies)
8074{
8075 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
8076 return RTTcpSelectOne(pSocketInt->hSocket, cMillies);
8077}
8078
8079DECLCALLBACK(int) Medium::i_vdTcpRead(VDSOCKET Sock, void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
8080{
8081 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
8082 return RTTcpRead(pSocketInt->hSocket, pvBuffer, cbBuffer, pcbRead);
8083}
8084
8085DECLCALLBACK(int) Medium::i_vdTcpWrite(VDSOCKET Sock, const void *pvBuffer, size_t cbBuffer)
8086{
8087 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
8088 return RTTcpWrite(pSocketInt->hSocket, pvBuffer, cbBuffer);
8089}
8090
8091DECLCALLBACK(int) Medium::i_vdTcpSgWrite(VDSOCKET Sock, PCRTSGBUF pSgBuf)
8092{
8093 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
8094 return RTTcpSgWrite(pSocketInt->hSocket, pSgBuf);
8095}
8096
8097DECLCALLBACK(int) Medium::i_vdTcpFlush(VDSOCKET Sock)
8098{
8099 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
8100 return RTTcpFlush(pSocketInt->hSocket);
8101}
8102
8103DECLCALLBACK(int) Medium::i_vdTcpSetSendCoalescing(VDSOCKET Sock, bool fEnable)
8104{
8105 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
8106 return RTTcpSetSendCoalescing(pSocketInt->hSocket, fEnable);
8107}
8108
8109DECLCALLBACK(int) Medium::i_vdTcpGetLocalAddress(VDSOCKET Sock, PRTNETADDR pAddr)
8110{
8111 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
8112 return RTTcpGetLocalAddress(pSocketInt->hSocket, pAddr);
8113}
8114
8115DECLCALLBACK(int) Medium::i_vdTcpGetPeerAddress(VDSOCKET Sock, PRTNETADDR pAddr)
8116{
8117 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
8118 return RTTcpGetPeerAddress(pSocketInt->hSocket, pAddr);
8119}
8120
8121DECLCALLBACK(bool) Medium::i_vdCryptoConfigAreKeysValid(void *pvUser, const char *pszzValid)
8122{
8123 /* Just return always true here. */
8124 NOREF(pvUser);
8125 NOREF(pszzValid);
8126 return true;
8127}
8128
8129DECLCALLBACK(int) Medium::i_vdCryptoConfigQuerySize(void *pvUser, const char *pszName, size_t *pcbValue)
8130{
8131 MediumCryptoFilterSettings *pSettings = (MediumCryptoFilterSettings *)pvUser;
8132 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
8133 AssertReturn(VALID_PTR(pcbValue), VERR_INVALID_POINTER);
8134
8135 size_t cbValue = 0;
8136 if (!strcmp(pszName, "Algorithm"))
8137 cbValue = strlen(pSettings->pszCipher) + 1;
8138 else if (!strcmp(pszName, "KeyId"))
8139 cbValue = sizeof("irrelevant");
8140 else if (!strcmp(pszName, "KeyStore"))
8141 {
8142 if (!pSettings->pszKeyStoreLoad)
8143 return VERR_CFGM_VALUE_NOT_FOUND;
8144 cbValue = strlen(pSettings->pszKeyStoreLoad) + 1;
8145 }
8146 else if (!strcmp(pszName, "CreateKeyStore"))
8147 cbValue = 2; /* Single digit + terminator. */
8148 else
8149 return VERR_CFGM_VALUE_NOT_FOUND;
8150
8151 *pcbValue = cbValue + 1 /* include terminator */;
8152
8153 return VINF_SUCCESS;
8154}
8155
8156DECLCALLBACK(int) Medium::i_vdCryptoConfigQuery(void *pvUser, const char *pszName,
8157 char *pszValue, size_t cchValue)
8158{
8159 MediumCryptoFilterSettings *pSettings = (MediumCryptoFilterSettings *)pvUser;
8160 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
8161 AssertReturn(VALID_PTR(pszValue), VERR_INVALID_POINTER);
8162
8163 const char *psz = NULL;
8164 if (!strcmp(pszName, "Algorithm"))
8165 psz = pSettings->pszCipher;
8166 else if (!strcmp(pszName, "KeyId"))
8167 psz = "irrelevant";
8168 else if (!strcmp(pszName, "KeyStore"))
8169 psz = pSettings->pszKeyStoreLoad;
8170 else if (!strcmp(pszName, "CreateKeyStore"))
8171 {
8172 if (pSettings->fCreateKeyStore)
8173 psz = "1";
8174 else
8175 psz = "0";
8176 }
8177 else
8178 return VERR_CFGM_VALUE_NOT_FOUND;
8179
8180 size_t cch = strlen(psz);
8181 if (cch >= cchValue)
8182 return VERR_CFGM_NOT_ENOUGH_SPACE;
8183
8184 memcpy(pszValue, psz, cch + 1);
8185 return VINF_SUCCESS;
8186}
8187
8188DECLCALLBACK(int) Medium::i_vdCryptoKeyRetain(void *pvUser, const char *pszId,
8189 const uint8_t **ppbKey, size_t *pcbKey)
8190{
8191 MediumCryptoFilterSettings *pSettings = (MediumCryptoFilterSettings *)pvUser;
8192 NOREF(pszId);
8193 NOREF(ppbKey);
8194 NOREF(pcbKey);
8195 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
8196 AssertMsgFailedReturn(("This method should not be called here!\n"), VERR_INVALID_STATE);
8197}
8198
8199DECLCALLBACK(int) Medium::i_vdCryptoKeyRelease(void *pvUser, const char *pszId)
8200{
8201 MediumCryptoFilterSettings *pSettings = (MediumCryptoFilterSettings *)pvUser;
8202 NOREF(pszId);
8203 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
8204 AssertMsgFailedReturn(("This method should not be called here!\n"), VERR_INVALID_STATE);
8205}
8206
8207DECLCALLBACK(int) Medium::i_vdCryptoKeyStorePasswordRetain(void *pvUser, const char *pszId, const char **ppszPassword)
8208{
8209 MediumCryptoFilterSettings *pSettings = (MediumCryptoFilterSettings *)pvUser;
8210 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
8211
8212 NOREF(pszId);
8213 *ppszPassword = pSettings->pszPassword;
8214 return VINF_SUCCESS;
8215}
8216
8217DECLCALLBACK(int) Medium::i_vdCryptoKeyStorePasswordRelease(void *pvUser, const char *pszId)
8218{
8219 MediumCryptoFilterSettings *pSettings = (MediumCryptoFilterSettings *)pvUser;
8220 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
8221 NOREF(pszId);
8222 return VINF_SUCCESS;
8223}
8224
8225DECLCALLBACK(int) Medium::i_vdCryptoKeyStoreSave(void *pvUser, const void *pvKeyStore, size_t cbKeyStore)
8226{
8227 MediumCryptoFilterSettings *pSettings = (MediumCryptoFilterSettings *)pvUser;
8228 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
8229
8230 pSettings->pszKeyStore = (char *)RTMemAllocZ(cbKeyStore);
8231 if (!pSettings->pszKeyStore)
8232 return VERR_NO_MEMORY;
8233
8234 memcpy(pSettings->pszKeyStore, pvKeyStore, cbKeyStore);
8235 return VINF_SUCCESS;
8236}
8237
8238DECLCALLBACK(int) Medium::i_vdCryptoKeyStoreReturnParameters(void *pvUser, const char *pszCipher,
8239 const uint8_t *pbDek, size_t cbDek)
8240{
8241 MediumCryptoFilterSettings *pSettings = (MediumCryptoFilterSettings *)pvUser;
8242 AssertPtrReturn(pSettings, VERR_GENERAL_FAILURE);
8243
8244 pSettings->pszCipherReturned = RTStrDup(pszCipher);
8245 pSettings->pbDek = pbDek;
8246 pSettings->cbDek = cbDek;
8247
8248 return pSettings->pszCipherReturned ? VINF_SUCCESS : VERR_NO_MEMORY;
8249}
8250
8251/**
8252 * Creates a VDISK instance for this medium.
8253 *
8254 * @note Caller should not hold any medium related locks as this method will
8255 * acquire the medium lock for writing and others (VirtualBox).
8256 *
8257 * @returns COM status code.
8258 * @param fWritable Whether to return a writable VDISK instance
8259 * (true) or a read-only one (false).
8260 * @param pKeyStore The key store.
8261 * @param ppHdd Where to return the pointer to the VDISK on
8262 * success.
8263 * @param pMediumLockList The lock list to populate and lock. Caller
8264 * is responsible for calling the destructor or
8265 * MediumLockList::Clear() after destroying
8266 * @a *ppHdd
8267 * @param pCryptoSettings The crypto settings to use for setting up
8268 * decryption/encryption of the VDISK. This object
8269 * must be alive until the VDISK is destroyed!
8270 */
8271HRESULT Medium::i_openForIO(bool fWritable, SecretKeyStore *pKeyStore, PVDISK *ppHdd, MediumLockList *pMediumLockList,
8272 MediumCryptoFilterSettings *pCryptoSettings)
8273{
8274 /*
8275 * Create the media lock list and lock the media.
8276 */
8277 HRESULT hrc = i_createMediumLockList(true /* fFailIfInaccessible */,
8278 fWritable ? this : NULL /* pToLockWrite */,
8279 false /* fMediumLockWriteAll */,
8280 NULL,
8281 *pMediumLockList);
8282 if (SUCCEEDED(hrc))
8283 hrc = pMediumLockList->Lock();
8284 if (FAILED(hrc))
8285 return hrc;
8286
8287 /*
8288 * Get the base medium before write locking this medium.
8289 */
8290 ComObjPtr<Medium> pBase = i_getBase();
8291 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
8292
8293 /*
8294 * Create the VDISK instance.
8295 */
8296 PVDISK pHdd;
8297 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &pHdd);
8298 AssertRCReturn(vrc, E_FAIL);
8299
8300 /*
8301 * Goto avoidance using try/catch/throw(HRESULT).
8302 */
8303 try
8304 {
8305 settings::StringsMap::iterator itKeyStore = pBase->m->mapProperties.find("CRYPT/KeyStore");
8306 if (itKeyStore != pBase->m->mapProperties.end())
8307 {
8308 settings::StringsMap::iterator itKeyId = pBase->m->mapProperties.find("CRYPT/KeyId");
8309
8310#ifdef VBOX_WITH_EXTPACK
8311 ExtPackManager *pExtPackManager = m->pVirtualBox->i_getExtPackManager();
8312 if (pExtPackManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
8313 {
8314 /* Load the plugin */
8315 Utf8Str strPlugin;
8316 hrc = pExtPackManager->i_getLibraryPathForExtPack(g_szVDPlugin, ORACLE_PUEL_EXTPACK_NAME, &strPlugin);
8317 if (SUCCEEDED(hrc))
8318 {
8319 vrc = VDPluginLoadFromFilename(strPlugin.c_str());
8320 if (RT_FAILURE(vrc))
8321 throw setErrorBoth(VBOX_E_NOT_SUPPORTED, vrc,
8322 tr("Retrieving encryption settings of the image failed because the encryption plugin could not be loaded (%s)"),
8323 i_vdError(vrc).c_str());
8324 }
8325 else
8326 throw setError(VBOX_E_NOT_SUPPORTED,
8327 tr("Encryption is not supported because the extension pack '%s' is missing the encryption plugin (old extension pack installed?)"),
8328 ORACLE_PUEL_EXTPACK_NAME);
8329 }
8330 else
8331 throw setError(VBOX_E_NOT_SUPPORTED,
8332 tr("Encryption is not supported because the extension pack '%s' is missing"),
8333 ORACLE_PUEL_EXTPACK_NAME);
8334#else
8335 throw setError(VBOX_E_NOT_SUPPORTED,
8336 tr("Encryption is not supported because extension pack support is not built in"));
8337#endif
8338
8339 if (itKeyId == pBase->m->mapProperties.end())
8340 throw setError(VBOX_E_INVALID_OBJECT_STATE,
8341 tr("Image '%s' is configured for encryption but doesn't has a key identifier set"),
8342 pBase->m->strLocationFull.c_str());
8343
8344 /* Find the proper secret key in the key store. */
8345 if (!pKeyStore)
8346 throw setError(VBOX_E_INVALID_OBJECT_STATE,
8347 tr("Image '%s' is configured for encryption but there is no key store to retrieve the password from"),
8348 pBase->m->strLocationFull.c_str());
8349
8350 SecretKey *pKey = NULL;
8351 vrc = pKeyStore->retainSecretKey(itKeyId->second, &pKey);
8352 if (RT_FAILURE(vrc))
8353 throw setErrorBoth(VBOX_E_INVALID_OBJECT_STATE, vrc,
8354 tr("Failed to retrieve the secret key with ID \"%s\" from the store (%Rrc)"),
8355 itKeyId->second.c_str(), vrc);
8356
8357 i_taskEncryptSettingsSetup(pCryptoSettings, NULL, itKeyStore->second.c_str(), (const char *)pKey->getKeyBuffer(),
8358 false /* fCreateKeyStore */);
8359 vrc = VDFilterAdd(pHdd, "CRYPT", VD_FILTER_FLAGS_DEFAULT, pCryptoSettings->vdFilterIfaces);
8360 pKeyStore->releaseSecretKey(itKeyId->second);
8361 if (vrc == VERR_VD_PASSWORD_INCORRECT)
8362 throw setErrorBoth(VBOX_E_PASSWORD_INCORRECT, vrc, tr("The password to decrypt the image is incorrect"));
8363 if (RT_FAILURE(vrc))
8364 throw setErrorBoth(VBOX_E_INVALID_OBJECT_STATE, vrc, tr("Failed to load the decryption filter: %s"),
8365 i_vdError(vrc).c_str());
8366 }
8367
8368 /*
8369 * Open all media in the source chain.
8370 */
8371 MediumLockList::Base::const_iterator sourceListBegin = pMediumLockList->GetBegin();
8372 MediumLockList::Base::const_iterator sourceListEnd = pMediumLockList->GetEnd();
8373 MediumLockList::Base::const_iterator mediumListLast = sourceListEnd;
8374 --mediumListLast;
8375
8376 for (MediumLockList::Base::const_iterator it = sourceListBegin; it != sourceListEnd; ++it)
8377 {
8378 const MediumLock &mediumLock = *it;
8379 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
8380 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
8381
8382 /* sanity check */
8383 Assert(pMedium->m->state == (fWritable && it == mediumListLast ? MediumState_LockedWrite : MediumState_LockedRead));
8384
8385 /* Open all media in read-only mode. */
8386 vrc = VDOpen(pHdd,
8387 pMedium->m->strFormat.c_str(),
8388 pMedium->m->strLocationFull.c_str(),
8389 m->uOpenFlagsDef | (fWritable && it == mediumListLast ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY),
8390 pMedium->m->vdImageIfaces);
8391 if (RT_FAILURE(vrc))
8392 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
8393 tr("Could not open the medium storage unit '%s'%s"),
8394 pMedium->m->strLocationFull.c_str(),
8395 i_vdError(vrc).c_str());
8396 }
8397
8398 Assert(m->state == (fWritable ? MediumState_LockedWrite : MediumState_LockedRead));
8399
8400 /*
8401 * Done!
8402 */
8403 *ppHdd = pHdd;
8404 return S_OK;
8405 }
8406 catch (HRESULT hrc2)
8407 {
8408 hrc = hrc2;
8409 }
8410
8411 VDDestroy(pHdd);
8412 return hrc;
8413
8414}
8415
8416/**
8417 * Implementation code for the "create base" task.
8418 *
8419 * This only gets started from Medium::CreateBaseStorage() and always runs
8420 * asynchronously. As a result, we always save the VirtualBox.xml file when
8421 * we're done here.
8422 *
8423 * @param task
8424 * @return
8425 */
8426HRESULT Medium::i_taskCreateBaseHandler(Medium::CreateBaseTask &task)
8427{
8428 /** @todo r=klaus The code below needs to be double checked with regard
8429 * to lock order violations, it probably causes lock order issues related
8430 * to the AutoCaller usage. */
8431 HRESULT rc = S_OK;
8432
8433 /* these parameters we need after creation */
8434 uint64_t size = 0, logicalSize = 0;
8435 MediumVariant_T variant = MediumVariant_Standard;
8436 bool fGenerateUuid = false;
8437
8438 try
8439 {
8440 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
8441
8442 /* The object may request a specific UUID (through a special form of
8443 * the moveTo() argument). Otherwise we have to generate it */
8444 Guid id = m->id;
8445
8446 fGenerateUuid = id.isZero();
8447 if (fGenerateUuid)
8448 {
8449 id.create();
8450 /* VirtualBox::i_registerMedium() will need UUID */
8451 unconst(m->id) = id;
8452 }
8453
8454 Utf8Str format(m->strFormat);
8455 Utf8Str location(m->strLocationFull);
8456 uint64_t capabilities = m->formatObj->i_getCapabilities();
8457 ComAssertThrow(capabilities & ( MediumFormatCapabilities_CreateFixed
8458 | MediumFormatCapabilities_CreateDynamic), E_FAIL);
8459 Assert(m->state == MediumState_Creating);
8460
8461 PVDISK hdd;
8462 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
8463 ComAssertRCThrow(vrc, E_FAIL);
8464
8465 /* unlock before the potentially lengthy operation */
8466 thisLock.release();
8467
8468 try
8469 {
8470 /* ensure the directory exists */
8471 if (capabilities & MediumFormatCapabilities_File)
8472 {
8473 rc = VirtualBox::i_ensureFilePathExists(location, !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
8474 if (FAILED(rc))
8475 throw rc;
8476 }
8477
8478 VDGEOMETRY geo = { 0, 0, 0 }; /* auto-detect */
8479
8480 vrc = VDCreateBase(hdd,
8481 format.c_str(),
8482 location.c_str(),
8483 task.mSize,
8484 task.mVariant & ~(MediumVariant_NoCreateDir | MediumVariant_Formatted),
8485 NULL,
8486 &geo,
8487 &geo,
8488 id.raw(),
8489 VD_OPEN_FLAGS_NORMAL | m->uOpenFlagsDef,
8490 m->vdImageIfaces,
8491 task.mVDOperationIfaces);
8492 if (RT_FAILURE(vrc))
8493 {
8494 if (vrc == VERR_VD_INVALID_TYPE)
8495 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
8496 tr("Parameters for creating the medium storage unit '%s' are invalid%s"),
8497 location.c_str(), i_vdError(vrc).c_str());
8498 else
8499 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
8500 tr("Could not create the medium storage unit '%s'%s"),
8501 location.c_str(), i_vdError(vrc).c_str());
8502 }
8503
8504 if (task.mVariant & MediumVariant_Formatted)
8505 {
8506 RTVFSFILE hVfsFile;
8507 vrc = VDCreateVfsFileFromDisk(hdd, 0 /*fFlags*/, &hVfsFile);
8508 if (RT_FAILURE(vrc))
8509 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Opening medium storage unit '%s' failed%s"),
8510 location.c_str(), i_vdError(vrc).c_str());
8511 RTERRINFOSTATIC ErrInfo;
8512 vrc = RTFsFatVolFormat(hVfsFile, 0 /* offVol */, 0 /* cbVol */, RTFSFATVOL_FMT_F_FULL,
8513 0 /* cbSector */, 0 /* cbSectorPerCluster */, RTFSFATTYPE_INVALID,
8514 0 /* cHeads */, 0 /* cSectorsPerTrack*/, 0 /* bMedia */,
8515 0 /* cRootDirEntries */, 0 /* cHiddenSectors */,
8516 RTErrInfoInitStatic(&ErrInfo));
8517 RTVfsFileRelease(hVfsFile);
8518 if (RT_FAILURE(vrc) && RTErrInfoIsSet(&ErrInfo.Core))
8519 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Formatting medium storage unit '%s' failed: %s"),
8520 location.c_str(), ErrInfo.Core.pszMsg);
8521 if (RT_FAILURE(vrc))
8522 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc, tr("Formatting medium storage unit '%s' failed%s"),
8523 location.c_str(), i_vdError(vrc).c_str());
8524 }
8525
8526 size = VDGetFileSize(hdd, 0);
8527 logicalSize = VDGetSize(hdd, 0);
8528 unsigned uImageFlags;
8529 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
8530 if (RT_SUCCESS(vrc))
8531 variant = (MediumVariant_T)uImageFlags;
8532 }
8533 catch (HRESULT aRC) { rc = aRC; }
8534
8535 VDDestroy(hdd);
8536 }
8537 catch (HRESULT aRC) { rc = aRC; }
8538
8539 if (SUCCEEDED(rc))
8540 {
8541 /* register with mVirtualBox as the last step and move to
8542 * Created state only on success (leaving an orphan file is
8543 * better than breaking media registry consistency) */
8544 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
8545 ComObjPtr<Medium> pMedium;
8546 rc = m->pVirtualBox->i_registerMedium(this, &pMedium, treeLock);
8547 Assert(pMedium == NULL || this == pMedium);
8548 }
8549
8550 // re-acquire the lock before changing state
8551 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
8552
8553 if (SUCCEEDED(rc))
8554 {
8555 m->state = MediumState_Created;
8556
8557 m->size = size;
8558 m->logicalSize = logicalSize;
8559 m->variant = variant;
8560
8561 thisLock.release();
8562 i_markRegistriesModified();
8563 if (task.isAsync())
8564 {
8565 // in asynchronous mode, save settings now
8566 m->pVirtualBox->i_saveModifiedRegistries();
8567 }
8568 if (task.NotifyAboutChanges())
8569 m->pVirtualBox->i_onMediumRegistered(m->id, m->devType, TRUE);
8570 }
8571 else
8572 {
8573 /* back to NotCreated on failure */
8574 m->state = MediumState_NotCreated;
8575
8576 /* reset UUID to prevent it from being reused next time */
8577 if (fGenerateUuid)
8578 unconst(m->id).clear();
8579 }
8580
8581 return rc;
8582}
8583
8584/**
8585 * Implementation code for the "create diff" task.
8586 *
8587 * This task always gets started from Medium::createDiffStorage() and can run
8588 * synchronously or asynchronously depending on the "wait" parameter passed to
8589 * that function. If we run synchronously, the caller expects the medium
8590 * registry modification to be set before returning; otherwise (in asynchronous
8591 * mode), we save the settings ourselves.
8592 *
8593 * @param task
8594 * @return
8595 */
8596HRESULT Medium::i_taskCreateDiffHandler(Medium::CreateDiffTask &task)
8597{
8598 /** @todo r=klaus The code below needs to be double checked with regard
8599 * to lock order violations, it probably causes lock order issues related
8600 * to the AutoCaller usage. */
8601 HRESULT rcTmp = S_OK;
8602
8603 const ComObjPtr<Medium> &pTarget = task.mTarget;
8604
8605 uint64_t size = 0, logicalSize = 0;
8606 MediumVariant_T variant = MediumVariant_Standard;
8607 bool fGenerateUuid = false;
8608
8609 try
8610 {
8611 if (i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX)
8612 {
8613 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8614 throw setError(VBOX_E_INVALID_OBJECT_STATE,
8615 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"),
8616 m->strLocationFull.c_str());
8617 }
8618
8619 /* Lock both in {parent,child} order. */
8620 AutoMultiWriteLock2 mediaLock(this, pTarget COMMA_LOCKVAL_SRC_POS);
8621
8622 /* The object may request a specific UUID (through a special form of
8623 * the moveTo() argument). Otherwise we have to generate it */
8624 Guid targetId = pTarget->m->id;
8625
8626 fGenerateUuid = targetId.isZero();
8627 if (fGenerateUuid)
8628 {
8629 targetId.create();
8630 /* VirtualBox::i_registerMedium() will need UUID */
8631 unconst(pTarget->m->id) = targetId;
8632 }
8633
8634 Guid id = m->id;
8635
8636 Utf8Str targetFormat(pTarget->m->strFormat);
8637 Utf8Str targetLocation(pTarget->m->strLocationFull);
8638 uint64_t capabilities = pTarget->m->formatObj->i_getCapabilities();
8639 ComAssertThrow(capabilities & MediumFormatCapabilities_CreateDynamic, E_FAIL);
8640
8641 Assert(pTarget->m->state == MediumState_Creating);
8642 Assert(m->state == MediumState_LockedRead);
8643
8644 PVDISK hdd;
8645 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
8646 ComAssertRCThrow(vrc, E_FAIL);
8647
8648 /* the two media are now protected by their non-default states;
8649 * unlock the media before the potentially lengthy operation */
8650 mediaLock.release();
8651
8652 try
8653 {
8654 /* Open all media in the target chain but the last. */
8655 MediumLockList::Base::const_iterator targetListBegin =
8656 task.mpMediumLockList->GetBegin();
8657 MediumLockList::Base::const_iterator targetListEnd =
8658 task.mpMediumLockList->GetEnd();
8659 for (MediumLockList::Base::const_iterator it = targetListBegin;
8660 it != targetListEnd;
8661 ++it)
8662 {
8663 const MediumLock &mediumLock = *it;
8664 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
8665
8666 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
8667
8668 /* Skip over the target diff medium */
8669 if (pMedium->m->state == MediumState_Creating)
8670 continue;
8671
8672 /* sanity check */
8673 Assert(pMedium->m->state == MediumState_LockedRead);
8674
8675 /* Open all media in appropriate mode. */
8676 vrc = VDOpen(hdd,
8677 pMedium->m->strFormat.c_str(),
8678 pMedium->m->strLocationFull.c_str(),
8679 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
8680 pMedium->m->vdImageIfaces);
8681 if (RT_FAILURE(vrc))
8682 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
8683 tr("Could not open the medium storage unit '%s'%s"),
8684 pMedium->m->strLocationFull.c_str(),
8685 i_vdError(vrc).c_str());
8686 }
8687
8688 /* ensure the target directory exists */
8689 if (capabilities & MediumFormatCapabilities_File)
8690 {
8691 HRESULT rc = VirtualBox::i_ensureFilePathExists(targetLocation,
8692 !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
8693 if (FAILED(rc))
8694 throw rc;
8695 }
8696
8697 vrc = VDCreateDiff(hdd,
8698 targetFormat.c_str(),
8699 targetLocation.c_str(),
8700 (task.mVariant & ~(MediumVariant_NoCreateDir | MediumVariant_Formatted | MediumVariant_VmdkESX))
8701 | VD_IMAGE_FLAGS_DIFF,
8702 NULL,
8703 targetId.raw(),
8704 id.raw(),
8705 VD_OPEN_FLAGS_NORMAL | m->uOpenFlagsDef,
8706 pTarget->m->vdImageIfaces,
8707 task.mVDOperationIfaces);
8708 if (RT_FAILURE(vrc))
8709 {
8710 if (vrc == VERR_VD_INVALID_TYPE)
8711 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
8712 tr("Parameters for creating the differencing medium storage unit '%s' are invalid%s"),
8713 targetLocation.c_str(), i_vdError(vrc).c_str());
8714 else
8715 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
8716 tr("Could not create the differencing medium storage unit '%s'%s"),
8717 targetLocation.c_str(), i_vdError(vrc).c_str());
8718 }
8719
8720 size = VDGetFileSize(hdd, VD_LAST_IMAGE);
8721 logicalSize = VDGetSize(hdd, VD_LAST_IMAGE);
8722 unsigned uImageFlags;
8723 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
8724 if (RT_SUCCESS(vrc))
8725 variant = (MediumVariant_T)uImageFlags;
8726 }
8727 catch (HRESULT aRC) { rcTmp = aRC; }
8728
8729 VDDestroy(hdd);
8730 }
8731 catch (HRESULT aRC) { rcTmp = aRC; }
8732
8733 MultiResult mrc(rcTmp);
8734
8735 if (SUCCEEDED(mrc))
8736 {
8737 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
8738
8739 Assert(pTarget->m->pParent.isNull());
8740
8741 /* associate child with the parent, maximum depth was checked above */
8742 pTarget->i_setParent(this);
8743
8744 /* diffs for immutable media are auto-reset by default */
8745 bool fAutoReset;
8746 {
8747 ComObjPtr<Medium> pBase = i_getBase();
8748 AutoReadLock block(pBase COMMA_LOCKVAL_SRC_POS);
8749 fAutoReset = (pBase->m->type == MediumType_Immutable);
8750 }
8751 {
8752 AutoWriteLock tlock(pTarget COMMA_LOCKVAL_SRC_POS);
8753 pTarget->m->autoReset = fAutoReset;
8754 }
8755
8756 /* register with mVirtualBox as the last step and move to
8757 * Created state only on success (leaving an orphan file is
8758 * better than breaking media registry consistency) */
8759 ComObjPtr<Medium> pMedium;
8760 mrc = m->pVirtualBox->i_registerMedium(pTarget, &pMedium, treeLock);
8761 Assert(pTarget == pMedium);
8762
8763 if (FAILED(mrc))
8764 /* break the parent association on failure to register */
8765 i_deparent();
8766 }
8767
8768 AutoMultiWriteLock2 mediaLock(this, pTarget COMMA_LOCKVAL_SRC_POS);
8769
8770 if (SUCCEEDED(mrc))
8771 {
8772 pTarget->m->state = MediumState_Created;
8773
8774 pTarget->m->size = size;
8775 pTarget->m->logicalSize = logicalSize;
8776 pTarget->m->variant = variant;
8777 }
8778 else
8779 {
8780 /* back to NotCreated on failure */
8781 pTarget->m->state = MediumState_NotCreated;
8782
8783 pTarget->m->autoReset = false;
8784
8785 /* reset UUID to prevent it from being reused next time */
8786 if (fGenerateUuid)
8787 unconst(pTarget->m->id).clear();
8788 }
8789
8790 // deregister the task registered in createDiffStorage()
8791 Assert(m->numCreateDiffTasks != 0);
8792 --m->numCreateDiffTasks;
8793
8794 mediaLock.release();
8795 i_markRegistriesModified();
8796 if (task.isAsync())
8797 {
8798 // in asynchronous mode, save settings now
8799 m->pVirtualBox->i_saveModifiedRegistries();
8800 }
8801
8802 /* Note that in sync mode, it's the caller's responsibility to
8803 * unlock the medium. */
8804
8805 if (task.NotifyAboutChanges() && SUCCEEDED(mrc))
8806 {
8807 m->pVirtualBox->i_onMediumRegistered(pTarget->i_getId(), pTarget->i_getDeviceType(), TRUE);
8808 m->pVirtualBox->i_onMediumConfigChanged(this);
8809 }
8810
8811 return mrc;
8812}
8813
8814/**
8815 * Implementation code for the "merge" task.
8816 *
8817 * This task always gets started from Medium::mergeTo() and can run
8818 * synchronously or asynchronously depending on the "wait" parameter passed to
8819 * that function. If we run synchronously, the caller expects the medium
8820 * registry modification to be set before returning; otherwise (in asynchronous
8821 * mode), we save the settings ourselves.
8822 *
8823 * @param task
8824 * @return
8825 */
8826HRESULT Medium::i_taskMergeHandler(Medium::MergeTask &task)
8827{
8828 /** @todo r=klaus The code below needs to be double checked with regard
8829 * to lock order violations, it probably causes lock order issues related
8830 * to the AutoCaller usage. */
8831 HRESULT rcTmp = S_OK;
8832
8833 const ComObjPtr<Medium> &pTarget = task.mTarget;
8834
8835 try
8836 {
8837 if (!task.mParentForTarget.isNull())
8838 if (task.mParentForTarget->i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX)
8839 {
8840 AutoReadLock plock(task.mParentForTarget COMMA_LOCKVAL_SRC_POS);
8841 throw setError(VBOX_E_INVALID_OBJECT_STATE,
8842 tr("Cannot merge image for medium '%s', because it exceeds the medium tree depth limit. Please merge some images which you no longer need"),
8843 task.mParentForTarget->m->strLocationFull.c_str());
8844 }
8845
8846 // Resize target to source size, if possible. Otherwise throw an error.
8847 // It's offline resizing. Online resizing will be called in the
8848 // SessionMachine::onlineMergeMedium.
8849
8850 uint64_t sourceSize = 0;
8851 Utf8Str sourceName;
8852 {
8853 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8854 sourceSize = i_getLogicalSize();
8855 sourceName = i_getName();
8856 }
8857 uint64_t targetSize = 0;
8858 Utf8Str targetName;
8859 {
8860 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
8861 targetSize = pTarget->i_getLogicalSize();
8862 targetName = pTarget->i_getName();
8863 }
8864
8865 //reducing vm disks are not implemented yet
8866 if (sourceSize > targetSize)
8867 {
8868 if (i_isMediumFormatFile())
8869 {
8870 // Have to make own lock list, because "resize" method resizes only last image
8871 // in the lock chain. The lock chain already in the task.mpMediumLockList, so
8872 // just make new lock list based on it. In fact the own lock list neither makes
8873 // double locking of mediums nor unlocks them during delete, because medium
8874 // already locked by task.mpMediumLockList and own list is used just to specify
8875 // what "resize" method should resize.
8876
8877 MediumLockList* pMediumLockListForResize = new MediumLockList();
8878
8879 for (MediumLockList::Base::iterator it = task.mpMediumLockList->GetBegin();
8880 it != task.mpMediumLockList->GetEnd();
8881 ++it)
8882 {
8883 ComObjPtr<Medium> pMedium = it->GetMedium();
8884 pMediumLockListForResize->Append(pMedium, pMedium->m->state == MediumState_LockedWrite);
8885 if (pMedium == pTarget)
8886 break;
8887 }
8888
8889 // just to switch internal state of the lock list to avoid errors during list deletion,
8890 // because all meduims in the list already locked by task.mpMediumLockList
8891 HRESULT rc = pMediumLockListForResize->Lock(true /* fSkipOverLockedMedia */);
8892 if (FAILED(rc))
8893 {
8894 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8895 rc = setError(rc,
8896 tr("Failed to lock the medium '%s' to resize before merge"),
8897 targetName.c_str());
8898 delete pMediumLockListForResize;
8899 throw rc;
8900 }
8901
8902 ComObjPtr<Progress> pProgress(task.GetProgressObject());
8903 rc = pTarget->i_resize(sourceSize, pMediumLockListForResize, &pProgress, true, false);
8904 if (FAILED(rc))
8905 {
8906 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8907 rc = setError(rc,
8908 tr("Failed to set size of '%s' to size of '%s'"),
8909 targetName.c_str(), sourceName.c_str());
8910 delete pMediumLockListForResize;
8911 throw rc;
8912 }
8913 delete pMediumLockListForResize;
8914 }
8915 else
8916 {
8917 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8918 HRESULT rc = setError(VBOX_E_NOT_SUPPORTED,
8919 tr("Sizes of '%s' and '%s' are different and medium format does not support resing"),
8920 sourceName.c_str(), targetName.c_str());
8921 throw rc;
8922 }
8923 }
8924
8925 task.GetProgressObject()->SetNextOperation(BstrFmt(tr("Merging medium '%s' to '%s'"),
8926 i_getName().c_str(),
8927 targetName.c_str()).raw(),
8928 1);
8929
8930 PVDISK hdd;
8931 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
8932 ComAssertRCThrow(vrc, E_FAIL);
8933
8934 try
8935 {
8936 // Similar code appears in SessionMachine::onlineMergeMedium, so
8937 // if you make any changes below check whether they are applicable
8938 // in that context as well.
8939
8940 unsigned uTargetIdx = VD_LAST_IMAGE;
8941 unsigned uSourceIdx = VD_LAST_IMAGE;
8942 /* Open all media in the chain. */
8943 MediumLockList::Base::iterator lockListBegin =
8944 task.mpMediumLockList->GetBegin();
8945 MediumLockList::Base::iterator lockListEnd =
8946 task.mpMediumLockList->GetEnd();
8947 unsigned i = 0;
8948 for (MediumLockList::Base::iterator it = lockListBegin;
8949 it != lockListEnd;
8950 ++it)
8951 {
8952 MediumLock &mediumLock = *it;
8953 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
8954
8955 if (pMedium == this)
8956 uSourceIdx = i;
8957 else if (pMedium == pTarget)
8958 uTargetIdx = i;
8959
8960 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
8961
8962 /*
8963 * complex sanity (sane complexity)
8964 *
8965 * The current medium must be in the Deleting (medium is merged)
8966 * or LockedRead (parent medium) state if it is not the target.
8967 * If it is the target it must be in the LockedWrite state.
8968 */
8969 Assert( ( pMedium != pTarget
8970 && ( pMedium->m->state == MediumState_Deleting
8971 || pMedium->m->state == MediumState_LockedRead))
8972 || ( pMedium == pTarget
8973 && pMedium->m->state == MediumState_LockedWrite));
8974 /*
8975 * Medium must be the target, in the LockedRead state
8976 * or Deleting state where it is not allowed to be attached
8977 * to a virtual machine.
8978 */
8979 Assert( pMedium == pTarget
8980 || pMedium->m->state == MediumState_LockedRead
8981 || ( pMedium->m->backRefs.size() == 0
8982 && pMedium->m->state == MediumState_Deleting));
8983 /* The source medium must be in Deleting state. */
8984 Assert( pMedium != this
8985 || pMedium->m->state == MediumState_Deleting);
8986
8987 unsigned uOpenFlags = VD_OPEN_FLAGS_NORMAL;
8988
8989 if ( pMedium->m->state == MediumState_LockedRead
8990 || pMedium->m->state == MediumState_Deleting)
8991 uOpenFlags = VD_OPEN_FLAGS_READONLY;
8992 if (pMedium->m->type == MediumType_Shareable)
8993 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
8994
8995 /* Open the medium */
8996 vrc = VDOpen(hdd,
8997 pMedium->m->strFormat.c_str(),
8998 pMedium->m->strLocationFull.c_str(),
8999 uOpenFlags | m->uOpenFlagsDef,
9000 pMedium->m->vdImageIfaces);
9001 if (RT_FAILURE(vrc))
9002 throw vrc;
9003
9004 i++;
9005 }
9006
9007 ComAssertThrow( uSourceIdx != VD_LAST_IMAGE
9008 && uTargetIdx != VD_LAST_IMAGE, E_FAIL);
9009
9010 vrc = VDMerge(hdd, uSourceIdx, uTargetIdx,
9011 task.mVDOperationIfaces);
9012 if (RT_FAILURE(vrc))
9013 throw vrc;
9014
9015 /* update parent UUIDs */
9016 if (!task.mfMergeForward)
9017 {
9018 /* we need to update UUIDs of all source's children
9019 * which cannot be part of the container at once so
9020 * add each one in there individually */
9021 if (task.mpChildrenToReparent)
9022 {
9023 MediumLockList::Base::iterator childrenBegin = task.mpChildrenToReparent->GetBegin();
9024 MediumLockList::Base::iterator childrenEnd = task.mpChildrenToReparent->GetEnd();
9025 for (MediumLockList::Base::iterator it = childrenBegin;
9026 it != childrenEnd;
9027 ++it)
9028 {
9029 Medium *pMedium = it->GetMedium();
9030 /* VD_OPEN_FLAGS_INFO since UUID is wrong yet */
9031 vrc = VDOpen(hdd,
9032 pMedium->m->strFormat.c_str(),
9033 pMedium->m->strLocationFull.c_str(),
9034 VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
9035 pMedium->m->vdImageIfaces);
9036 if (RT_FAILURE(vrc))
9037 throw vrc;
9038
9039 vrc = VDSetParentUuid(hdd, VD_LAST_IMAGE,
9040 pTarget->m->id.raw());
9041 if (RT_FAILURE(vrc))
9042 throw vrc;
9043
9044 vrc = VDClose(hdd, false /* fDelete */);
9045 if (RT_FAILURE(vrc))
9046 throw vrc;
9047 }
9048 }
9049 }
9050 }
9051 catch (HRESULT aRC) { rcTmp = aRC; }
9052 catch (int aVRC)
9053 {
9054 rcTmp = setErrorBoth(VBOX_E_FILE_ERROR, aVRC,
9055 tr("Could not merge the medium '%s' to '%s'%s"),
9056 m->strLocationFull.c_str(),
9057 pTarget->m->strLocationFull.c_str(),
9058 i_vdError(aVRC).c_str());
9059 }
9060
9061 VDDestroy(hdd);
9062 }
9063 catch (HRESULT aRC) { rcTmp = aRC; }
9064
9065 ErrorInfoKeeper eik;
9066 MultiResult mrc(rcTmp);
9067 HRESULT rc2;
9068
9069 std::set<ComObjPtr<Medium> > pMediumsForNotify;
9070 std::map<Guid, DeviceType_T> uIdsForNotify;
9071
9072 if (SUCCEEDED(mrc))
9073 {
9074 /* all media but the target were successfully deleted by
9075 * VDMerge; reparent the last one and uninitialize deleted media. */
9076
9077 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
9078
9079 if (task.mfMergeForward)
9080 {
9081 /* first, unregister the target since it may become a base
9082 * medium which needs re-registration */
9083 rc2 = m->pVirtualBox->i_unregisterMedium(pTarget);
9084 AssertComRC(rc2);
9085
9086 /* then, reparent it and disconnect the deleted branch at both ends
9087 * (chain->parent() is source's parent). Depth check above. */
9088 pTarget->i_deparent();
9089 pTarget->i_setParent(task.mParentForTarget);
9090 if (task.mParentForTarget)
9091 {
9092 i_deparent();
9093 if (task.NotifyAboutChanges())
9094 pMediumsForNotify.insert(task.mParentForTarget);
9095 }
9096
9097 /* then, register again */
9098 ComObjPtr<Medium> pMedium;
9099 rc2 = m->pVirtualBox->i_registerMedium(pTarget, &pMedium,
9100 treeLock);
9101 AssertComRC(rc2);
9102 }
9103 else
9104 {
9105 Assert(pTarget->i_getChildren().size() == 1);
9106 Medium *targetChild = pTarget->i_getChildren().front();
9107
9108 /* disconnect the deleted branch at the elder end */
9109 targetChild->i_deparent();
9110
9111 /* reparent source's children and disconnect the deleted
9112 * branch at the younger end */
9113 if (task.mpChildrenToReparent)
9114 {
9115 /* obey {parent,child} lock order */
9116 AutoWriteLock sourceLock(this COMMA_LOCKVAL_SRC_POS);
9117
9118 MediumLockList::Base::iterator childrenBegin = task.mpChildrenToReparent->GetBegin();
9119 MediumLockList::Base::iterator childrenEnd = task.mpChildrenToReparent->GetEnd();
9120 for (MediumLockList::Base::iterator it = childrenBegin;
9121 it != childrenEnd;
9122 ++it)
9123 {
9124 Medium *pMedium = it->GetMedium();
9125 AutoWriteLock childLock(pMedium COMMA_LOCKVAL_SRC_POS);
9126
9127 pMedium->i_deparent(); // removes pMedium from source
9128 // no depth check, reduces depth
9129 pMedium->i_setParent(pTarget);
9130
9131 if (task.NotifyAboutChanges())
9132 pMediumsForNotify.insert(pMedium);
9133 }
9134 }
9135 pMediumsForNotify.insert(pTarget);
9136 }
9137
9138 /* unregister and uninitialize all media removed by the merge */
9139 MediumLockList::Base::iterator lockListBegin =
9140 task.mpMediumLockList->GetBegin();
9141 MediumLockList::Base::iterator lockListEnd =
9142 task.mpMediumLockList->GetEnd();
9143 for (MediumLockList::Base::iterator it = lockListBegin;
9144 it != lockListEnd;
9145 )
9146 {
9147 MediumLock &mediumLock = *it;
9148 /* Create a real copy of the medium pointer, as the medium
9149 * lock deletion below would invalidate the referenced object. */
9150 const ComObjPtr<Medium> pMedium = mediumLock.GetMedium();
9151
9152 /* The target and all media not merged (readonly) are skipped */
9153 if ( pMedium == pTarget
9154 || pMedium->m->state == MediumState_LockedRead)
9155 {
9156 ++it;
9157 continue;
9158 }
9159
9160 uIdsForNotify[pMedium->i_getId()] = pMedium->i_getDeviceType();
9161 rc2 = pMedium->m->pVirtualBox->i_unregisterMedium(pMedium);
9162 AssertComRC(rc2);
9163
9164 /* now, uninitialize the deleted medium (note that
9165 * due to the Deleting state, uninit() will not touch
9166 * the parent-child relationship so we need to
9167 * uninitialize each disk individually) */
9168
9169 /* note that the operation initiator medium (which is
9170 * normally also the source medium) is a special case
9171 * -- there is one more caller added by Task to it which
9172 * we must release. Also, if we are in sync mode, the
9173 * caller may still hold an AutoCaller instance for it
9174 * and therefore we cannot uninit() it (it's therefore
9175 * the caller's responsibility) */
9176 if (pMedium == this)
9177 {
9178 Assert(i_getChildren().size() == 0);
9179 Assert(m->backRefs.size() == 0);
9180 task.mMediumCaller.release();
9181 }
9182
9183 /* Delete the medium lock list entry, which also releases the
9184 * caller added by MergeChain before uninit() and updates the
9185 * iterator to point to the right place. */
9186 rc2 = task.mpMediumLockList->RemoveByIterator(it);
9187 AssertComRC(rc2);
9188
9189 if (task.isAsync() || pMedium != this)
9190 {
9191 treeLock.release();
9192 pMedium->uninit();
9193 treeLock.acquire();
9194 }
9195 }
9196 }
9197
9198 i_markRegistriesModified();
9199 if (task.isAsync())
9200 {
9201 // in asynchronous mode, save settings now
9202 eik.restore();
9203 m->pVirtualBox->i_saveModifiedRegistries();
9204 eik.fetch();
9205 }
9206
9207 if (FAILED(mrc))
9208 {
9209 /* Here we come if either VDMerge() failed (in which case we
9210 * assume that it tried to do everything to make a further
9211 * retry possible -- e.g. not deleted intermediate media
9212 * and so on) or VirtualBox::saveRegistries() failed (where we
9213 * should have the original tree but with intermediate storage
9214 * units deleted by VDMerge()). We have to only restore states
9215 * (through the MergeChain dtor) unless we are run synchronously
9216 * in which case it's the responsibility of the caller as stated
9217 * in the mergeTo() docs. The latter also implies that we
9218 * don't own the merge chain, so release it in this case. */
9219 if (task.isAsync())
9220 i_cancelMergeTo(task.mpChildrenToReparent, task.mpMediumLockList);
9221 }
9222 else if (task.NotifyAboutChanges())
9223 {
9224 for (std::set<ComObjPtr<Medium> >::const_iterator it = pMediumsForNotify.begin();
9225 it != pMediumsForNotify.end();
9226 ++it)
9227 {
9228 if (it->isNotNull())
9229 m->pVirtualBox->i_onMediumConfigChanged(*it);
9230 }
9231 for (std::map<Guid, DeviceType_T>::const_iterator it = uIdsForNotify.begin();
9232 it != uIdsForNotify.end();
9233 ++it)
9234 {
9235 m->pVirtualBox->i_onMediumRegistered(it->first, it->second, FALSE);
9236 }
9237 }
9238
9239 return mrc;
9240}
9241
9242/**
9243 * Implementation code for the "clone" task.
9244 *
9245 * This only gets started from Medium::CloneTo() and always runs asynchronously.
9246 * As a result, we always save the VirtualBox.xml file when we're done here.
9247 *
9248 * @param task
9249 * @return
9250 */
9251HRESULT Medium::i_taskCloneHandler(Medium::CloneTask &task)
9252{
9253 /** @todo r=klaus The code below needs to be double checked with regard
9254 * to lock order violations, it probably causes lock order issues related
9255 * to the AutoCaller usage. */
9256 HRESULT rcTmp = S_OK;
9257
9258 const ComObjPtr<Medium> &pTarget = task.mTarget;
9259 const ComObjPtr<Medium> &pParent = task.mParent;
9260
9261 bool fCreatingTarget = false;
9262
9263 uint64_t size = 0, logicalSize = 0;
9264 MediumVariant_T variant = MediumVariant_Standard;
9265 bool fGenerateUuid = false;
9266
9267 try
9268 {
9269 if (!pParent.isNull())
9270 {
9271
9272 if (pParent->i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX)
9273 {
9274 AutoReadLock plock(pParent COMMA_LOCKVAL_SRC_POS);
9275 throw setError(VBOX_E_INVALID_OBJECT_STATE,
9276 tr("Cannot clone image for medium '%s', because it exceeds the medium tree depth limit. Please merge some images which you no longer need"),
9277 pParent->m->strLocationFull.c_str());
9278 }
9279 }
9280
9281 /* Lock all in {parent,child} order. The lock is also used as a
9282 * signal from the task initiator (which releases it only after
9283 * RTThreadCreate()) that we can start the job. */
9284 AutoMultiWriteLock3 thisLock(this, pTarget, pParent COMMA_LOCKVAL_SRC_POS);
9285
9286 fCreatingTarget = pTarget->m->state == MediumState_Creating;
9287
9288 /* The object may request a specific UUID (through a special form of
9289 * the moveTo() argument). Otherwise we have to generate it */
9290 Guid targetId = pTarget->m->id;
9291
9292 fGenerateUuid = targetId.isZero();
9293 if (fGenerateUuid)
9294 {
9295 targetId.create();
9296 /* VirtualBox::registerMedium() will need UUID */
9297 unconst(pTarget->m->id) = targetId;
9298 }
9299
9300 PVDISK hdd;
9301 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
9302 ComAssertRCThrow(vrc, E_FAIL);
9303
9304 try
9305 {
9306 /* Open all media in the source chain. */
9307 MediumLockList::Base::const_iterator sourceListBegin =
9308 task.mpSourceMediumLockList->GetBegin();
9309 MediumLockList::Base::const_iterator sourceListEnd =
9310 task.mpSourceMediumLockList->GetEnd();
9311 for (MediumLockList::Base::const_iterator it = sourceListBegin;
9312 it != sourceListEnd;
9313 ++it)
9314 {
9315 const MediumLock &mediumLock = *it;
9316 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
9317 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
9318
9319 /* sanity check */
9320 Assert(pMedium->m->state == MediumState_LockedRead);
9321
9322 /** Open all media in read-only mode. */
9323 vrc = VDOpen(hdd,
9324 pMedium->m->strFormat.c_str(),
9325 pMedium->m->strLocationFull.c_str(),
9326 VD_OPEN_FLAGS_READONLY | m->uOpenFlagsDef,
9327 pMedium->m->vdImageIfaces);
9328 if (RT_FAILURE(vrc))
9329 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9330 tr("Could not open the medium storage unit '%s'%s"),
9331 pMedium->m->strLocationFull.c_str(),
9332 i_vdError(vrc).c_str());
9333 }
9334
9335 Utf8Str targetFormat(pTarget->m->strFormat);
9336 Utf8Str targetLocation(pTarget->m->strLocationFull);
9337 uint64_t capabilities = pTarget->m->formatObj->i_getCapabilities();
9338
9339 Assert( pTarget->m->state == MediumState_Creating
9340 || pTarget->m->state == MediumState_LockedWrite);
9341 Assert(m->state == MediumState_LockedRead);
9342 Assert( pParent.isNull()
9343 || pParent->m->state == MediumState_LockedRead);
9344
9345 /* unlock before the potentially lengthy operation */
9346 thisLock.release();
9347
9348 /* ensure the target directory exists */
9349 if (capabilities & MediumFormatCapabilities_File)
9350 {
9351 HRESULT rc = VirtualBox::i_ensureFilePathExists(targetLocation,
9352 !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
9353 if (FAILED(rc))
9354 throw rc;
9355 }
9356
9357 PVDISK targetHdd;
9358 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &targetHdd);
9359 ComAssertRCThrow(vrc, E_FAIL);
9360
9361 try
9362 {
9363 /* Open all media in the target chain. */
9364 MediumLockList::Base::const_iterator targetListBegin =
9365 task.mpTargetMediumLockList->GetBegin();
9366 MediumLockList::Base::const_iterator targetListEnd =
9367 task.mpTargetMediumLockList->GetEnd();
9368 for (MediumLockList::Base::const_iterator it = targetListBegin;
9369 it != targetListEnd;
9370 ++it)
9371 {
9372 const MediumLock &mediumLock = *it;
9373 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
9374
9375 /* If the target medium is not created yet there's no
9376 * reason to open it. */
9377 if (pMedium == pTarget && fCreatingTarget)
9378 continue;
9379
9380 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
9381
9382 /* sanity check */
9383 Assert( pMedium->m->state == MediumState_LockedRead
9384 || pMedium->m->state == MediumState_LockedWrite);
9385
9386 unsigned uOpenFlags = VD_OPEN_FLAGS_NORMAL;
9387 if (pMedium->m->state != MediumState_LockedWrite)
9388 uOpenFlags = VD_OPEN_FLAGS_READONLY;
9389 if (pMedium->m->type == MediumType_Shareable)
9390 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
9391
9392 /* Open all media in appropriate mode. */
9393 vrc = VDOpen(targetHdd,
9394 pMedium->m->strFormat.c_str(),
9395 pMedium->m->strLocationFull.c_str(),
9396 uOpenFlags | m->uOpenFlagsDef,
9397 pMedium->m->vdImageIfaces);
9398 if (RT_FAILURE(vrc))
9399 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9400 tr("Could not open the medium storage unit '%s'%s"),
9401 pMedium->m->strLocationFull.c_str(),
9402 i_vdError(vrc).c_str());
9403 }
9404
9405 /* target isn't locked, but no changing data is accessed */
9406 if (task.midxSrcImageSame == UINT32_MAX)
9407 {
9408 vrc = VDCopy(hdd,
9409 VD_LAST_IMAGE,
9410 targetHdd,
9411 targetFormat.c_str(),
9412 (fCreatingTarget) ? targetLocation.c_str() : (char *)NULL,
9413 false /* fMoveByRename */,
9414 0 /* cbSize */,
9415 task.mVariant & ~(MediumVariant_NoCreateDir | MediumVariant_Formatted),
9416 targetId.raw(),
9417 VD_OPEN_FLAGS_NORMAL | m->uOpenFlagsDef,
9418 NULL /* pVDIfsOperation */,
9419 pTarget->m->vdImageIfaces,
9420 task.mVDOperationIfaces);
9421 }
9422 else
9423 {
9424 vrc = VDCopyEx(hdd,
9425 VD_LAST_IMAGE,
9426 targetHdd,
9427 targetFormat.c_str(),
9428 (fCreatingTarget) ? targetLocation.c_str() : (char *)NULL,
9429 false /* fMoveByRename */,
9430 0 /* cbSize */,
9431 task.midxSrcImageSame,
9432 task.midxDstImageSame,
9433 task.mVariant & ~(MediumVariant_NoCreateDir | MediumVariant_Formatted),
9434 targetId.raw(),
9435 VD_OPEN_FLAGS_NORMAL | m->uOpenFlagsDef,
9436 NULL /* pVDIfsOperation */,
9437 pTarget->m->vdImageIfaces,
9438 task.mVDOperationIfaces);
9439 }
9440 if (RT_FAILURE(vrc))
9441 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9442 tr("Could not create the clone medium '%s'%s"),
9443 targetLocation.c_str(), i_vdError(vrc).c_str());
9444
9445 size = VDGetFileSize(targetHdd, VD_LAST_IMAGE);
9446 logicalSize = VDGetSize(targetHdd, VD_LAST_IMAGE);
9447 unsigned uImageFlags;
9448 vrc = VDGetImageFlags(targetHdd, 0, &uImageFlags);
9449 if (RT_SUCCESS(vrc))
9450 variant = (MediumVariant_T)uImageFlags;
9451 }
9452 catch (HRESULT aRC) { rcTmp = aRC; }
9453
9454 VDDestroy(targetHdd);
9455 }
9456 catch (HRESULT aRC) { rcTmp = aRC; }
9457
9458 VDDestroy(hdd);
9459 }
9460 catch (HRESULT aRC) { rcTmp = aRC; }
9461
9462 ErrorInfoKeeper eik;
9463 MultiResult mrc(rcTmp);
9464
9465 /* Only do the parent changes for newly created media. */
9466 if (SUCCEEDED(mrc) && fCreatingTarget)
9467 {
9468 /* we set m->pParent & children() */
9469 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
9470
9471 Assert(pTarget->m->pParent.isNull());
9472
9473 if (pParent)
9474 {
9475 /* Associate the clone with the parent and deassociate
9476 * from VirtualBox. Depth check above. */
9477 pTarget->i_setParent(pParent);
9478
9479 /* register with mVirtualBox as the last step and move to
9480 * Created state only on success (leaving an orphan file is
9481 * better than breaking media registry consistency) */
9482 eik.restore();
9483 ComObjPtr<Medium> pMedium;
9484 mrc = pParent->m->pVirtualBox->i_registerMedium(pTarget, &pMedium,
9485 treeLock);
9486 Assert( FAILED(mrc)
9487 || pTarget == pMedium);
9488 eik.fetch();
9489
9490 if (FAILED(mrc))
9491 /* break parent association on failure to register */
9492 pTarget->i_deparent(); // removes target from parent
9493 }
9494 else
9495 {
9496 /* just register */
9497 eik.restore();
9498 ComObjPtr<Medium> pMedium;
9499 mrc = m->pVirtualBox->i_registerMedium(pTarget, &pMedium,
9500 treeLock);
9501 Assert( FAILED(mrc)
9502 || pTarget == pMedium);
9503 eik.fetch();
9504 }
9505 }
9506
9507 if (fCreatingTarget)
9508 {
9509 AutoWriteLock mLock(pTarget COMMA_LOCKVAL_SRC_POS);
9510
9511 if (SUCCEEDED(mrc))
9512 {
9513 pTarget->m->state = MediumState_Created;
9514
9515 pTarget->m->size = size;
9516 pTarget->m->logicalSize = logicalSize;
9517 pTarget->m->variant = variant;
9518 }
9519 else
9520 {
9521 /* back to NotCreated on failure */
9522 pTarget->m->state = MediumState_NotCreated;
9523
9524 /* reset UUID to prevent it from being reused next time */
9525 if (fGenerateUuid)
9526 unconst(pTarget->m->id).clear();
9527 }
9528 }
9529
9530 /* Copy any filter related settings over to the target. */
9531 if (SUCCEEDED(mrc))
9532 {
9533 /* Copy any filter related settings over. */
9534 ComObjPtr<Medium> pBase = i_getBase();
9535 ComObjPtr<Medium> pTargetBase = pTarget->i_getBase();
9536 std::vector<com::Utf8Str> aFilterPropNames;
9537 std::vector<com::Utf8Str> aFilterPropValues;
9538 mrc = pBase->i_getFilterProperties(aFilterPropNames, aFilterPropValues);
9539 if (SUCCEEDED(mrc))
9540 {
9541 /* Go through the properties and add them to the target medium. */
9542 for (unsigned idx = 0; idx < aFilterPropNames.size(); idx++)
9543 {
9544 mrc = pTargetBase->i_setPropertyDirect(aFilterPropNames[idx], aFilterPropValues[idx]);
9545 if (FAILED(mrc)) break;
9546 }
9547
9548 // now, at the end of this task (always asynchronous), save the settings
9549 if (SUCCEEDED(mrc))
9550 {
9551 // save the settings
9552 i_markRegistriesModified();
9553 /* collect multiple errors */
9554 eik.restore();
9555 m->pVirtualBox->i_saveModifiedRegistries();
9556 eik.fetch();
9557
9558 if (task.NotifyAboutChanges())
9559 {
9560 if (!fCreatingTarget)
9561 {
9562 if (!aFilterPropNames.empty())
9563 m->pVirtualBox->i_onMediumConfigChanged(pTargetBase);
9564 m->pVirtualBox->i_onMediumConfigChanged(pParent);
9565 }
9566 else
9567 {
9568 m->pVirtualBox->i_onMediumRegistered(pTarget->i_getId(), pTarget->i_getDeviceType(), TRUE);
9569 }
9570 }
9571 }
9572 }
9573 }
9574
9575 /* Everything is explicitly unlocked when the task exits,
9576 * as the task destruction also destroys the source chain. */
9577
9578 /* Make sure the source chain is released early. It could happen
9579 * that we get a deadlock in Appliance::Import when Medium::Close
9580 * is called & the source chain is released at the same time. */
9581 task.mpSourceMediumLockList->Clear();
9582
9583 return mrc;
9584}
9585
9586/**
9587 * Implementation code for the "move" task.
9588 *
9589 * This only gets started from Medium::MoveTo() and always
9590 * runs asynchronously.
9591 *
9592 * @param task
9593 * @return
9594 */
9595HRESULT Medium::i_taskMoveHandler(Medium::MoveTask &task)
9596{
9597
9598 HRESULT rcOut = S_OK;
9599
9600 /* pTarget is equal "this" in our case */
9601 const ComObjPtr<Medium> &pTarget = task.mMedium;
9602
9603 uint64_t size = 0; NOREF(size);
9604 uint64_t logicalSize = 0; NOREF(logicalSize);
9605 MediumVariant_T variant = MediumVariant_Standard; NOREF(variant);
9606
9607 /*
9608 * it's exactly moving, not cloning
9609 */
9610 if (!i_isMoveOperation(pTarget))
9611 {
9612 HRESULT rc = setError(VBOX_E_FILE_ERROR,
9613 tr("Wrong preconditions for moving the medium %s"),
9614 pTarget->m->strLocationFull.c_str());
9615 return rc;
9616 }
9617
9618 try
9619 {
9620 /* Lock all in {parent,child} order. The lock is also used as a
9621 * signal from the task initiator (which releases it only after
9622 * RTThreadCreate()) that we can start the job. */
9623
9624 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
9625
9626 PVDISK hdd;
9627 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
9628 ComAssertRCThrow(vrc, E_FAIL);
9629
9630 try
9631 {
9632 /* Open all media in the source chain. */
9633 MediumLockList::Base::const_iterator sourceListBegin =
9634 task.mpMediumLockList->GetBegin();
9635 MediumLockList::Base::const_iterator sourceListEnd =
9636 task.mpMediumLockList->GetEnd();
9637 for (MediumLockList::Base::const_iterator it = sourceListBegin;
9638 it != sourceListEnd;
9639 ++it)
9640 {
9641 const MediumLock &mediumLock = *it;
9642 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
9643 AutoWriteLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
9644
9645 /* sanity check */
9646 Assert(pMedium->m->state == MediumState_LockedWrite);
9647
9648 vrc = VDOpen(hdd,
9649 pMedium->m->strFormat.c_str(),
9650 pMedium->m->strLocationFull.c_str(),
9651 VD_OPEN_FLAGS_NORMAL,
9652 pMedium->m->vdImageIfaces);
9653 if (RT_FAILURE(vrc))
9654 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9655 tr("Could not open the medium storage unit '%s'%s"),
9656 pMedium->m->strLocationFull.c_str(),
9657 i_vdError(vrc).c_str());
9658 }
9659
9660 /* we can directly use pTarget->m->"variables" but for better reading we use local copies */
9661 Guid targetId = pTarget->m->id;
9662 Utf8Str targetFormat(pTarget->m->strFormat);
9663 uint64_t targetCapabilities = pTarget->m->formatObj->i_getCapabilities();
9664
9665 /*
9666 * change target location
9667 * m->strNewLocationFull has been set already together with m->fMoveThisMedium in
9668 * i_preparationForMoving()
9669 */
9670 Utf8Str targetLocation = i_getNewLocationForMoving();
9671
9672 /* unlock before the potentially lengthy operation */
9673 thisLock.release();
9674
9675 /* ensure the target directory exists */
9676 if (targetCapabilities & MediumFormatCapabilities_File)
9677 {
9678 HRESULT rc = VirtualBox::i_ensureFilePathExists(targetLocation,
9679 !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
9680 if (FAILED(rc))
9681 throw rc;
9682 }
9683
9684 try
9685 {
9686 vrc = VDCopy(hdd,
9687 VD_LAST_IMAGE,
9688 hdd,
9689 targetFormat.c_str(),
9690 targetLocation.c_str(),
9691 true /* fMoveByRename */,
9692 0 /* cbSize */,
9693 VD_IMAGE_FLAGS_NONE,
9694 targetId.raw(),
9695 VD_OPEN_FLAGS_NORMAL,
9696 NULL /* pVDIfsOperation */,
9697 NULL,
9698 NULL);
9699 if (RT_FAILURE(vrc))
9700 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9701 tr("Could not move medium '%s'%s"),
9702 targetLocation.c_str(), i_vdError(vrc).c_str());
9703 size = VDGetFileSize(hdd, VD_LAST_IMAGE);
9704 logicalSize = VDGetSize(hdd, VD_LAST_IMAGE);
9705 unsigned uImageFlags;
9706 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
9707 if (RT_SUCCESS(vrc))
9708 variant = (MediumVariant_T)uImageFlags;
9709
9710 /*
9711 * set current location, because VDCopy\VDCopyEx doesn't do it.
9712 * also reset moving flag
9713 */
9714 i_resetMoveOperationData();
9715 m->strLocationFull = targetLocation;
9716
9717 }
9718 catch (HRESULT aRC) { rcOut = aRC; }
9719
9720 }
9721 catch (HRESULT aRC) { rcOut = aRC; }
9722
9723 VDDestroy(hdd);
9724 }
9725 catch (HRESULT aRC) { rcOut = aRC; }
9726
9727 ErrorInfoKeeper eik;
9728 MultiResult mrc(rcOut);
9729
9730 // now, at the end of this task (always asynchronous), save the settings
9731 if (SUCCEEDED(mrc))
9732 {
9733 // save the settings
9734 i_markRegistriesModified();
9735 /* collect multiple errors */
9736 eik.restore();
9737 m->pVirtualBox->i_saveModifiedRegistries();
9738 eik.fetch();
9739 }
9740
9741 /* Everything is explicitly unlocked when the task exits,
9742 * as the task destruction also destroys the source chain. */
9743
9744 task.mpMediumLockList->Clear();
9745
9746 if (task.NotifyAboutChanges() && SUCCEEDED(mrc))
9747 m->pVirtualBox->i_onMediumConfigChanged(this);
9748 return mrc;
9749}
9750
9751/**
9752 * Implementation code for the "delete" task.
9753 *
9754 * This task always gets started from Medium::deleteStorage() and can run
9755 * synchronously or asynchronously depending on the "wait" parameter passed to
9756 * that function.
9757 *
9758 * @param task
9759 * @return
9760 */
9761HRESULT Medium::i_taskDeleteHandler(Medium::DeleteTask &task)
9762{
9763 NOREF(task);
9764 HRESULT rc = S_OK;
9765
9766 try
9767 {
9768 /* The lock is also used as a signal from the task initiator (which
9769 * releases it only after RTThreadCreate()) that we can start the job */
9770 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
9771
9772 PVDISK hdd;
9773 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
9774 ComAssertRCThrow(vrc, E_FAIL);
9775
9776 Utf8Str format(m->strFormat);
9777 Utf8Str location(m->strLocationFull);
9778
9779 /* unlock before the potentially lengthy operation */
9780 Assert(m->state == MediumState_Deleting);
9781 thisLock.release();
9782
9783 try
9784 {
9785 vrc = VDOpen(hdd,
9786 format.c_str(),
9787 location.c_str(),
9788 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
9789 m->vdImageIfaces);
9790 if (RT_SUCCESS(vrc))
9791 vrc = VDClose(hdd, true /* fDelete */);
9792
9793 if (RT_FAILURE(vrc) && vrc != VERR_FILE_NOT_FOUND)
9794 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9795 tr("Could not delete the medium storage unit '%s'%s"),
9796 location.c_str(), i_vdError(vrc).c_str());
9797
9798 }
9799 catch (HRESULT aRC) { rc = aRC; }
9800
9801 VDDestroy(hdd);
9802 }
9803 catch (HRESULT aRC) { rc = aRC; }
9804
9805 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
9806
9807 /* go to the NotCreated state even on failure since the storage
9808 * may have been already partially deleted and cannot be used any
9809 * more. One will be able to manually re-open the storage if really
9810 * needed to re-register it. */
9811 m->state = MediumState_NotCreated;
9812
9813 /* Reset UUID to prevent Create* from reusing it again */
9814 unconst(m->id).clear();
9815
9816 if (task.NotifyAboutChanges() && SUCCEEDED(rc) && m->pParent.isNotNull())
9817 m->pVirtualBox->i_onMediumConfigChanged(m->pParent);
9818
9819 return rc;
9820}
9821
9822/**
9823 * Implementation code for the "reset" task.
9824 *
9825 * This always gets started asynchronously from Medium::Reset().
9826 *
9827 * @param task
9828 * @return
9829 */
9830HRESULT Medium::i_taskResetHandler(Medium::ResetTask &task)
9831{
9832 HRESULT rc = S_OK;
9833
9834 uint64_t size = 0, logicalSize = 0;
9835 MediumVariant_T variant = MediumVariant_Standard;
9836
9837 try
9838 {
9839 /* The lock is also used as a signal from the task initiator (which
9840 * releases it only after RTThreadCreate()) that we can start the job */
9841 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
9842
9843 /// @todo Below we use a pair of delete/create operations to reset
9844 /// the diff contents but the most efficient way will of course be
9845 /// to add a VDResetDiff() API call
9846
9847 PVDISK hdd;
9848 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
9849 ComAssertRCThrow(vrc, E_FAIL);
9850
9851 Guid id = m->id;
9852 Utf8Str format(m->strFormat);
9853 Utf8Str location(m->strLocationFull);
9854
9855 Medium *pParent = m->pParent;
9856 Guid parentId = pParent->m->id;
9857 Utf8Str parentFormat(pParent->m->strFormat);
9858 Utf8Str parentLocation(pParent->m->strLocationFull);
9859
9860 Assert(m->state == MediumState_LockedWrite);
9861
9862 /* unlock before the potentially lengthy operation */
9863 thisLock.release();
9864
9865 try
9866 {
9867 /* Open all media in the target chain but the last. */
9868 MediumLockList::Base::const_iterator targetListBegin =
9869 task.mpMediumLockList->GetBegin();
9870 MediumLockList::Base::const_iterator targetListEnd =
9871 task.mpMediumLockList->GetEnd();
9872 for (MediumLockList::Base::const_iterator it = targetListBegin;
9873 it != targetListEnd;
9874 ++it)
9875 {
9876 const MediumLock &mediumLock = *it;
9877 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
9878
9879 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
9880
9881 /* sanity check, "this" is checked above */
9882 Assert( pMedium == this
9883 || pMedium->m->state == MediumState_LockedRead);
9884
9885 /* Open all media in appropriate mode. */
9886 vrc = VDOpen(hdd,
9887 pMedium->m->strFormat.c_str(),
9888 pMedium->m->strLocationFull.c_str(),
9889 VD_OPEN_FLAGS_READONLY | m->uOpenFlagsDef,
9890 pMedium->m->vdImageIfaces);
9891 if (RT_FAILURE(vrc))
9892 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9893 tr("Could not open the medium storage unit '%s'%s"),
9894 pMedium->m->strLocationFull.c_str(),
9895 i_vdError(vrc).c_str());
9896
9897 /* Done when we hit the media which should be reset */
9898 if (pMedium == this)
9899 break;
9900 }
9901
9902 /* first, delete the storage unit */
9903 vrc = VDClose(hdd, true /* fDelete */);
9904 if (RT_FAILURE(vrc))
9905 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9906 tr("Could not delete the medium storage unit '%s'%s"),
9907 location.c_str(), i_vdError(vrc).c_str());
9908
9909 /* next, create it again */
9910 vrc = VDOpen(hdd,
9911 parentFormat.c_str(),
9912 parentLocation.c_str(),
9913 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
9914 m->vdImageIfaces);
9915 if (RT_FAILURE(vrc))
9916 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9917 tr("Could not open the medium storage unit '%s'%s"),
9918 parentLocation.c_str(), i_vdError(vrc).c_str());
9919
9920 vrc = VDCreateDiff(hdd,
9921 format.c_str(),
9922 location.c_str(),
9923 /// @todo use the same medium variant as before
9924 VD_IMAGE_FLAGS_NONE,
9925 NULL,
9926 id.raw(),
9927 parentId.raw(),
9928 VD_OPEN_FLAGS_NORMAL,
9929 m->vdImageIfaces,
9930 task.mVDOperationIfaces);
9931 if (RT_FAILURE(vrc))
9932 {
9933 if (vrc == VERR_VD_INVALID_TYPE)
9934 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9935 tr("Parameters for creating the differencing medium storage unit '%s' are invalid%s"),
9936 location.c_str(), i_vdError(vrc).c_str());
9937 else
9938 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
9939 tr("Could not create the differencing medium storage unit '%s'%s"),
9940 location.c_str(), i_vdError(vrc).c_str());
9941 }
9942
9943 size = VDGetFileSize(hdd, VD_LAST_IMAGE);
9944 logicalSize = VDGetSize(hdd, VD_LAST_IMAGE);
9945 unsigned uImageFlags;
9946 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
9947 if (RT_SUCCESS(vrc))
9948 variant = (MediumVariant_T)uImageFlags;
9949 }
9950 catch (HRESULT aRC) { rc = aRC; }
9951
9952 VDDestroy(hdd);
9953 }
9954 catch (HRESULT aRC) { rc = aRC; }
9955
9956 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
9957
9958 m->size = size;
9959 m->logicalSize = logicalSize;
9960 m->variant = variant;
9961
9962 if (task.NotifyAboutChanges() && SUCCEEDED(rc))
9963 m->pVirtualBox->i_onMediumConfigChanged(this);
9964
9965 /* Everything is explicitly unlocked when the task exits,
9966 * as the task destruction also destroys the media chain. */
9967
9968 return rc;
9969}
9970
9971/**
9972 * Implementation code for the "compact" task.
9973 *
9974 * @param task
9975 * @return
9976 */
9977HRESULT Medium::i_taskCompactHandler(Medium::CompactTask &task)
9978{
9979 HRESULT rc = S_OK;
9980
9981 /* Lock all in {parent,child} order. The lock is also used as a
9982 * signal from the task initiator (which releases it only after
9983 * RTThreadCreate()) that we can start the job. */
9984 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
9985
9986 try
9987 {
9988 PVDISK hdd;
9989 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
9990 ComAssertRCThrow(vrc, E_FAIL);
9991
9992 try
9993 {
9994 /* Open all media in the chain. */
9995 MediumLockList::Base::const_iterator mediumListBegin =
9996 task.mpMediumLockList->GetBegin();
9997 MediumLockList::Base::const_iterator mediumListEnd =
9998 task.mpMediumLockList->GetEnd();
9999 MediumLockList::Base::const_iterator mediumListLast =
10000 mediumListEnd;
10001 --mediumListLast;
10002 for (MediumLockList::Base::const_iterator it = mediumListBegin;
10003 it != mediumListEnd;
10004 ++it)
10005 {
10006 const MediumLock &mediumLock = *it;
10007 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
10008 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
10009
10010 /* sanity check */
10011 if (it == mediumListLast)
10012 Assert(pMedium->m->state == MediumState_LockedWrite);
10013 else
10014 Assert(pMedium->m->state == MediumState_LockedRead);
10015
10016 /* Open all media but last in read-only mode. Do not handle
10017 * shareable media, as compaction and sharing are mutually
10018 * exclusive. */
10019 vrc = VDOpen(hdd,
10020 pMedium->m->strFormat.c_str(),
10021 pMedium->m->strLocationFull.c_str(),
10022 m->uOpenFlagsDef | (it == mediumListLast ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY),
10023 pMedium->m->vdImageIfaces);
10024 if (RT_FAILURE(vrc))
10025 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10026 tr("Could not open the medium storage unit '%s'%s"),
10027 pMedium->m->strLocationFull.c_str(),
10028 i_vdError(vrc).c_str());
10029 }
10030
10031 Assert(m->state == MediumState_LockedWrite);
10032
10033 Utf8Str location(m->strLocationFull);
10034
10035 /* unlock before the potentially lengthy operation */
10036 thisLock.release();
10037
10038 vrc = VDCompact(hdd, VD_LAST_IMAGE, task.mVDOperationIfaces);
10039 if (RT_FAILURE(vrc))
10040 {
10041 if (vrc == VERR_NOT_SUPPORTED)
10042 throw setErrorBoth(VBOX_E_NOT_SUPPORTED, vrc,
10043 tr("Compacting is not yet supported for medium '%s'"),
10044 location.c_str());
10045 else if (vrc == VERR_NOT_IMPLEMENTED)
10046 throw setErrorBoth(E_NOTIMPL, vrc,
10047 tr("Compacting is not implemented, medium '%s'"),
10048 location.c_str());
10049 else
10050 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10051 tr("Could not compact medium '%s'%s"),
10052 location.c_str(),
10053 i_vdError(vrc).c_str());
10054 }
10055 }
10056 catch (HRESULT aRC) { rc = aRC; }
10057
10058 VDDestroy(hdd);
10059 }
10060 catch (HRESULT aRC) { rc = aRC; }
10061
10062 if (task.NotifyAboutChanges() && SUCCEEDED(rc))
10063 m->pVirtualBox->i_onMediumConfigChanged(this);
10064
10065 /* Everything is explicitly unlocked when the task exits,
10066 * as the task destruction also destroys the media chain. */
10067
10068 return rc;
10069}
10070
10071/**
10072 * Implementation code for the "resize" task.
10073 *
10074 * @param task
10075 * @return
10076 */
10077HRESULT Medium::i_taskResizeHandler(Medium::ResizeTask &task)
10078{
10079 HRESULT rc = S_OK;
10080
10081 uint64_t size = 0, logicalSize = 0;
10082
10083 try
10084 {
10085 /* The lock is also used as a signal from the task initiator (which
10086 * releases it only after RTThreadCreate()) that we can start the job */
10087 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
10088
10089 PVDISK hdd;
10090 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
10091 ComAssertRCThrow(vrc, E_FAIL);
10092
10093 try
10094 {
10095 /* Open all media in the chain. */
10096 MediumLockList::Base::const_iterator mediumListBegin =
10097 task.mpMediumLockList->GetBegin();
10098 MediumLockList::Base::const_iterator mediumListEnd =
10099 task.mpMediumLockList->GetEnd();
10100 MediumLockList::Base::const_iterator mediumListLast =
10101 mediumListEnd;
10102 --mediumListLast;
10103 for (MediumLockList::Base::const_iterator it = mediumListBegin;
10104 it != mediumListEnd;
10105 ++it)
10106 {
10107 const MediumLock &mediumLock = *it;
10108 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
10109 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
10110
10111 /* sanity check */
10112 if (it == mediumListLast)
10113 Assert(pMedium->m->state == MediumState_LockedWrite);
10114 else
10115 Assert(pMedium->m->state == MediumState_LockedRead ||
10116 // Allow resize the target image during mergeTo in case
10117 // of direction from parent to child because all intermediate
10118 // images are marked to MediumState_Deleting and will be
10119 // destroyed after successful merge
10120 pMedium->m->state == MediumState_Deleting);
10121
10122 /* Open all media but last in read-only mode. Do not handle
10123 * shareable media, as compaction and sharing are mutually
10124 * exclusive. */
10125 vrc = VDOpen(hdd,
10126 pMedium->m->strFormat.c_str(),
10127 pMedium->m->strLocationFull.c_str(),
10128 m->uOpenFlagsDef | (it == mediumListLast ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY),
10129 pMedium->m->vdImageIfaces);
10130 if (RT_FAILURE(vrc))
10131 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10132 tr("Could not open the medium storage unit '%s'%s"),
10133 pMedium->m->strLocationFull.c_str(),
10134 i_vdError(vrc).c_str());
10135 }
10136
10137 Assert(m->state == MediumState_LockedWrite);
10138
10139 Utf8Str location(m->strLocationFull);
10140
10141 /* unlock before the potentially lengthy operation */
10142 thisLock.release();
10143
10144 VDGEOMETRY geo = {0, 0, 0}; /* auto */
10145 vrc = VDResize(hdd, task.mSize, &geo, &geo, task.mVDOperationIfaces);
10146 if (RT_FAILURE(vrc))
10147 {
10148 if (vrc == VERR_VD_SHRINK_NOT_SUPPORTED)
10149 throw setErrorBoth(VBOX_E_NOT_SUPPORTED, vrc,
10150 tr("Shrinking is not yet supported for medium '%s'"),
10151 location.c_str());
10152 if (vrc == VERR_NOT_SUPPORTED)
10153 throw setErrorBoth(VBOX_E_NOT_SUPPORTED, vrc,
10154 tr("Resizing to new size %llu is not yet supported for medium '%s'"),
10155 task.mSize, location.c_str());
10156 else if (vrc == VERR_NOT_IMPLEMENTED)
10157 throw setErrorBoth(E_NOTIMPL, vrc,
10158 tr("Resiting is not implemented, medium '%s'"),
10159 location.c_str());
10160 else
10161 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10162 tr("Could not resize medium '%s'%s"),
10163 location.c_str(),
10164 i_vdError(vrc).c_str());
10165 }
10166 size = VDGetFileSize(hdd, VD_LAST_IMAGE);
10167 logicalSize = VDGetSize(hdd, VD_LAST_IMAGE);
10168 }
10169 catch (HRESULT aRC) { rc = aRC; }
10170
10171 VDDestroy(hdd);
10172 }
10173 catch (HRESULT aRC) { rc = aRC; }
10174
10175 if (SUCCEEDED(rc))
10176 {
10177 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
10178 m->size = size;
10179 m->logicalSize = logicalSize;
10180
10181 if (task.NotifyAboutChanges())
10182 m->pVirtualBox->i_onMediumConfigChanged(this);
10183 }
10184
10185 /* Everything is explicitly unlocked when the task exits,
10186 * as the task destruction also destroys the media chain. */
10187
10188 return rc;
10189}
10190
10191/**
10192 * Implementation code for the "import" task.
10193 *
10194 * This only gets started from Medium::importFile() and always runs
10195 * asynchronously. It potentially touches the media registry, so we
10196 * always save the VirtualBox.xml file when we're done here.
10197 *
10198 * @param task
10199 * @return
10200 */
10201HRESULT Medium::i_taskImportHandler(Medium::ImportTask &task)
10202{
10203 /** @todo r=klaus The code below needs to be double checked with regard
10204 * to lock order violations, it probably causes lock order issues related
10205 * to the AutoCaller usage. */
10206 HRESULT rcTmp = S_OK;
10207
10208 const ComObjPtr<Medium> &pParent = task.mParent;
10209
10210 bool fCreatingTarget = false;
10211
10212 uint64_t size = 0, logicalSize = 0;
10213 MediumVariant_T variant = MediumVariant_Standard;
10214 bool fGenerateUuid = false;
10215
10216 try
10217 {
10218 if (!pParent.isNull())
10219 if (pParent->i_getDepth() >= SETTINGS_MEDIUM_DEPTH_MAX)
10220 {
10221 AutoReadLock plock(pParent COMMA_LOCKVAL_SRC_POS);
10222 throw setError(VBOX_E_INVALID_OBJECT_STATE,
10223 tr("Cannot import image for medium '%s', because it exceeds the medium tree depth limit. Please merge some images which you no longer need"),
10224 pParent->m->strLocationFull.c_str());
10225 }
10226
10227 /* Lock all in {parent,child} order. The lock is also used as a
10228 * signal from the task initiator (which releases it only after
10229 * RTThreadCreate()) that we can start the job. */
10230 AutoMultiWriteLock2 thisLock(this, pParent COMMA_LOCKVAL_SRC_POS);
10231
10232 fCreatingTarget = m->state == MediumState_Creating;
10233
10234 /* The object may request a specific UUID (through a special form of
10235 * the moveTo() argument). Otherwise we have to generate it */
10236 Guid targetId = m->id;
10237
10238 fGenerateUuid = targetId.isZero();
10239 if (fGenerateUuid)
10240 {
10241 targetId.create();
10242 /* VirtualBox::i_registerMedium() will need UUID */
10243 unconst(m->id) = targetId;
10244 }
10245
10246
10247 PVDISK hdd;
10248 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
10249 ComAssertRCThrow(vrc, E_FAIL);
10250
10251 try
10252 {
10253 /* Open source medium. */
10254 vrc = VDOpen(hdd,
10255 task.mFormat->i_getId().c_str(),
10256 task.mFilename.c_str(),
10257 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SEQUENTIAL | m->uOpenFlagsDef,
10258 task.mVDImageIfaces);
10259 if (RT_FAILURE(vrc))
10260 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10261 tr("Could not open the medium storage unit '%s'%s"),
10262 task.mFilename.c_str(),
10263 i_vdError(vrc).c_str());
10264
10265 Utf8Str targetFormat(m->strFormat);
10266 Utf8Str targetLocation(m->strLocationFull);
10267 uint64_t capabilities = task.mFormat->i_getCapabilities();
10268
10269 Assert( m->state == MediumState_Creating
10270 || m->state == MediumState_LockedWrite);
10271 Assert( pParent.isNull()
10272 || pParent->m->state == MediumState_LockedRead);
10273
10274 /* unlock before the potentially lengthy operation */
10275 thisLock.release();
10276
10277 /* ensure the target directory exists */
10278 if (capabilities & MediumFormatCapabilities_File)
10279 {
10280 HRESULT rc = VirtualBox::i_ensureFilePathExists(targetLocation,
10281 !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
10282 if (FAILED(rc))
10283 throw rc;
10284 }
10285
10286 PVDISK targetHdd;
10287 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &targetHdd);
10288 ComAssertRCThrow(vrc, E_FAIL);
10289
10290 try
10291 {
10292 /* Open all media in the target chain. */
10293 MediumLockList::Base::const_iterator targetListBegin =
10294 task.mpTargetMediumLockList->GetBegin();
10295 MediumLockList::Base::const_iterator targetListEnd =
10296 task.mpTargetMediumLockList->GetEnd();
10297 for (MediumLockList::Base::const_iterator it = targetListBegin;
10298 it != targetListEnd;
10299 ++it)
10300 {
10301 const MediumLock &mediumLock = *it;
10302 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
10303
10304 /* If the target medium is not created yet there's no
10305 * reason to open it. */
10306 if (pMedium == this && fCreatingTarget)
10307 continue;
10308
10309 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
10310
10311 /* sanity check */
10312 Assert( pMedium->m->state == MediumState_LockedRead
10313 || pMedium->m->state == MediumState_LockedWrite);
10314
10315 unsigned uOpenFlags = VD_OPEN_FLAGS_NORMAL;
10316 if (pMedium->m->state != MediumState_LockedWrite)
10317 uOpenFlags = VD_OPEN_FLAGS_READONLY;
10318 if (pMedium->m->type == MediumType_Shareable)
10319 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
10320
10321 /* Open all media in appropriate mode. */
10322 vrc = VDOpen(targetHdd,
10323 pMedium->m->strFormat.c_str(),
10324 pMedium->m->strLocationFull.c_str(),
10325 uOpenFlags | m->uOpenFlagsDef,
10326 pMedium->m->vdImageIfaces);
10327 if (RT_FAILURE(vrc))
10328 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10329 tr("Could not open the medium storage unit '%s'%s"),
10330 pMedium->m->strLocationFull.c_str(),
10331 i_vdError(vrc).c_str());
10332 }
10333
10334 vrc = VDCopy(hdd,
10335 VD_LAST_IMAGE,
10336 targetHdd,
10337 targetFormat.c_str(),
10338 (fCreatingTarget) ? targetLocation.c_str() : (char *)NULL,
10339 false /* fMoveByRename */,
10340 0 /* cbSize */,
10341 task.mVariant & ~(MediumVariant_NoCreateDir | MediumVariant_Formatted),
10342 targetId.raw(),
10343 VD_OPEN_FLAGS_NORMAL,
10344 NULL /* pVDIfsOperation */,
10345 m->vdImageIfaces,
10346 task.mVDOperationIfaces);
10347 if (RT_FAILURE(vrc))
10348 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10349 tr("Could not create the imported medium '%s'%s"),
10350 targetLocation.c_str(), i_vdError(vrc).c_str());
10351
10352 size = VDGetFileSize(targetHdd, VD_LAST_IMAGE);
10353 logicalSize = VDGetSize(targetHdd, VD_LAST_IMAGE);
10354 unsigned uImageFlags;
10355 vrc = VDGetImageFlags(targetHdd, 0, &uImageFlags);
10356 if (RT_SUCCESS(vrc))
10357 variant = (MediumVariant_T)uImageFlags;
10358 }
10359 catch (HRESULT aRC) { rcTmp = aRC; }
10360
10361 VDDestroy(targetHdd);
10362 }
10363 catch (HRESULT aRC) { rcTmp = aRC; }
10364
10365 VDDestroy(hdd);
10366 }
10367 catch (HRESULT aRC) { rcTmp = aRC; }
10368
10369 ErrorInfoKeeper eik;
10370 MultiResult mrc(rcTmp);
10371
10372 /* Only do the parent changes for newly created media. */
10373 if (SUCCEEDED(mrc) && fCreatingTarget)
10374 {
10375 /* we set m->pParent & children() */
10376 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10377
10378 Assert(m->pParent.isNull());
10379
10380 if (pParent)
10381 {
10382 /* Associate the imported medium with the parent and deassociate
10383 * from VirtualBox. Depth check above. */
10384 i_setParent(pParent);
10385
10386 /* register with mVirtualBox as the last step and move to
10387 * Created state only on success (leaving an orphan file is
10388 * better than breaking media registry consistency) */
10389 eik.restore();
10390 ComObjPtr<Medium> pMedium;
10391 mrc = pParent->m->pVirtualBox->i_registerMedium(this, &pMedium,
10392 treeLock);
10393 Assert(this == pMedium);
10394 eik.fetch();
10395
10396 if (FAILED(mrc))
10397 /* break parent association on failure to register */
10398 this->i_deparent(); // removes target from parent
10399 }
10400 else
10401 {
10402 /* just register */
10403 eik.restore();
10404 ComObjPtr<Medium> pMedium;
10405 mrc = m->pVirtualBox->i_registerMedium(this, &pMedium, treeLock);
10406 Assert(this == pMedium);
10407 eik.fetch();
10408 }
10409 }
10410
10411 if (fCreatingTarget)
10412 {
10413 AutoWriteLock mLock(this COMMA_LOCKVAL_SRC_POS);
10414
10415 if (SUCCEEDED(mrc))
10416 {
10417 m->state = MediumState_Created;
10418
10419 m->size = size;
10420 m->logicalSize = logicalSize;
10421 m->variant = variant;
10422 }
10423 else
10424 {
10425 /* back to NotCreated on failure */
10426 m->state = MediumState_NotCreated;
10427
10428 /* reset UUID to prevent it from being reused next time */
10429 if (fGenerateUuid)
10430 unconst(m->id).clear();
10431 }
10432 }
10433
10434 // now, at the end of this task (always asynchronous), save the settings
10435 {
10436 // save the settings
10437 i_markRegistriesModified();
10438 /* collect multiple errors */
10439 eik.restore();
10440 m->pVirtualBox->i_saveModifiedRegistries();
10441 eik.fetch();
10442 }
10443
10444 /* Everything is explicitly unlocked when the task exits,
10445 * as the task destruction also destroys the target chain. */
10446
10447 /* Make sure the target chain is released early, otherwise it can
10448 * lead to deadlocks with concurrent IAppliance activities. */
10449 task.mpTargetMediumLockList->Clear();
10450
10451 if (task.NotifyAboutChanges() && SUCCEEDED(mrc))
10452 {
10453 if (pParent)
10454 m->pVirtualBox->i_onMediumConfigChanged(pParent);
10455 if (fCreatingTarget)
10456 m->pVirtualBox->i_onMediumConfigChanged(this);
10457 else
10458 m->pVirtualBox->i_onMediumRegistered(m->id, m->devType, TRUE);
10459 }
10460
10461 return mrc;
10462}
10463
10464/**
10465 * Sets up the encryption settings for a filter.
10466 */
10467void Medium::i_taskEncryptSettingsSetup(MediumCryptoFilterSettings *pSettings, const char *pszCipher,
10468 const char *pszKeyStore, const char *pszPassword,
10469 bool fCreateKeyStore)
10470{
10471 pSettings->pszCipher = pszCipher;
10472 pSettings->pszPassword = pszPassword;
10473 pSettings->pszKeyStoreLoad = pszKeyStore;
10474 pSettings->fCreateKeyStore = fCreateKeyStore;
10475 pSettings->pbDek = NULL;
10476 pSettings->cbDek = 0;
10477 pSettings->vdFilterIfaces = NULL;
10478
10479 pSettings->vdIfCfg.pfnAreKeysValid = i_vdCryptoConfigAreKeysValid;
10480 pSettings->vdIfCfg.pfnQuerySize = i_vdCryptoConfigQuerySize;
10481 pSettings->vdIfCfg.pfnQuery = i_vdCryptoConfigQuery;
10482 pSettings->vdIfCfg.pfnQueryBytes = NULL;
10483
10484 pSettings->vdIfCrypto.pfnKeyRetain = i_vdCryptoKeyRetain;
10485 pSettings->vdIfCrypto.pfnKeyRelease = i_vdCryptoKeyRelease;
10486 pSettings->vdIfCrypto.pfnKeyStorePasswordRetain = i_vdCryptoKeyStorePasswordRetain;
10487 pSettings->vdIfCrypto.pfnKeyStorePasswordRelease = i_vdCryptoKeyStorePasswordRelease;
10488 pSettings->vdIfCrypto.pfnKeyStoreSave = i_vdCryptoKeyStoreSave;
10489 pSettings->vdIfCrypto.pfnKeyStoreReturnParameters = i_vdCryptoKeyStoreReturnParameters;
10490
10491 int vrc = VDInterfaceAdd(&pSettings->vdIfCfg.Core,
10492 "Medium::vdInterfaceCfgCrypto",
10493 VDINTERFACETYPE_CONFIG, pSettings,
10494 sizeof(VDINTERFACECONFIG), &pSettings->vdFilterIfaces);
10495 AssertRC(vrc);
10496
10497 vrc = VDInterfaceAdd(&pSettings->vdIfCrypto.Core,
10498 "Medium::vdInterfaceCrypto",
10499 VDINTERFACETYPE_CRYPTO, pSettings,
10500 sizeof(VDINTERFACECRYPTO), &pSettings->vdFilterIfaces);
10501 AssertRC(vrc);
10502}
10503
10504/**
10505 * Implementation code for the "encrypt" task.
10506 *
10507 * @param task
10508 * @return
10509 */
10510HRESULT Medium::i_taskEncryptHandler(Medium::EncryptTask &task)
10511{
10512# ifndef VBOX_WITH_EXTPACK
10513 RT_NOREF(task);
10514# endif
10515 HRESULT rc = S_OK;
10516
10517 /* Lock all in {parent,child} order. The lock is also used as a
10518 * signal from the task initiator (which releases it only after
10519 * RTThreadCreate()) that we can start the job. */
10520 ComObjPtr<Medium> pBase = i_getBase();
10521 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
10522
10523 try
10524 {
10525# ifdef VBOX_WITH_EXTPACK
10526 ExtPackManager *pExtPackManager = m->pVirtualBox->i_getExtPackManager();
10527 if (pExtPackManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
10528 {
10529 /* Load the plugin */
10530 Utf8Str strPlugin;
10531 rc = pExtPackManager->i_getLibraryPathForExtPack(g_szVDPlugin, ORACLE_PUEL_EXTPACK_NAME, &strPlugin);
10532 if (SUCCEEDED(rc))
10533 {
10534 int vrc = VDPluginLoadFromFilename(strPlugin.c_str());
10535 if (RT_FAILURE(vrc))
10536 throw setErrorBoth(VBOX_E_NOT_SUPPORTED, vrc,
10537 tr("Encrypting the image failed because the encryption plugin could not be loaded (%s)"),
10538 i_vdError(vrc).c_str());
10539 }
10540 else
10541 throw setError(VBOX_E_NOT_SUPPORTED,
10542 tr("Encryption is not supported because the extension pack '%s' is missing the encryption plugin (old extension pack installed?)"),
10543 ORACLE_PUEL_EXTPACK_NAME);
10544 }
10545 else
10546 throw setError(VBOX_E_NOT_SUPPORTED,
10547 tr("Encryption is not supported because the extension pack '%s' is missing"),
10548 ORACLE_PUEL_EXTPACK_NAME);
10549
10550 PVDISK pDisk = NULL;
10551 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &pDisk);
10552 ComAssertRCThrow(vrc, E_FAIL);
10553
10554 MediumCryptoFilterSettings CryptoSettingsRead;
10555 MediumCryptoFilterSettings CryptoSettingsWrite;
10556
10557 void *pvBuf = NULL;
10558 const char *pszPasswordNew = NULL;
10559 try
10560 {
10561 /* Set up disk encryption filters. */
10562 if (task.mstrCurrentPassword.isEmpty())
10563 {
10564 /*
10565 * Query whether the medium property indicating that encryption is
10566 * configured is existing.
10567 */
10568 settings::StringsMap::iterator it = pBase->m->mapProperties.find("CRYPT/KeyStore");
10569 if (it != pBase->m->mapProperties.end())
10570 throw setError(VBOX_E_PASSWORD_INCORRECT,
10571 tr("The password given for the encrypted image is incorrect"));
10572 }
10573 else
10574 {
10575 settings::StringsMap::iterator it = pBase->m->mapProperties.find("CRYPT/KeyStore");
10576 if (it == pBase->m->mapProperties.end())
10577 throw setError(VBOX_E_INVALID_OBJECT_STATE,
10578 tr("The image is not configured for encryption"));
10579
10580 i_taskEncryptSettingsSetup(&CryptoSettingsRead, NULL, it->second.c_str(), task.mstrCurrentPassword.c_str(),
10581 false /* fCreateKeyStore */);
10582 vrc = VDFilterAdd(pDisk, "CRYPT", VD_FILTER_FLAGS_READ, CryptoSettingsRead.vdFilterIfaces);
10583 if (vrc == VERR_VD_PASSWORD_INCORRECT)
10584 throw setError(VBOX_E_PASSWORD_INCORRECT,
10585 tr("The password to decrypt the image is incorrect"));
10586 else if (RT_FAILURE(vrc))
10587 throw setError(VBOX_E_INVALID_OBJECT_STATE,
10588 tr("Failed to load the decryption filter: %s"),
10589 i_vdError(vrc).c_str());
10590 }
10591
10592 if (task.mstrCipher.isNotEmpty())
10593 {
10594 if ( task.mstrNewPassword.isEmpty()
10595 && task.mstrNewPasswordId.isEmpty()
10596 && task.mstrCurrentPassword.isNotEmpty())
10597 {
10598 /* An empty password and password ID will default to the current password. */
10599 pszPasswordNew = task.mstrCurrentPassword.c_str();
10600 }
10601 else if (task.mstrNewPassword.isEmpty())
10602 throw setError(VBOX_E_OBJECT_NOT_FOUND,
10603 tr("A password must be given for the image encryption"));
10604 else if (task.mstrNewPasswordId.isEmpty())
10605 throw setError(VBOX_E_INVALID_OBJECT_STATE,
10606 tr("A valid identifier for the password must be given"));
10607 else
10608 pszPasswordNew = task.mstrNewPassword.c_str();
10609
10610 i_taskEncryptSettingsSetup(&CryptoSettingsWrite, task.mstrCipher.c_str(), NULL,
10611 pszPasswordNew, true /* fCreateKeyStore */);
10612 vrc = VDFilterAdd(pDisk, "CRYPT", VD_FILTER_FLAGS_WRITE, CryptoSettingsWrite.vdFilterIfaces);
10613 if (RT_FAILURE(vrc))
10614 throw setErrorBoth(VBOX_E_INVALID_OBJECT_STATE, vrc,
10615 tr("Failed to load the encryption filter: %s"),
10616 i_vdError(vrc).c_str());
10617 }
10618 else if (task.mstrNewPasswordId.isNotEmpty() || task.mstrNewPassword.isNotEmpty())
10619 throw setError(VBOX_E_INVALID_OBJECT_STATE,
10620 tr("The password and password identifier must be empty if the output should be unencrypted"));
10621
10622 /* Open all media in the chain. */
10623 MediumLockList::Base::const_iterator mediumListBegin =
10624 task.mpMediumLockList->GetBegin();
10625 MediumLockList::Base::const_iterator mediumListEnd =
10626 task.mpMediumLockList->GetEnd();
10627 MediumLockList::Base::const_iterator mediumListLast =
10628 mediumListEnd;
10629 --mediumListLast;
10630 for (MediumLockList::Base::const_iterator it = mediumListBegin;
10631 it != mediumListEnd;
10632 ++it)
10633 {
10634 const MediumLock &mediumLock = *it;
10635 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
10636 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
10637
10638 Assert(pMedium->m->state == MediumState_LockedWrite);
10639
10640 /* Open all media but last in read-only mode. Do not handle
10641 * shareable media, as compaction and sharing are mutually
10642 * exclusive. */
10643 vrc = VDOpen(pDisk,
10644 pMedium->m->strFormat.c_str(),
10645 pMedium->m->strLocationFull.c_str(),
10646 m->uOpenFlagsDef | (it == mediumListLast ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY),
10647 pMedium->m->vdImageIfaces);
10648 if (RT_FAILURE(vrc))
10649 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10650 tr("Could not open the medium storage unit '%s'%s"),
10651 pMedium->m->strLocationFull.c_str(),
10652 i_vdError(vrc).c_str());
10653 }
10654
10655 Assert(m->state == MediumState_LockedWrite);
10656
10657 Utf8Str location(m->strLocationFull);
10658
10659 /* unlock before the potentially lengthy operation */
10660 thisLock.release();
10661
10662 vrc = VDPrepareWithFilters(pDisk, task.mVDOperationIfaces);
10663 if (RT_FAILURE(vrc))
10664 throw setErrorBoth(VBOX_E_FILE_ERROR, vrc,
10665 tr("Could not prepare disk images for encryption (%Rrc): %s"),
10666 vrc, i_vdError(vrc).c_str());
10667
10668 thisLock.acquire();
10669 /* If everything went well set the new key store. */
10670 settings::StringsMap::iterator it = pBase->m->mapProperties.find("CRYPT/KeyStore");
10671 if (it != pBase->m->mapProperties.end())
10672 pBase->m->mapProperties.erase(it);
10673
10674 /* Delete KeyId if encryption is removed or the password did change. */
10675 if ( task.mstrNewPasswordId.isNotEmpty()
10676 || task.mstrCipher.isEmpty())
10677 {
10678 it = pBase->m->mapProperties.find("CRYPT/KeyId");
10679 if (it != pBase->m->mapProperties.end())
10680 pBase->m->mapProperties.erase(it);
10681 }
10682
10683 if (CryptoSettingsWrite.pszKeyStore)
10684 {
10685 pBase->m->mapProperties["CRYPT/KeyStore"] = Utf8Str(CryptoSettingsWrite.pszKeyStore);
10686 if (task.mstrNewPasswordId.isNotEmpty())
10687 pBase->m->mapProperties["CRYPT/KeyId"] = task.mstrNewPasswordId;
10688 }
10689
10690 if (CryptoSettingsRead.pszCipherReturned)
10691 RTStrFree(CryptoSettingsRead.pszCipherReturned);
10692
10693 if (CryptoSettingsWrite.pszCipherReturned)
10694 RTStrFree(CryptoSettingsWrite.pszCipherReturned);
10695
10696 thisLock.release();
10697 pBase->i_markRegistriesModified();
10698 m->pVirtualBox->i_saveModifiedRegistries();
10699 }
10700 catch (HRESULT aRC) { rc = aRC; }
10701
10702 if (pvBuf)
10703 RTMemFree(pvBuf);
10704
10705 VDDestroy(pDisk);
10706# else
10707 throw setError(VBOX_E_NOT_SUPPORTED,
10708 tr("Encryption is not supported because extension pack support is not built in"));
10709# endif
10710 }
10711 catch (HRESULT aRC) { rc = aRC; }
10712
10713 /* Everything is explicitly unlocked when the task exits,
10714 * as the task destruction also destroys the media chain. */
10715
10716 return rc;
10717}
10718
10719/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

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