VirtualBox

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

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

Main: Don't use Logging.h.

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

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