VirtualBox

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

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

Added AllocationBlockSize property to VDI backend, and ability to set properties with VBoxManage --property key=value

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

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