VirtualBox

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

Last change on this file since 52858 was 52831, checked in by vboxsync, 11 years ago

MediumImpl: Medium::getVariant cleanup

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 282.4 KB
Line 
1/* $Id: MediumImpl.cpp 52831 2014-09-24 08:10:43Z vboxsync $ */
2/** @file
3 * VirtualBox COM class implementation
4 */
5
6/*
7 * Copyright (C) 2008-2014 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#include "MediumImpl.h"
19#include "TokenImpl.h"
20#include "ProgressImpl.h"
21#include "SystemPropertiesImpl.h"
22#include "VirtualBoxImpl.h"
23
24#include "AutoCaller.h"
25#include "Logging.h"
26
27#include <VBox/com/array.h>
28#include "VBox/com/MultiResult.h"
29#include "VBox/com/ErrorInfo.h"
30
31#include <VBox/err.h>
32#include <VBox/settings.h>
33
34#include <iprt/param.h>
35#include <iprt/path.h>
36#include <iprt/file.h>
37#include <iprt/tcp.h>
38#include <iprt/cpp/utils.h>
39
40#include <VBox/vd.h>
41
42#include <algorithm>
43#include <list>
44
45typedef std::list<Guid> GuidList;
46
47////////////////////////////////////////////////////////////////////////////////
48//
49// Medium data definition
50//
51////////////////////////////////////////////////////////////////////////////////
52
53/** Describes how a machine refers to this medium. */
54struct BackRef
55{
56 /** Equality predicate for stdc++. */
57 struct EqualsTo : public std::unary_function <BackRef, bool>
58 {
59 explicit EqualsTo(const Guid &aMachineId) : machineId(aMachineId) {}
60
61 bool operator()(const argument_type &aThat) const
62 {
63 return aThat.machineId == machineId;
64 }
65
66 const Guid machineId;
67 };
68
69 BackRef(const Guid &aMachineId,
70 const Guid &aSnapshotId = Guid::Empty)
71 : machineId(aMachineId),
72 fInCurState(aSnapshotId.isZero())
73 {
74 if (aSnapshotId.isValid() && !aSnapshotId.isZero())
75 llSnapshotIds.push_back(aSnapshotId);
76 }
77
78 Guid machineId;
79 bool fInCurState : 1;
80 GuidList llSnapshotIds;
81};
82
83typedef std::list<BackRef> BackRefList;
84
85struct Medium::Data
86{
87 Data()
88 : pVirtualBox(NULL),
89 state(MediumState_NotCreated),
90 variant(MediumVariant_Standard),
91 size(0),
92 readers(0),
93 preLockState(MediumState_NotCreated),
94 queryInfoSem(LOCKCLASS_MEDIUMQUERY),
95 queryInfoRunning(false),
96 type(MediumType_Normal),
97 devType(DeviceType_HardDisk),
98 logicalSize(0),
99 hddOpenMode(OpenReadWrite),
100 autoReset(false),
101 hostDrive(false),
102 implicit(false),
103 uOpenFlagsDef(VD_OPEN_FLAGS_IGNORE_FLUSH),
104 numCreateDiffTasks(0),
105 vdDiskIfaces(NULL),
106 vdImageIfaces(NULL)
107 { }
108
109 /** weak VirtualBox parent */
110 VirtualBox * const pVirtualBox;
111
112 // pParent and llChildren are protected by VirtualBox::i_getMediaTreeLockHandle()
113 ComObjPtr<Medium> pParent;
114 MediaList llChildren; // to add a child, just call push_back; to remove
115 // a child, call child->deparent() which does a lookup
116
117 GuidList llRegistryIDs; // media registries in which this medium is listed
118
119 const Guid id;
120 Utf8Str strDescription;
121 MediumState_T state;
122 MediumVariant_T variant;
123 Utf8Str strLocationFull;
124 uint64_t size;
125 Utf8Str strLastAccessError;
126
127 BackRefList backRefs;
128
129 size_t readers;
130 MediumState_T preLockState;
131
132 /** Special synchronization for operations which must wait for
133 * Medium::i_queryInfo in another thread to complete. Using a SemRW is
134 * not quite ideal, but at least it is subject to the lock validator,
135 * unlike the SemEventMulti which we had here for many years. Catching
136 * possible deadlocks is more important than a tiny bit of efficiency. */
137 RWLockHandle queryInfoSem;
138 bool queryInfoRunning : 1;
139
140 const Utf8Str strFormat;
141 ComObjPtr<MediumFormat> formatObj;
142
143 MediumType_T type;
144 DeviceType_T devType;
145 uint64_t logicalSize;
146
147 HDDOpenMode hddOpenMode;
148
149 bool autoReset : 1;
150
151 /** New UUID to be set on the next Medium::i_queryInfo call. */
152 const Guid uuidImage;
153 /** New parent UUID to be set on the next Medium::i_queryInfo call. */
154 const Guid uuidParentImage;
155
156 bool hostDrive : 1;
157
158 settings::StringsMap mapProperties;
159
160 bool implicit : 1;
161
162 /** Default flags passed to VDOpen(). */
163 unsigned uOpenFlagsDef;
164
165 uint32_t numCreateDiffTasks;
166
167 Utf8Str vdError; /*< Error remembered by the VD error callback. */
168
169 VDINTERFACEERROR vdIfError;
170
171 VDINTERFACECONFIG vdIfConfig;
172
173 VDINTERFACETCPNET vdIfTcpNet;
174
175 PVDINTERFACE vdDiskIfaces;
176 PVDINTERFACE vdImageIfaces;
177};
178
179typedef struct VDSOCKETINT
180{
181 /** Socket handle. */
182 RTSOCKET hSocket;
183} VDSOCKETINT, *PVDSOCKETINT;
184
185////////////////////////////////////////////////////////////////////////////////
186//
187// Globals
188//
189////////////////////////////////////////////////////////////////////////////////
190
191/**
192 * Medium::Task class for asynchronous operations.
193 *
194 * @note Instances of this class must be created using new() because the
195 * task thread function will delete them when the task is complete.
196 *
197 * @note The constructor of this class adds a caller on the managed Medium
198 * object which is automatically released upon destruction.
199 */
200class Medium::Task
201{
202public:
203 Task(Medium *aMedium, Progress *aProgress)
204 : mVDOperationIfaces(NULL),
205 mMedium(aMedium),
206 mMediumCaller(aMedium),
207 mThread(NIL_RTTHREAD),
208 mProgress(aProgress),
209 mVirtualBoxCaller(NULL)
210 {
211 AssertReturnVoidStmt(aMedium, mRC = E_FAIL);
212 mRC = mMediumCaller.rc();
213 if (FAILED(mRC))
214 return;
215
216 /* Get strong VirtualBox reference, see below. */
217 VirtualBox *pVirtualBox = aMedium->m->pVirtualBox;
218 mVirtualBox = pVirtualBox;
219 mVirtualBoxCaller.attach(pVirtualBox);
220 mRC = mVirtualBoxCaller.rc();
221 if (FAILED(mRC))
222 return;
223
224 /* Set up a per-operation progress interface, can be used freely (for
225 * binary operations you can use it either on the source or target). */
226 mVDIfProgress.pfnProgress = vdProgressCall;
227 int vrc = VDInterfaceAdd(&mVDIfProgress.Core,
228 "Medium::Task::vdInterfaceProgress",
229 VDINTERFACETYPE_PROGRESS,
230 mProgress,
231 sizeof(VDINTERFACEPROGRESS),
232 &mVDOperationIfaces);
233 AssertRC(vrc);
234 if (RT_FAILURE(vrc))
235 mRC = E_FAIL;
236 }
237
238 // Make all destructors virtual. Just in case.
239 virtual ~Task()
240 {}
241
242 HRESULT rc() const { return mRC; }
243 bool isOk() const { return SUCCEEDED(rc()); }
244
245 static int fntMediumTask(RTTHREAD aThread, void *pvUser);
246
247 bool isAsync() { return mThread != NIL_RTTHREAD; }
248
249 PVDINTERFACE mVDOperationIfaces;
250
251 const ComObjPtr<Medium> mMedium;
252 AutoCaller mMediumCaller;
253
254 friend HRESULT Medium::i_runNow(Medium::Task*);
255
256protected:
257 HRESULT mRC;
258 RTTHREAD mThread;
259
260private:
261 virtual HRESULT handler() = 0;
262
263 const ComObjPtr<Progress> mProgress;
264
265 static DECLCALLBACK(int) vdProgressCall(void *pvUser, unsigned uPercent);
266
267 VDINTERFACEPROGRESS mVDIfProgress;
268
269 /* Must have a strong VirtualBox reference during a task otherwise the
270 * reference count might drop to 0 while a task is still running. This
271 * would result in weird behavior, including deadlocks due to uninit and
272 * locking order issues. The deadlock often is not detectable because the
273 * uninit uses event semaphores which sabotages deadlock detection. */
274 ComObjPtr<VirtualBox> mVirtualBox;
275 AutoCaller mVirtualBoxCaller;
276};
277
278class Medium::CreateBaseTask : public Medium::Task
279{
280public:
281 CreateBaseTask(Medium *aMedium,
282 Progress *aProgress,
283 uint64_t aSize,
284 MediumVariant_T aVariant)
285 : Medium::Task(aMedium, aProgress),
286 mSize(aSize),
287 mVariant(aVariant)
288 {}
289
290 uint64_t mSize;
291 MediumVariant_T mVariant;
292
293private:
294 virtual HRESULT handler();
295};
296
297class Medium::CreateDiffTask : public Medium::Task
298{
299public:
300 CreateDiffTask(Medium *aMedium,
301 Progress *aProgress,
302 Medium *aTarget,
303 MediumVariant_T aVariant,
304 MediumLockList *aMediumLockList,
305 bool fKeepMediumLockList = false)
306 : Medium::Task(aMedium, aProgress),
307 mpMediumLockList(aMediumLockList),
308 mTarget(aTarget),
309 mVariant(aVariant),
310 mTargetCaller(aTarget),
311 mfKeepMediumLockList(fKeepMediumLockList)
312 {
313 AssertReturnVoidStmt(aTarget != NULL, mRC = E_FAIL);
314 mRC = mTargetCaller.rc();
315 if (FAILED(mRC))
316 return;
317 }
318
319 ~CreateDiffTask()
320 {
321 if (!mfKeepMediumLockList && mpMediumLockList)
322 delete mpMediumLockList;
323 }
324
325 MediumLockList *mpMediumLockList;
326
327 const ComObjPtr<Medium> mTarget;
328 MediumVariant_T mVariant;
329
330private:
331 virtual HRESULT handler();
332
333 AutoCaller mTargetCaller;
334 bool mfKeepMediumLockList;
335};
336
337class Medium::CloneTask : public Medium::Task
338{
339public:
340 CloneTask(Medium *aMedium,
341 Progress *aProgress,
342 Medium *aTarget,
343 MediumVariant_T aVariant,
344 Medium *aParent,
345 uint32_t idxSrcImageSame,
346 uint32_t idxDstImageSame,
347 MediumLockList *aSourceMediumLockList,
348 MediumLockList *aTargetMediumLockList,
349 bool fKeepSourceMediumLockList = false,
350 bool fKeepTargetMediumLockList = false)
351 : Medium::Task(aMedium, aProgress),
352 mTarget(aTarget),
353 mParent(aParent),
354 mpSourceMediumLockList(aSourceMediumLockList),
355 mpTargetMediumLockList(aTargetMediumLockList),
356 mVariant(aVariant),
357 midxSrcImageSame(idxSrcImageSame),
358 midxDstImageSame(idxDstImageSame),
359 mTargetCaller(aTarget),
360 mParentCaller(aParent),
361 mfKeepSourceMediumLockList(fKeepSourceMediumLockList),
362 mfKeepTargetMediumLockList(fKeepTargetMediumLockList)
363 {
364 AssertReturnVoidStmt(aTarget != NULL, mRC = E_FAIL);
365 mRC = mTargetCaller.rc();
366 if (FAILED(mRC))
367 return;
368 /* aParent may be NULL */
369 mRC = mParentCaller.rc();
370 if (FAILED(mRC))
371 return;
372 AssertReturnVoidStmt(aSourceMediumLockList != NULL, mRC = E_FAIL);
373 AssertReturnVoidStmt(aTargetMediumLockList != NULL, mRC = E_FAIL);
374 }
375
376 ~CloneTask()
377 {
378 if (!mfKeepSourceMediumLockList && mpSourceMediumLockList)
379 delete mpSourceMediumLockList;
380 if (!mfKeepTargetMediumLockList && mpTargetMediumLockList)
381 delete mpTargetMediumLockList;
382 }
383
384 const ComObjPtr<Medium> mTarget;
385 const ComObjPtr<Medium> mParent;
386 MediumLockList *mpSourceMediumLockList;
387 MediumLockList *mpTargetMediumLockList;
388 MediumVariant_T mVariant;
389 uint32_t midxSrcImageSame;
390 uint32_t midxDstImageSame;
391
392private:
393 virtual HRESULT handler();
394
395 AutoCaller mTargetCaller;
396 AutoCaller mParentCaller;
397 bool mfKeepSourceMediumLockList;
398 bool mfKeepTargetMediumLockList;
399};
400
401class Medium::CompactTask : public Medium::Task
402{
403public:
404 CompactTask(Medium *aMedium,
405 Progress *aProgress,
406 MediumLockList *aMediumLockList,
407 bool fKeepMediumLockList = false)
408 : Medium::Task(aMedium, aProgress),
409 mpMediumLockList(aMediumLockList),
410 mfKeepMediumLockList(fKeepMediumLockList)
411 {
412 AssertReturnVoidStmt(aMediumLockList != NULL, mRC = E_FAIL);
413 }
414
415 ~CompactTask()
416 {
417 if (!mfKeepMediumLockList && mpMediumLockList)
418 delete mpMediumLockList;
419 }
420
421 MediumLockList *mpMediumLockList;
422
423private:
424 virtual HRESULT handler();
425
426 bool mfKeepMediumLockList;
427};
428
429class Medium::ResizeTask : public Medium::Task
430{
431public:
432 ResizeTask(Medium *aMedium,
433 uint64_t aSize,
434 Progress *aProgress,
435 MediumLockList *aMediumLockList,
436 bool fKeepMediumLockList = false)
437 : Medium::Task(aMedium, aProgress),
438 mSize(aSize),
439 mpMediumLockList(aMediumLockList),
440 mfKeepMediumLockList(fKeepMediumLockList)
441 {
442 AssertReturnVoidStmt(aMediumLockList != NULL, mRC = E_FAIL);
443 }
444
445 ~ResizeTask()
446 {
447 if (!mfKeepMediumLockList && mpMediumLockList)
448 delete mpMediumLockList;
449 }
450
451 uint64_t mSize;
452 MediumLockList *mpMediumLockList;
453
454private:
455 virtual HRESULT handler();
456
457 bool mfKeepMediumLockList;
458};
459
460class Medium::ResetTask : public Medium::Task
461{
462public:
463 ResetTask(Medium *aMedium,
464 Progress *aProgress,
465 MediumLockList *aMediumLockList,
466 bool fKeepMediumLockList = false)
467 : Medium::Task(aMedium, aProgress),
468 mpMediumLockList(aMediumLockList),
469 mfKeepMediumLockList(fKeepMediumLockList)
470 {}
471
472 ~ResetTask()
473 {
474 if (!mfKeepMediumLockList && mpMediumLockList)
475 delete mpMediumLockList;
476 }
477
478 MediumLockList *mpMediumLockList;
479
480private:
481 virtual HRESULT handler();
482
483 bool mfKeepMediumLockList;
484};
485
486class Medium::DeleteTask : public Medium::Task
487{
488public:
489 DeleteTask(Medium *aMedium,
490 Progress *aProgress,
491 MediumLockList *aMediumLockList,
492 bool fKeepMediumLockList = false)
493 : Medium::Task(aMedium, aProgress),
494 mpMediumLockList(aMediumLockList),
495 mfKeepMediumLockList(fKeepMediumLockList)
496 {}
497
498 ~DeleteTask()
499 {
500 if (!mfKeepMediumLockList && mpMediumLockList)
501 delete mpMediumLockList;
502 }
503
504 MediumLockList *mpMediumLockList;
505
506private:
507 virtual HRESULT handler();
508
509 bool mfKeepMediumLockList;
510};
511
512class Medium::MergeTask : public Medium::Task
513{
514public:
515 MergeTask(Medium *aMedium,
516 Medium *aTarget,
517 bool fMergeForward,
518 Medium *aParentForTarget,
519 MediumLockList *aChildrenToReparent,
520 Progress *aProgress,
521 MediumLockList *aMediumLockList,
522 bool fKeepMediumLockList = false)
523 : Medium::Task(aMedium, aProgress),
524 mTarget(aTarget),
525 mfMergeForward(fMergeForward),
526 mParentForTarget(aParentForTarget),
527 mpChildrenToReparent(aChildrenToReparent),
528 mpMediumLockList(aMediumLockList),
529 mTargetCaller(aTarget),
530 mParentForTargetCaller(aParentForTarget),
531 mfKeepMediumLockList(fKeepMediumLockList)
532 {
533 AssertReturnVoidStmt(aMediumLockList != NULL, mRC = E_FAIL);
534 }
535
536 ~MergeTask()
537 {
538 if (!mfKeepMediumLockList && mpMediumLockList)
539 delete mpMediumLockList;
540 if (mpChildrenToReparent)
541 delete mpChildrenToReparent;
542 }
543
544 const ComObjPtr<Medium> mTarget;
545 bool mfMergeForward;
546 /* When mpChildrenToReparent is null then mParentForTarget is non-null and
547 * vice versa. In other words: they are used in different cases. */
548 const ComObjPtr<Medium> mParentForTarget;
549 MediumLockList *mpChildrenToReparent;
550 MediumLockList *mpMediumLockList;
551
552private:
553 virtual HRESULT handler();
554
555 AutoCaller mTargetCaller;
556 AutoCaller mParentForTargetCaller;
557 bool mfKeepMediumLockList;
558};
559
560class Medium::ExportTask : public Medium::Task
561{
562public:
563 ExportTask(Medium *aMedium,
564 Progress *aProgress,
565 const char *aFilename,
566 MediumFormat *aFormat,
567 MediumVariant_T aVariant,
568 VDINTERFACEIO *aVDImageIOIf,
569 void *aVDImageIOUser,
570 MediumLockList *aSourceMediumLockList,
571 bool fKeepSourceMediumLockList = false)
572 : Medium::Task(aMedium, aProgress),
573 mpSourceMediumLockList(aSourceMediumLockList),
574 mFilename(aFilename),
575 mFormat(aFormat),
576 mVariant(aVariant),
577 mfKeepSourceMediumLockList(fKeepSourceMediumLockList)
578 {
579 AssertReturnVoidStmt(aSourceMediumLockList != NULL, mRC = E_FAIL);
580
581 mVDImageIfaces = aMedium->m->vdImageIfaces;
582 if (aVDImageIOIf)
583 {
584 int vrc = VDInterfaceAdd(&aVDImageIOIf->Core, "Medium::vdInterfaceIO",
585 VDINTERFACETYPE_IO, aVDImageIOUser,
586 sizeof(VDINTERFACEIO), &mVDImageIfaces);
587 AssertRCReturnVoidStmt(vrc, mRC = E_FAIL);
588 }
589 }
590
591 ~ExportTask()
592 {
593 if (!mfKeepSourceMediumLockList && mpSourceMediumLockList)
594 delete mpSourceMediumLockList;
595 }
596
597 MediumLockList *mpSourceMediumLockList;
598 Utf8Str mFilename;
599 ComObjPtr<MediumFormat> mFormat;
600 MediumVariant_T mVariant;
601 PVDINTERFACE mVDImageIfaces;
602
603private:
604 virtual HRESULT handler();
605
606 bool mfKeepSourceMediumLockList;
607};
608
609class Medium::ImportTask : public Medium::Task
610{
611public:
612 ImportTask(Medium *aMedium,
613 Progress *aProgress,
614 const char *aFilename,
615 MediumFormat *aFormat,
616 MediumVariant_T aVariant,
617 VDINTERFACEIO *aVDImageIOIf,
618 void *aVDImageIOUser,
619 Medium *aParent,
620 MediumLockList *aTargetMediumLockList,
621 bool fKeepTargetMediumLockList = false)
622 : Medium::Task(aMedium, aProgress),
623 mFilename(aFilename),
624 mFormat(aFormat),
625 mVariant(aVariant),
626 mParent(aParent),
627 mpTargetMediumLockList(aTargetMediumLockList),
628 mParentCaller(aParent),
629 mfKeepTargetMediumLockList(fKeepTargetMediumLockList)
630 {
631 AssertReturnVoidStmt(aTargetMediumLockList != NULL, mRC = E_FAIL);
632 /* aParent may be NULL */
633 mRC = mParentCaller.rc();
634 if (FAILED(mRC))
635 return;
636
637 mVDImageIfaces = aMedium->m->vdImageIfaces;
638 if (aVDImageIOIf)
639 {
640 int vrc = VDInterfaceAdd(&aVDImageIOIf->Core, "Medium::vdInterfaceIO",
641 VDINTERFACETYPE_IO, aVDImageIOUser,
642 sizeof(VDINTERFACEIO), &mVDImageIfaces);
643 AssertRCReturnVoidStmt(vrc, mRC = E_FAIL);
644 }
645 }
646
647 ~ImportTask()
648 {
649 if (!mfKeepTargetMediumLockList && mpTargetMediumLockList)
650 delete mpTargetMediumLockList;
651 }
652
653 Utf8Str mFilename;
654 ComObjPtr<MediumFormat> mFormat;
655 MediumVariant_T mVariant;
656 const ComObjPtr<Medium> mParent;
657 MediumLockList *mpTargetMediumLockList;
658 PVDINTERFACE mVDImageIfaces;
659
660private:
661 virtual HRESULT handler();
662
663 AutoCaller mParentCaller;
664 bool mfKeepTargetMediumLockList;
665};
666
667/**
668 * Thread function for time-consuming medium tasks.
669 *
670 * @param pvUser Pointer to the Medium::Task instance.
671 */
672/* static */
673DECLCALLBACK(int) Medium::Task::fntMediumTask(RTTHREAD aThread, void *pvUser)
674{
675 LogFlowFuncEnter();
676 AssertReturn(pvUser, (int)E_INVALIDARG);
677 Medium::Task *pTask = static_cast<Medium::Task *>(pvUser);
678
679 pTask->mThread = aThread;
680
681 HRESULT rc = pTask->handler();
682
683 /* complete the progress if run asynchronously */
684 if (pTask->isAsync())
685 if (!pTask->mProgress.isNull())
686 pTask->mProgress->i_notifyComplete(rc);
687
688 /* pTask is no longer needed, delete it. */
689 delete pTask;
690
691 LogFlowFunc(("rc=%Rhrc\n", rc));
692 LogFlowFuncLeave();
693
694 return (int)rc;
695}
696
697/**
698 * PFNVDPROGRESS callback handler for Task operations.
699 *
700 * @param pvUser Pointer to the Progress instance.
701 * @param uPercent Completion percentage (0-100).
702 */
703/*static*/
704DECLCALLBACK(int) Medium::Task::vdProgressCall(void *pvUser, unsigned uPercent)
705{
706 Progress *that = static_cast<Progress *>(pvUser);
707
708 if (that != NULL)
709 {
710 /* update the progress object, capping it at 99% as the final percent
711 * is used for additional operations like setting the UUIDs and similar. */
712 HRESULT rc = that->SetCurrentOperationProgress(uPercent * 99 / 100);
713 if (FAILED(rc))
714 {
715 if (rc == E_FAIL)
716 return VERR_CANCELLED;
717 else
718 return VERR_INVALID_STATE;
719 }
720 }
721
722 return VINF_SUCCESS;
723}
724
725/**
726 * Implementation code for the "create base" task.
727 */
728HRESULT Medium::CreateBaseTask::handler()
729{
730 return mMedium->i_taskCreateBaseHandler(*this);
731}
732
733/**
734 * Implementation code for the "create diff" task.
735 */
736HRESULT Medium::CreateDiffTask::handler()
737{
738 return mMedium->i_taskCreateDiffHandler(*this);
739}
740
741/**
742 * Implementation code for the "clone" task.
743 */
744HRESULT Medium::CloneTask::handler()
745{
746 return mMedium->i_taskCloneHandler(*this);
747}
748
749/**
750 * Implementation code for the "compact" task.
751 */
752HRESULT Medium::CompactTask::handler()
753{
754 return mMedium->i_taskCompactHandler(*this);
755}
756
757/**
758 * Implementation code for the "resize" task.
759 */
760HRESULT Medium::ResizeTask::handler()
761{
762 return mMedium->i_taskResizeHandler(*this);
763}
764
765
766/**
767 * Implementation code for the "reset" task.
768 */
769HRESULT Medium::ResetTask::handler()
770{
771 return mMedium->i_taskResetHandler(*this);
772}
773
774/**
775 * Implementation code for the "delete" task.
776 */
777HRESULT Medium::DeleteTask::handler()
778{
779 return mMedium->i_taskDeleteHandler(*this);
780}
781
782/**
783 * Implementation code for the "merge" task.
784 */
785HRESULT Medium::MergeTask::handler()
786{
787 return mMedium->i_taskMergeHandler(*this);
788}
789
790/**
791 * Implementation code for the "export" task.
792 */
793HRESULT Medium::ExportTask::handler()
794{
795 return mMedium->i_taskExportHandler(*this);
796}
797
798/**
799 * Implementation code for the "import" task.
800 */
801HRESULT Medium::ImportTask::handler()
802{
803 return mMedium->i_taskImportHandler(*this);
804}
805
806////////////////////////////////////////////////////////////////////////////////
807//
808// Medium constructor / destructor
809//
810////////////////////////////////////////////////////////////////////////////////
811
812DEFINE_EMPTY_CTOR_DTOR(Medium)
813
814HRESULT Medium::FinalConstruct()
815{
816 m = new Data;
817
818 /* Initialize the callbacks of the VD error interface */
819 m->vdIfError.pfnError = i_vdErrorCall;
820 m->vdIfError.pfnMessage = NULL;
821
822 /* Initialize the callbacks of the VD config interface */
823 m->vdIfConfig.pfnAreKeysValid = i_vdConfigAreKeysValid;
824 m->vdIfConfig.pfnQuerySize = i_vdConfigQuerySize;
825 m->vdIfConfig.pfnQuery = i_vdConfigQuery;
826 m->vdIfConfig.pfnQueryBytes = NULL;
827
828 /* Initialize the callbacks of the VD TCP interface (we always use the host
829 * IP stack for now) */
830 m->vdIfTcpNet.pfnSocketCreate = i_vdTcpSocketCreate;
831 m->vdIfTcpNet.pfnSocketDestroy = i_vdTcpSocketDestroy;
832 m->vdIfTcpNet.pfnClientConnect = i_vdTcpClientConnect;
833 m->vdIfTcpNet.pfnClientClose = i_vdTcpClientClose;
834 m->vdIfTcpNet.pfnIsClientConnected = i_vdTcpIsClientConnected;
835 m->vdIfTcpNet.pfnSelectOne = i_vdTcpSelectOne;
836 m->vdIfTcpNet.pfnRead = i_vdTcpRead;
837 m->vdIfTcpNet.pfnWrite = i_vdTcpWrite;
838 m->vdIfTcpNet.pfnSgWrite = i_vdTcpSgWrite;
839 m->vdIfTcpNet.pfnFlush = i_vdTcpFlush;
840 m->vdIfTcpNet.pfnSetSendCoalescing = i_vdTcpSetSendCoalescing;
841 m->vdIfTcpNet.pfnGetLocalAddress = i_vdTcpGetLocalAddress;
842 m->vdIfTcpNet.pfnGetPeerAddress = i_vdTcpGetPeerAddress;
843 m->vdIfTcpNet.pfnSelectOneEx = NULL;
844 m->vdIfTcpNet.pfnPoke = NULL;
845
846 /* Initialize the per-disk interface chain (could be done more globally,
847 * but it's not wasting much time or space so it's not worth it). */
848 int vrc;
849 vrc = VDInterfaceAdd(&m->vdIfError.Core,
850 "Medium::vdInterfaceError",
851 VDINTERFACETYPE_ERROR, this,
852 sizeof(VDINTERFACEERROR), &m->vdDiskIfaces);
853 AssertRCReturn(vrc, E_FAIL);
854
855 /* Initialize the per-image interface chain */
856 vrc = VDInterfaceAdd(&m->vdIfConfig.Core,
857 "Medium::vdInterfaceConfig",
858 VDINTERFACETYPE_CONFIG, this,
859 sizeof(VDINTERFACECONFIG), &m->vdImageIfaces);
860 AssertRCReturn(vrc, E_FAIL);
861
862 vrc = VDInterfaceAdd(&m->vdIfTcpNet.Core,
863 "Medium::vdInterfaceTcpNet",
864 VDINTERFACETYPE_TCPNET, this,
865 sizeof(VDINTERFACETCPNET), &m->vdImageIfaces);
866 AssertRCReturn(vrc, E_FAIL);
867
868 return BaseFinalConstruct();
869}
870
871void Medium::FinalRelease()
872{
873 uninit();
874
875 delete m;
876
877 BaseFinalRelease();
878}
879
880/**
881 * Initializes an empty hard disk object without creating or opening an associated
882 * storage unit.
883 *
884 * This gets called by VirtualBox::CreateHardDisk() in which case uuidMachineRegistry
885 * is empty since starting with VirtualBox 4.0, we no longer add opened media to a
886 * registry automatically (this is deferred until the medium is attached to a machine).
887 *
888 * This also gets called when VirtualBox creates diff images; in this case uuidMachineRegistry
889 * is set to the registry of the parent image to make sure they all end up in the same
890 * file.
891 *
892 * For hard disks that don't have the MediumFormatCapabilities_CreateFixed or
893 * MediumFormatCapabilities_CreateDynamic capability (and therefore cannot be created or deleted
894 * with the means of VirtualBox) the associated storage unit is assumed to be
895 * ready for use so the state of the hard disk object will be set to Created.
896 *
897 * @param aVirtualBox VirtualBox object.
898 * @param aFormat
899 * @param aLocation Storage unit location.
900 * @param uuidMachineRegistry The registry to which this medium should be added
901 * (global registry UUID or machine UUID or empty if none).
902 */
903HRESULT Medium::init(VirtualBox *aVirtualBox,
904 const Utf8Str &aFormat,
905 const Utf8Str &aLocation,
906 const Guid &uuidMachineRegistry)
907{
908 AssertReturn(aVirtualBox != NULL, E_FAIL);
909 AssertReturn(!aFormat.isEmpty(), E_FAIL);
910
911 /* Enclose the state transition NotReady->InInit->Ready */
912 AutoInitSpan autoInitSpan(this);
913 AssertReturn(autoInitSpan.isOk(), E_FAIL);
914
915 HRESULT rc = S_OK;
916
917 unconst(m->pVirtualBox) = aVirtualBox;
918
919 if (uuidMachineRegistry.isValid() && !uuidMachineRegistry.isZero())
920 m->llRegistryIDs.push_back(uuidMachineRegistry);
921
922 /* no storage yet */
923 m->state = MediumState_NotCreated;
924
925 /* cannot be a host drive */
926 m->hostDrive = false;
927
928 /* No storage unit is created yet, no need to call Medium::i_queryInfo */
929
930 rc = i_setFormat(aFormat);
931 if (FAILED(rc)) return rc;
932
933 rc = i_setLocation(aLocation);
934 if (FAILED(rc)) return rc;
935
936 if (!(m->formatObj->i_getCapabilities() & ( MediumFormatCapabilities_CreateFixed
937 | MediumFormatCapabilities_CreateDynamic))
938 )
939 {
940 /* Storage for hard disks of this format can neither be explicitly
941 * created by VirtualBox nor deleted, so we place the hard disk to
942 * Inaccessible state here and also add it to the registry. The
943 * state means that one has to use RefreshState() to update the
944 * medium format specific fields. */
945 m->state = MediumState_Inaccessible;
946 // create new UUID
947 unconst(m->id).create();
948
949 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
950 ComObjPtr<Medium> pMedium;
951
952 /*
953 * Check whether the UUID is taken already and create a new one
954 * if required.
955 * Try this only a limited amount of times in case the PRNG is broken
956 * in some way to prevent an endless loop.
957 */
958 for (unsigned i = 0; i < 5; i++)
959 {
960 bool fInUse;
961
962 fInUse = m->pVirtualBox->i_isMediaUuidInUse(m->id, DeviceType_HardDisk);
963 if (fInUse)
964 {
965 // create new UUID
966 unconst(m->id).create();
967 }
968 else
969 break;
970 }
971
972 rc = m->pVirtualBox->i_registerMedium(this, &pMedium, DeviceType_HardDisk, treeLock);
973 Assert(this == pMedium || FAILED(rc));
974 }
975
976 /* Confirm a successful initialization when it's the case */
977 if (SUCCEEDED(rc))
978 autoInitSpan.setSucceeded();
979
980 return rc;
981}
982
983/**
984 * Initializes the medium object by opening the storage unit at the specified
985 * location. The enOpenMode parameter defines whether the medium will be opened
986 * read/write or read-only.
987 *
988 * This gets called by VirtualBox::OpenMedium() and also by
989 * Machine::AttachDevice() and createImplicitDiffs() when new diff
990 * images are created.
991 *
992 * There is no registry for this case since starting with VirtualBox 4.0, we
993 * no longer add opened media to a registry automatically (this is deferred
994 * until the medium is attached to a machine).
995 *
996 * For hard disks, the UUID, format and the parent of this medium will be
997 * determined when reading the medium storage unit. For DVD and floppy images,
998 * which have no UUIDs in their storage units, new UUIDs are created.
999 * If the detected or set parent is not known to VirtualBox, then this method
1000 * will fail.
1001 *
1002 * @param aVirtualBox VirtualBox object.
1003 * @param aLocation Storage unit location.
1004 * @param enOpenMode Whether to open the medium read/write or read-only.
1005 * @param fForceNewUuid Whether a new UUID should be set to avoid duplicates.
1006 * @param aDeviceType Device type of medium.
1007 */
1008HRESULT Medium::init(VirtualBox *aVirtualBox,
1009 const Utf8Str &aLocation,
1010 HDDOpenMode enOpenMode,
1011 bool fForceNewUuid,
1012 DeviceType_T aDeviceType)
1013{
1014 AssertReturn(aVirtualBox, E_INVALIDARG);
1015 AssertReturn(!aLocation.isEmpty(), E_INVALIDARG);
1016
1017 HRESULT rc = S_OK;
1018
1019 {
1020 /* Enclose the state transition NotReady->InInit->Ready */
1021 AutoInitSpan autoInitSpan(this);
1022 AssertReturn(autoInitSpan.isOk(), E_FAIL);
1023
1024 unconst(m->pVirtualBox) = aVirtualBox;
1025
1026 /* there must be a storage unit */
1027 m->state = MediumState_Created;
1028
1029 /* remember device type for correct unregistering later */
1030 m->devType = aDeviceType;
1031
1032 /* cannot be a host drive */
1033 m->hostDrive = false;
1034
1035 /* remember the open mode (defaults to ReadWrite) */
1036 m->hddOpenMode = enOpenMode;
1037
1038 if (aDeviceType == DeviceType_DVD)
1039 m->type = MediumType_Readonly;
1040 else if (aDeviceType == DeviceType_Floppy)
1041 m->type = MediumType_Writethrough;
1042
1043 rc = i_setLocation(aLocation);
1044 if (FAILED(rc)) return rc;
1045
1046 /* get all the information about the medium from the storage unit */
1047 if (fForceNewUuid)
1048 unconst(m->uuidImage).create();
1049
1050 m->state = MediumState_Inaccessible;
1051 m->strLastAccessError = tr("Accessibility check was not yet performed");
1052
1053 /* Confirm a successful initialization before the call to i_queryInfo.
1054 * Otherwise we can end up with a AutoCaller deadlock because the
1055 * medium becomes visible but is not marked as initialized. Causes
1056 * locking trouble (e.g. trying to save media registries) which is
1057 * hard to solve. */
1058 autoInitSpan.setSucceeded();
1059 }
1060
1061 /* we're normal code from now on, no longer init */
1062 AutoCaller autoCaller(this);
1063 if (FAILED(autoCaller.rc()))
1064 return autoCaller.rc();
1065
1066 /* need to call i_queryInfo immediately to correctly place the medium in
1067 * the respective media tree and update other information such as uuid */
1068 rc = i_queryInfo(fForceNewUuid /* fSetImageId */, false /* fSetParentId */,
1069 autoCaller);
1070 if (SUCCEEDED(rc))
1071 {
1072 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1073
1074 /* if the storage unit is not accessible, it's not acceptable for the
1075 * newly opened media so convert this into an error */
1076 if (m->state == MediumState_Inaccessible)
1077 {
1078 Assert(!m->strLastAccessError.isEmpty());
1079 rc = setError(E_FAIL, "%s", m->strLastAccessError.c_str());
1080 alock.release();
1081 autoCaller.release();
1082 uninit();
1083 }
1084 else
1085 {
1086 AssertStmt(!m->id.isZero(),
1087 alock.release(); autoCaller.release(); uninit(); return E_FAIL);
1088
1089 /* storage format must be detected by Medium::i_queryInfo if the
1090 * medium is accessible */
1091 AssertStmt(!m->strFormat.isEmpty(),
1092 alock.release(); autoCaller.release(); uninit(); return E_FAIL);
1093 }
1094 }
1095 else
1096 {
1097 /* opening this image failed, mark the object as dead */
1098 autoCaller.release();
1099 uninit();
1100 }
1101
1102 return rc;
1103}
1104
1105/**
1106 * Initializes the medium object by loading its data from the given settings
1107 * node. In this mode, the medium will always be opened read/write.
1108 *
1109 * In this case, since we're loading from a registry, uuidMachineRegistry is
1110 * always set: it's either the global registry UUID or a machine UUID when
1111 * loading from a per-machine registry.
1112 *
1113 * @param aVirtualBox VirtualBox object.
1114 * @param aParent Parent medium disk or NULL for a root (base) medium.
1115 * @param aDeviceType Device type of the medium.
1116 * @param uuidMachineRegistry The registry to which this medium should be
1117 * added (global registry UUID or machine UUID).
1118 * @param aNode Configuration settings.
1119 * @param strMachineFolder The machine folder with which to resolve relative paths;
1120 * if empty, then we use the VirtualBox home directory
1121 *
1122 * @note Locks the medium tree for writing.
1123 */
1124HRESULT Medium::init(VirtualBox *aVirtualBox,
1125 Medium *aParent,
1126 DeviceType_T aDeviceType,
1127 const Guid &uuidMachineRegistry,
1128 const settings::Medium &data,
1129 const Utf8Str &strMachineFolder)
1130{
1131 using namespace settings;
1132
1133 AssertReturn(aVirtualBox, E_INVALIDARG);
1134
1135 /* Enclose the state transition NotReady->InInit->Ready */
1136 AutoInitSpan autoInitSpan(this);
1137 AssertReturn(autoInitSpan.isOk(), E_FAIL);
1138
1139 HRESULT rc = S_OK;
1140
1141 unconst(m->pVirtualBox) = aVirtualBox;
1142
1143 if (uuidMachineRegistry.isValid() && !uuidMachineRegistry.isZero())
1144 m->llRegistryIDs.push_back(uuidMachineRegistry);
1145
1146 /* register with VirtualBox/parent early, since uninit() will
1147 * unconditionally unregister on failure */
1148 if (aParent)
1149 {
1150 // differencing medium: add to parent
1151 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
1152 m->pParent = aParent;
1153 aParent->m->llChildren.push_back(this);
1154 }
1155
1156 /* see below why we don't call Medium::i_queryInfo (and therefore treat
1157 * the medium as inaccessible for now */
1158 m->state = MediumState_Inaccessible;
1159 m->strLastAccessError = tr("Accessibility check was not yet performed");
1160
1161 /* required */
1162 unconst(m->id) = data.uuid;
1163
1164 /* assume not a host drive */
1165 m->hostDrive = false;
1166
1167 /* optional */
1168 m->strDescription = data.strDescription;
1169
1170 /* required */
1171 if (aDeviceType == DeviceType_HardDisk)
1172 {
1173 AssertReturn(!data.strFormat.isEmpty(), E_FAIL);
1174 rc = i_setFormat(data.strFormat);
1175 if (FAILED(rc)) return rc;
1176 }
1177 else
1178 {
1179 /// @todo handle host drive settings here as well?
1180 if (!data.strFormat.isEmpty())
1181 rc = i_setFormat(data.strFormat);
1182 else
1183 rc = i_setFormat("RAW");
1184 if (FAILED(rc)) return rc;
1185 }
1186
1187 /* optional, only for diffs, default is false; we can only auto-reset
1188 * diff media so they must have a parent */
1189 if (aParent != NULL)
1190 m->autoReset = data.fAutoReset;
1191 else
1192 m->autoReset = false;
1193
1194 /* properties (after setting the format as it populates the map). Note that
1195 * if some properties are not supported but present in the settings file,
1196 * they will still be read and accessible (for possible backward
1197 * compatibility; we can also clean them up from the XML upon next
1198 * XML format version change if we wish) */
1199 for (settings::StringsMap::const_iterator it = data.properties.begin();
1200 it != data.properties.end();
1201 ++it)
1202 {
1203 const Utf8Str &name = it->first;
1204 const Utf8Str &value = it->second;
1205 m->mapProperties[name] = value;
1206 }
1207
1208 /* try to decrypt an optional iSCSI initiator secret */
1209 settings::StringsMap::const_iterator itCph = data.properties.find("InitiatorSecretEncrypted");
1210 if ( itCph != data.properties.end()
1211 && !itCph->second.isEmpty())
1212 {
1213 Utf8Str strPlaintext;
1214 int vrc = m->pVirtualBox->i_decryptSetting(&strPlaintext, itCph->second);
1215 if (RT_SUCCESS(vrc))
1216 m->mapProperties["InitiatorSecret"] = strPlaintext;
1217 }
1218
1219 Utf8Str strFull;
1220 if (m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File)
1221 {
1222 // compose full path of the medium, if it's not fully qualified...
1223 // slightly convoluted logic here. If the caller has given us a
1224 // machine folder, then a relative path will be relative to that:
1225 if ( !strMachineFolder.isEmpty()
1226 && !RTPathStartsWithRoot(data.strLocation.c_str())
1227 )
1228 {
1229 strFull = strMachineFolder;
1230 strFull += RTPATH_SLASH;
1231 strFull += data.strLocation;
1232 }
1233 else
1234 {
1235 // Otherwise use the old VirtualBox "make absolute path" logic:
1236 rc = m->pVirtualBox->i_calculateFullPath(data.strLocation, strFull);
1237 if (FAILED(rc)) return rc;
1238 }
1239 }
1240 else
1241 strFull = data.strLocation;
1242
1243 rc = i_setLocation(strFull);
1244 if (FAILED(rc)) return rc;
1245
1246 if (aDeviceType == DeviceType_HardDisk)
1247 {
1248 /* type is only for base hard disks */
1249 if (m->pParent.isNull())
1250 m->type = data.hdType;
1251 }
1252 else if (aDeviceType == DeviceType_DVD)
1253 m->type = MediumType_Readonly;
1254 else
1255 m->type = MediumType_Writethrough;
1256
1257 /* remember device type for correct unregistering later */
1258 m->devType = aDeviceType;
1259
1260 LogFlowThisFunc(("m->strLocationFull='%s', m->strFormat=%s, m->id={%RTuuid}\n",
1261 m->strLocationFull.c_str(), m->strFormat.c_str(), m->id.raw()));
1262
1263 /* Don't call Medium::i_queryInfo for registered media to prevent the calling
1264 * thread (i.e. the VirtualBox server startup thread) from an unexpected
1265 * freeze but mark it as initially inaccessible instead. The vital UUID,
1266 * location and format properties are read from the registry file above; to
1267 * get the actual state and the rest of the data, the user will have to call
1268 * COMGETTER(State). */
1269
1270 /* load all children */
1271 for (settings::MediaList::const_iterator it = data.llChildren.begin();
1272 it != data.llChildren.end();
1273 ++it)
1274 {
1275 const settings::Medium &med = *it;
1276
1277 ComObjPtr<Medium> pHD;
1278 pHD.createObject();
1279 rc = pHD->init(aVirtualBox,
1280 this, // parent
1281 aDeviceType,
1282 uuidMachineRegistry,
1283 med, // child data
1284 strMachineFolder);
1285 if (FAILED(rc)) break;
1286
1287 AutoWriteLock treeLock(aVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
1288
1289 rc = m->pVirtualBox->i_registerMedium(pHD, &pHD, DeviceType_HardDisk, treeLock);
1290 if (FAILED(rc)) break;
1291 }
1292
1293 /* Confirm a successful initialization when it's the case */
1294 if (SUCCEEDED(rc))
1295 autoInitSpan.setSucceeded();
1296
1297 return rc;
1298}
1299
1300/**
1301 * Initializes the medium object by providing the host drive information.
1302 * Not used for anything but the host floppy/host DVD case.
1303 *
1304 * There is no registry for this case.
1305 *
1306 * @param aVirtualBox VirtualBox object.
1307 * @param aDeviceType Device type of the medium.
1308 * @param aLocation Location of the host drive.
1309 * @param aDescription Comment for this host drive.
1310 *
1311 * @note Locks VirtualBox lock for writing.
1312 */
1313HRESULT Medium::init(VirtualBox *aVirtualBox,
1314 DeviceType_T aDeviceType,
1315 const Utf8Str &aLocation,
1316 const Utf8Str &aDescription /* = Utf8Str::Empty */)
1317{
1318 ComAssertRet(aDeviceType == DeviceType_DVD || aDeviceType == DeviceType_Floppy, E_INVALIDARG);
1319 ComAssertRet(!aLocation.isEmpty(), E_INVALIDARG);
1320
1321 /* Enclose the state transition NotReady->InInit->Ready */
1322 AutoInitSpan autoInitSpan(this);
1323 AssertReturn(autoInitSpan.isOk(), E_FAIL);
1324
1325 unconst(m->pVirtualBox) = aVirtualBox;
1326
1327 // We do not store host drives in VirtualBox.xml or anywhere else, so if we want
1328 // host drives to be identifiable by UUID and not give the drive a different UUID
1329 // every time VirtualBox starts, we need to fake a reproducible UUID here:
1330 RTUUID uuid;
1331 RTUuidClear(&uuid);
1332 if (aDeviceType == DeviceType_DVD)
1333 memcpy(&uuid.au8[0], "DVD", 3);
1334 else
1335 memcpy(&uuid.au8[0], "FD", 2);
1336 /* use device name, adjusted to the end of uuid, shortened if necessary */
1337 size_t lenLocation = aLocation.length();
1338 if (lenLocation > 12)
1339 memcpy(&uuid.au8[4], aLocation.c_str() + (lenLocation - 12), 12);
1340 else
1341 memcpy(&uuid.au8[4 + 12 - lenLocation], aLocation.c_str(), lenLocation);
1342 unconst(m->id) = uuid;
1343
1344 if (aDeviceType == DeviceType_DVD)
1345 m->type = MediumType_Readonly;
1346 else
1347 m->type = MediumType_Writethrough;
1348 m->devType = aDeviceType;
1349 m->state = MediumState_Created;
1350 m->hostDrive = true;
1351 HRESULT rc = i_setFormat("RAW");
1352 if (FAILED(rc)) return rc;
1353 rc = i_setLocation(aLocation);
1354 if (FAILED(rc)) return rc;
1355 m->strDescription = aDescription;
1356
1357 autoInitSpan.setSucceeded();
1358 return S_OK;
1359}
1360
1361/**
1362 * Uninitializes the instance.
1363 *
1364 * Called either from FinalRelease() or by the parent when it gets destroyed.
1365 *
1366 * @note All children of this medium get uninitialized by calling their
1367 * uninit() methods.
1368 */
1369void Medium::uninit()
1370{
1371 /* It is possible that some previous/concurrent uninit has already cleared
1372 * the pVirtualBox reference, and in this case we don't need to continue.
1373 * Normally this would be handled through the AutoUninitSpan magic,
1374 * however this cannot be done at this point as the media tree must be
1375 * locked before reaching the AutoUninitSpan, otherwise deadlocks can
1376 * happen due to*/
1377 ComObjPtr<VirtualBox> pVirtualBox(m->pVirtualBox);
1378 if (!pVirtualBox)
1379 return;
1380
1381 /* Caller must not hold the object or media tree lock over uninit(). */
1382 Assert(!isWriteLockOnCurrentThread());
1383 Assert(!pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
1384
1385 AutoWriteLock treeLock(pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
1386
1387 /* Enclose the state transition Ready->InUninit->NotReady */
1388 AutoUninitSpan autoUninitSpan(this);
1389 if (autoUninitSpan.uninitDone())
1390 return;
1391
1392 if (!m->formatObj.isNull())
1393 m->formatObj.setNull();
1394
1395 if (m->state == MediumState_Deleting)
1396 {
1397 /* This medium has been already deleted (directly or as part of a
1398 * merge). Reparenting has already been done. */
1399 Assert(m->pParent.isNull());
1400 }
1401 else
1402 {
1403 MediaList llChildren(m->llChildren);
1404 m->llChildren.clear();
1405 autoUninitSpan.setSucceeded();
1406
1407 while (!llChildren.empty())
1408 {
1409 ComObjPtr<Medium> pChild = llChildren.front();
1410 llChildren.pop_front();
1411 pChild->m->pParent.setNull();
1412 treeLock.release();
1413 pChild->uninit();
1414 treeLock.acquire();
1415 }
1416
1417 if (m->pParent)
1418 {
1419 // this is a differencing disk: then remove it from the parent's children list
1420 i_deparent();
1421 }
1422 }
1423
1424 unconst(m->pVirtualBox) = NULL;
1425}
1426
1427/**
1428 * Internal helper that removes "this" from the list of children of its
1429 * parent. Used in uninit() and other places when reparenting is necessary.
1430 *
1431 * The caller must hold the medium tree lock!
1432 */
1433void Medium::i_deparent()
1434{
1435 MediaList &llParent = m->pParent->m->llChildren;
1436 for (MediaList::iterator it = llParent.begin();
1437 it != llParent.end();
1438 ++it)
1439 {
1440 Medium *pParentsChild = *it;
1441 if (this == pParentsChild)
1442 {
1443 llParent.erase(it);
1444 break;
1445 }
1446 }
1447 m->pParent.setNull();
1448}
1449
1450/**
1451 * Internal helper that removes "this" from the list of children of its
1452 * parent. Used in uninit() and other places when reparenting is necessary.
1453 *
1454 * The caller must hold the medium tree lock!
1455 */
1456void Medium::i_setParent(const ComObjPtr<Medium> &pParent)
1457{
1458 m->pParent = pParent;
1459 if (pParent)
1460 pParent->m->llChildren.push_back(this);
1461}
1462
1463
1464////////////////////////////////////////////////////////////////////////////////
1465//
1466// IMedium public methods
1467//
1468////////////////////////////////////////////////////////////////////////////////
1469
1470HRESULT Medium::getId(com::Guid &aId)
1471{
1472 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1473
1474 aId = m->id;
1475
1476 return S_OK;
1477}
1478
1479HRESULT Medium::getDescription(com::Utf8Str &aDescription)
1480{
1481 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1482
1483 aDescription = m->strDescription;
1484
1485 return S_OK;
1486}
1487
1488HRESULT Medium::setDescription(const com::Utf8Str &aDescription)
1489{
1490// AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1491
1492 /// @todo update m->description and save the global registry (and local
1493 /// registries of portable VMs referring to this medium), this will also
1494 /// require to add the mRegistered flag to data
1495 NOREF(aDescription);
1496 ReturnComNotImplemented();
1497}
1498
1499HRESULT Medium::getState(MediumState_T *aState)
1500{
1501 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1502 *aState = m->state;
1503
1504 return S_OK;
1505}
1506
1507HRESULT Medium::getVariant(std::vector<MediumVariant_T> &aVariant)
1508{
1509 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1510
1511 const size_t cBits = sizeof(MediumVariant_T) * 8;
1512 aVariant.resize(cBits);
1513 for (size_t i = 0; i < cBits; ++i)
1514 aVariant[i] = (MediumVariant_T)(m->variant & RT_BIT(i));
1515
1516 return S_OK;
1517}
1518
1519HRESULT Medium::getLocation(com::Utf8Str &aLocation)
1520{
1521 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1522
1523 aLocation = m->strLocationFull;
1524
1525 return S_OK;
1526}
1527
1528HRESULT Medium::getName(com::Utf8Str &aName)
1529{
1530 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1531
1532 aName = i_getName();
1533
1534 return S_OK;
1535}
1536
1537HRESULT Medium::getDeviceType(DeviceType_T *aDeviceType)
1538{
1539 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1540
1541 *aDeviceType = m->devType;
1542
1543 return S_OK;
1544}
1545
1546HRESULT Medium::getHostDrive(BOOL *aHostDrive)
1547{
1548 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1549
1550 *aHostDrive = m->hostDrive;
1551
1552 return S_OK;
1553}
1554
1555HRESULT Medium::getSize(LONG64 *aSize)
1556{
1557 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1558
1559 *aSize = m->size;
1560
1561 return S_OK;
1562}
1563
1564HRESULT Medium::getFormat(com::Utf8Str &aFormat)
1565{
1566 /* no need to lock, m->strFormat is const */
1567
1568 aFormat = m->strFormat;
1569 return S_OK;
1570}
1571
1572HRESULT Medium::getMediumFormat(ComPtr<IMediumFormat> &aMediumFormat)
1573{
1574 /* no need to lock, m->formatObj is const */
1575 m->formatObj.queryInterfaceTo(aMediumFormat.asOutParam());
1576
1577 return S_OK;
1578}
1579
1580HRESULT Medium::getType(MediumType_T *aType)
1581{
1582 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1583
1584 *aType = m->type;
1585
1586 return S_OK;
1587}
1588
1589HRESULT Medium::setType(MediumType_T aType)
1590{
1591 // we access mParent and members
1592 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
1593 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
1594
1595 switch (m->state)
1596 {
1597 case MediumState_Created:
1598 case MediumState_Inaccessible:
1599 break;
1600 default:
1601 return i_setStateError();
1602 }
1603
1604 if (m->type == aType)
1605 {
1606 /* Nothing to do */
1607 return S_OK;
1608 }
1609
1610 DeviceType_T devType = i_getDeviceType();
1611 // DVD media can only be readonly.
1612 if (devType == DeviceType_DVD && aType != MediumType_Readonly)
1613 return setError(VBOX_E_INVALID_OBJECT_STATE,
1614 tr("Cannot change the type of DVD medium '%s'"),
1615 m->strLocationFull.c_str());
1616 // Floppy media can only be writethrough or readonly.
1617 if ( devType == DeviceType_Floppy
1618 && aType != MediumType_Writethrough
1619 && aType != MediumType_Readonly)
1620 return setError(VBOX_E_INVALID_OBJECT_STATE,
1621 tr("Cannot change the type of floppy medium '%s'"),
1622 m->strLocationFull.c_str());
1623
1624 /* cannot change the type of a differencing medium */
1625 if (m->pParent)
1626 return setError(VBOX_E_INVALID_OBJECT_STATE,
1627 tr("Cannot change the type of medium '%s' because it is a differencing medium"),
1628 m->strLocationFull.c_str());
1629
1630 /* Cannot change the type of a medium being in use by more than one VM.
1631 * If the change is to Immutable or MultiAttach then it must not be
1632 * directly attached to any VM, otherwise the assumptions about indirect
1633 * attachment elsewhere are violated and the VM becomes inaccessible.
1634 * Attaching an immutable medium triggers the diff creation, and this is
1635 * vital for the correct operation. */
1636 if ( m->backRefs.size() > 1
1637 || ( ( aType == MediumType_Immutable
1638 || aType == MediumType_MultiAttach)
1639 && m->backRefs.size() > 0))
1640 return setError(VBOX_E_INVALID_OBJECT_STATE,
1641 tr("Cannot change the type of medium '%s' because it is attached to %d virtual machines"),
1642 m->strLocationFull.c_str(), m->backRefs.size());
1643
1644 switch (aType)
1645 {
1646 case MediumType_Normal:
1647 case MediumType_Immutable:
1648 case MediumType_MultiAttach:
1649 {
1650 /* normal can be easily converted to immutable and vice versa even
1651 * if they have children as long as they are not attached to any
1652 * machine themselves */
1653 break;
1654 }
1655 case MediumType_Writethrough:
1656 case MediumType_Shareable:
1657 case MediumType_Readonly:
1658 {
1659 /* cannot change to writethrough, shareable or readonly
1660 * if there are children */
1661 if (i_getChildren().size() != 0)
1662 return setError(VBOX_E_OBJECT_IN_USE,
1663 tr("Cannot change type for medium '%s' since it has %d child media"),
1664 m->strLocationFull.c_str(), i_getChildren().size());
1665 if (aType == MediumType_Shareable)
1666 {
1667 MediumVariant_T variant = i_getVariant();
1668 if (!(variant & MediumVariant_Fixed))
1669 return setError(VBOX_E_INVALID_OBJECT_STATE,
1670 tr("Cannot change type for medium '%s' to 'Shareable' since it is a dynamic medium storage unit"),
1671 m->strLocationFull.c_str());
1672 }
1673 else if (aType == MediumType_Readonly && devType == DeviceType_HardDisk)
1674 {
1675 // Readonly hard disks are not allowed, this medium type is reserved for
1676 // DVDs and floppy images at the moment. Later we might allow readonly hard
1677 // disks, but that's extremely unusual and many guest OSes will have trouble.
1678 return setError(VBOX_E_INVALID_OBJECT_STATE,
1679 tr("Cannot change type for medium '%s' to 'Readonly' since it is a hard disk"),
1680 m->strLocationFull.c_str());
1681 }
1682 break;
1683 }
1684 default:
1685 AssertFailedReturn(E_FAIL);
1686 }
1687
1688 if (aType == MediumType_MultiAttach)
1689 {
1690 // This type is new with VirtualBox 4.0 and therefore requires settings
1691 // version 1.11 in the settings backend. Unfortunately it is not enough to do
1692 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
1693 // two reasons: The medium type is a property of the media registry tree, which
1694 // can reside in the global config file (for pre-4.0 media); we would therefore
1695 // possibly need to bump the global config version. We don't want to do that though
1696 // because that might make downgrading to pre-4.0 impossible.
1697 // As a result, we can only use these two new types if the medium is NOT in the
1698 // global registry:
1699 const Guid &uuidGlobalRegistry = m->pVirtualBox->i_getGlobalRegistryId();
1700 if (i_isInRegistry(uuidGlobalRegistry))
1701 return setError(VBOX_E_INVALID_OBJECT_STATE,
1702 tr("Cannot change type for medium '%s': the media type 'MultiAttach' can only be used "
1703 "on media registered with a machine that was created with VirtualBox 4.0 or later"),
1704 m->strLocationFull.c_str());
1705 }
1706
1707 m->type = aType;
1708
1709 // save the settings
1710 mlock.release();
1711 treeLock.release();
1712 i_markRegistriesModified();
1713 m->pVirtualBox->i_saveModifiedRegistries();
1714
1715 return S_OK;
1716}
1717
1718HRESULT Medium::getAllowedTypes(std::vector<MediumType_T> &aAllowedTypes)
1719{
1720 NOREF(aAllowedTypes);
1721 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1722
1723 ReturnComNotImplemented();
1724}
1725
1726HRESULT Medium::getParent(AutoCaller &autoCaller, ComPtr<IMedium> &aParent)
1727{
1728 autoCaller.release();
1729
1730 /* It is possible that some previous/concurrent uninit has already cleared
1731 * the pVirtualBox reference, see #uninit(). */
1732 ComObjPtr<VirtualBox> pVirtualBox(m->pVirtualBox);
1733
1734 /* we access mParent */
1735 AutoReadLock treeLock(!pVirtualBox.isNull() ? &pVirtualBox->i_getMediaTreeLockHandle() : NULL COMMA_LOCKVAL_SRC_POS);
1736
1737 autoCaller.add();
1738 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1739
1740 m->pParent.queryInterfaceTo(aParent.asOutParam());
1741
1742 return S_OK;
1743}
1744
1745HRESULT Medium::getChildren(AutoCaller &autoCaller, std::vector<ComPtr<IMedium> > &aChildren)
1746{
1747 autoCaller.release();
1748
1749 /* It is possible that some previous/concurrent uninit has already cleared
1750 * the pVirtualBox reference, see #uninit(). */
1751 ComObjPtr<VirtualBox> pVirtualBox(m->pVirtualBox);
1752
1753 /* we access children */
1754 AutoReadLock treeLock(!pVirtualBox.isNull() ? &pVirtualBox->i_getMediaTreeLockHandle() : NULL COMMA_LOCKVAL_SRC_POS);
1755
1756 autoCaller.add();
1757 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1758
1759 MediaList children(this->i_getChildren());
1760 aChildren.resize(children.size());
1761 size_t i = 0;
1762 for (MediaList::const_iterator it = children.begin(); it != children.end(); ++it, ++i)
1763 (*it).queryInterfaceTo(aChildren[i].asOutParam());
1764 return S_OK;
1765}
1766
1767HRESULT Medium::getBase(AutoCaller &autoCaller, ComPtr<IMedium> &aBase)
1768{
1769 autoCaller.release();
1770
1771 /* i_getBase() will do callers/locking */
1772 i_getBase().queryInterfaceTo(aBase.asOutParam());
1773
1774 return S_OK;
1775}
1776
1777HRESULT Medium::getReadOnly(BOOL *aReadOnly)
1778{
1779 /* isReadOnly() will do locking */
1780 *aReadOnly = i_isReadOnly();
1781
1782 return S_OK;
1783}
1784
1785HRESULT Medium::getLogicalSize(LONG64 *aLogicalSize)
1786{
1787 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1788
1789 *aLogicalSize = m->logicalSize;
1790
1791 return S_OK;
1792}
1793
1794HRESULT Medium::getAutoReset(BOOL *aAutoReset)
1795{
1796 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1797
1798 if (m->pParent.isNull())
1799 *aAutoReset = FALSE;
1800 else
1801 *aAutoReset = m->autoReset;
1802
1803 return S_OK;
1804}
1805
1806HRESULT Medium::setAutoReset(BOOL aAutoReset)
1807{
1808 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
1809
1810 if (m->pParent.isNull())
1811 return setError(VBOX_E_NOT_SUPPORTED,
1812 tr("Medium '%s' is not differencing"),
1813 m->strLocationFull.c_str());
1814
1815 if (m->autoReset != !!aAutoReset)
1816 {
1817 m->autoReset = !!aAutoReset;
1818
1819 // save the settings
1820 mlock.release();
1821 i_markRegistriesModified();
1822 m->pVirtualBox->i_saveModifiedRegistries();
1823 }
1824
1825 return S_OK;
1826}
1827
1828HRESULT Medium::getLastAccessError(com::Utf8Str &aLastAccessError)
1829{
1830 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1831
1832 aLastAccessError = m->strLastAccessError;
1833
1834 return S_OK;
1835}
1836
1837HRESULT Medium::getMachineIds(std::vector<com::Guid> &aMachineIds)
1838{
1839 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1840
1841 if (m->backRefs.size() != 0)
1842 {
1843 BackRefList brlist(m->backRefs);
1844 aMachineIds.resize(brlist.size());
1845 size_t i = 0;
1846 for (BackRefList::const_iterator it = brlist.begin(); it != brlist.end(); ++it, ++i)
1847 aMachineIds[i] = it->machineId;
1848 }
1849
1850 return S_OK;
1851}
1852
1853HRESULT Medium::setIds(AutoCaller &autoCaller,
1854 BOOL aSetImageId,
1855 const com::Guid &aImageId,
1856 BOOL aSetParentId,
1857 const com::Guid &aParentId)
1858{
1859 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1860
1861 switch (m->state)
1862 {
1863 case MediumState_Created:
1864 break;
1865 default:
1866 return i_setStateError();
1867 }
1868
1869 Guid imageId, parentId;
1870 if (aSetImageId)
1871 {
1872 if (aImageId.toUtf16().isEmpty())
1873 imageId.create();
1874 else
1875 {
1876 imageId = aImageId;
1877 if (!imageId.isValid())
1878 return setError(E_INVALIDARG, tr("Argument %s is invalid"), "aImageId");
1879 }
1880 }
1881 if (aSetParentId)
1882 {
1883 if (aParentId.toUtf16().isEmpty())
1884 parentId.create();
1885 else
1886 parentId = aParentId;
1887 }
1888
1889 unconst(m->uuidImage) = imageId;
1890 unconst(m->uuidParentImage) = parentId;
1891
1892 // must not hold any locks before calling Medium::i_queryInfo
1893 alock.release();
1894
1895 HRESULT rc = i_queryInfo(!!aSetImageId /* fSetImageId */,
1896 !!aSetParentId /* fSetParentId */,
1897 autoCaller);
1898
1899 return rc;
1900}
1901
1902HRESULT Medium::refreshState(AutoCaller &autoCaller, MediumState_T *aState)
1903{
1904 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1905
1906 HRESULT rc = S_OK;
1907
1908 switch (m->state)
1909 {
1910 case MediumState_Created:
1911 case MediumState_Inaccessible:
1912 case MediumState_LockedRead:
1913 {
1914 // must not hold any locks before calling Medium::i_queryInfo
1915 alock.release();
1916
1917 rc = i_queryInfo(false /* fSetImageId */, false /* fSetParentId */,
1918 autoCaller);
1919
1920 alock.acquire();
1921 break;
1922 }
1923 default:
1924 break;
1925 }
1926
1927 *aState = m->state;
1928
1929 return rc;
1930}
1931
1932HRESULT Medium::getSnapshotIds(const com::Guid &aMachineId,
1933 std::vector<com::Guid> &aSnapshotIds)
1934{
1935 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1936
1937 for (BackRefList::const_iterator it = m->backRefs.begin();
1938 it != m->backRefs.end(); ++it)
1939 {
1940 if (it->machineId == aMachineId)
1941 {
1942 size_t size = it->llSnapshotIds.size();
1943
1944 /* if the medium is attached to the machine in the current state, we
1945 * return its ID as the first element of the array */
1946 if (it->fInCurState)
1947 ++size;
1948
1949 if (size > 0)
1950 {
1951 aSnapshotIds.resize(size);
1952
1953 size_t j = 0;
1954 if (it->fInCurState)
1955 aSnapshotIds[j++] = it->machineId.toUtf16();
1956
1957 for(GuidList::const_iterator jt = it->llSnapshotIds.begin(); jt != it->llSnapshotIds.end(); ++jt, ++j)
1958 aSnapshotIds[j] = (*jt);
1959 }
1960
1961 break;
1962 }
1963 }
1964
1965 return S_OK;
1966}
1967
1968HRESULT Medium::lockRead(ComPtr<IToken> &aToken)
1969{
1970 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1971
1972 /* Wait for a concurrently running Medium::i_queryInfo to complete. */
1973 if (m->queryInfoRunning)
1974 {
1975 /* Must not hold the media tree lock, as Medium::i_queryInfo needs this
1976 * lock and thus we would run into a deadlock here. */
1977 Assert(!m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
1978 while (m->queryInfoRunning)
1979 {
1980 alock.release();
1981 /* must not hold the object lock now */
1982 Assert(!isWriteLockOnCurrentThread());
1983 {
1984 AutoReadLock qlock(m->queryInfoSem COMMA_LOCKVAL_SRC_POS);
1985 }
1986 alock.acquire();
1987 }
1988 }
1989
1990 HRESULT rc = S_OK;
1991
1992 switch (m->state)
1993 {
1994 case MediumState_Created:
1995 case MediumState_Inaccessible:
1996 case MediumState_LockedRead:
1997 {
1998 ++m->readers;
1999
2000 ComAssertMsgBreak(m->readers != 0, ("Counter overflow"), rc = E_FAIL);
2001
2002 /* Remember pre-lock state */
2003 if (m->state != MediumState_LockedRead)
2004 m->preLockState = m->state;
2005
2006 LogFlowThisFunc(("Okay - prev state=%d readers=%d\n", m->state, m->readers));
2007 m->state = MediumState_LockedRead;
2008
2009 ComObjPtr<MediumLockToken> pToken;
2010 rc = pToken.createObject();
2011 if (SUCCEEDED(rc))
2012 rc = pToken->init(this, false /* fWrite */);
2013 if (FAILED(rc))
2014 {
2015 --m->readers;
2016 if (m->readers == 0)
2017 m->state = m->preLockState;
2018 return rc;
2019 }
2020
2021 pToken.queryInterfaceTo(aToken.asOutParam());
2022 break;
2023 }
2024 default:
2025 {
2026 LogFlowThisFunc(("Failing - state=%d\n", m->state));
2027 rc = i_setStateError();
2028 break;
2029 }
2030 }
2031
2032 return rc;
2033}
2034
2035/**
2036 * @note @a aState may be NULL if the state value is not needed (only for
2037 * in-process calls).
2038 */
2039HRESULT Medium::i_unlockRead(MediumState_T *aState)
2040{
2041 AutoCaller autoCaller(this);
2042 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2043
2044 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2045
2046 HRESULT rc = S_OK;
2047
2048 switch (m->state)
2049 {
2050 case MediumState_LockedRead:
2051 {
2052 ComAssertMsgBreak(m->readers != 0, ("Counter underflow"), rc = E_FAIL);
2053 --m->readers;
2054
2055 /* Reset the state after the last reader */
2056 if (m->readers == 0)
2057 {
2058 m->state = m->preLockState;
2059 /* There are cases where we inject the deleting state into
2060 * a medium locked for reading. Make sure #unmarkForDeletion()
2061 * gets the right state afterwards. */
2062 if (m->preLockState == MediumState_Deleting)
2063 m->preLockState = MediumState_Created;
2064 }
2065
2066 LogFlowThisFunc(("new state=%d\n", m->state));
2067 break;
2068 }
2069 default:
2070 {
2071 LogFlowThisFunc(("Failing - state=%d\n", m->state));
2072 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
2073 tr("Medium '%s' is not locked for reading"),
2074 m->strLocationFull.c_str());
2075 break;
2076 }
2077 }
2078
2079 /* return the current state after */
2080 if (aState)
2081 *aState = m->state;
2082
2083 return rc;
2084}
2085HRESULT Medium::lockWrite(ComPtr<IToken> &aToken)
2086{
2087 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2088
2089 /* Wait for a concurrently running Medium::i_queryInfo to complete. */
2090 if (m->queryInfoRunning)
2091 {
2092 /* Must not hold the media tree lock, as Medium::i_queryInfo needs this
2093 * lock and thus we would run into a deadlock here. */
2094 Assert(!m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
2095 while (m->queryInfoRunning)
2096 {
2097 alock.release();
2098 /* must not hold the object lock now */
2099 Assert(!isWriteLockOnCurrentThread());
2100 {
2101 AutoReadLock qlock(m->queryInfoSem COMMA_LOCKVAL_SRC_POS);
2102 }
2103 alock.acquire();
2104 }
2105 }
2106
2107 HRESULT rc = S_OK;
2108
2109 switch (m->state)
2110 {
2111 case MediumState_Created:
2112 case MediumState_Inaccessible:
2113 {
2114 m->preLockState = m->state;
2115
2116 LogFlowThisFunc(("Okay - prev state=%d locationFull=%s\n", m->state, i_getLocationFull().c_str()));
2117 m->state = MediumState_LockedWrite;
2118
2119 ComObjPtr<MediumLockToken> pToken;
2120 rc = pToken.createObject();
2121 if (SUCCEEDED(rc))
2122 rc = pToken->init(this, true /* fWrite */);
2123 if (FAILED(rc))
2124 {
2125 m->state = m->preLockState;
2126 return rc;
2127 }
2128
2129 pToken.queryInterfaceTo(aToken.asOutParam());
2130 break;
2131 }
2132 default:
2133 {
2134 LogFlowThisFunc(("Failing - state=%d locationFull=%s\n", m->state, i_getLocationFull().c_str()));
2135 rc = i_setStateError();
2136 break;
2137 }
2138 }
2139
2140 return rc;
2141}
2142
2143/**
2144 * @note @a aState may be NULL if the state value is not needed (only for
2145 * in-process calls).
2146 */
2147HRESULT Medium::i_unlockWrite(MediumState_T *aState)
2148{
2149 AutoCaller autoCaller(this);
2150 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2151
2152 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2153
2154 HRESULT rc = S_OK;
2155
2156 switch (m->state)
2157 {
2158 case MediumState_LockedWrite:
2159 {
2160 m->state = m->preLockState;
2161 /* There are cases where we inject the deleting state into
2162 * a medium locked for writing. Make sure #unmarkForDeletion()
2163 * gets the right state afterwards. */
2164 if (m->preLockState == MediumState_Deleting)
2165 m->preLockState = MediumState_Created;
2166 LogFlowThisFunc(("new state=%d locationFull=%s\n", m->state, i_getLocationFull().c_str()));
2167 break;
2168 }
2169 default:
2170 {
2171 LogFlowThisFunc(("Failing - state=%d locationFull=%s\n", m->state, i_getLocationFull().c_str()));
2172 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
2173 tr("Medium '%s' is not locked for writing"),
2174 m->strLocationFull.c_str());
2175 break;
2176 }
2177 }
2178
2179 /* return the current state after */
2180 if (aState)
2181 *aState = m->state;
2182
2183 return rc;
2184}
2185
2186HRESULT Medium::close(AutoCaller &aAutoCaller)
2187{
2188 // make a copy of VirtualBox pointer which gets nulled by uninit()
2189 ComObjPtr<VirtualBox> pVirtualBox(m->pVirtualBox);
2190
2191 MultiResult mrc = i_close(aAutoCaller);
2192
2193 pVirtualBox->i_saveModifiedRegistries();
2194
2195 return mrc;
2196}
2197
2198HRESULT Medium::getProperty(const com::Utf8Str &aName,
2199 com::Utf8Str &aValue)
2200{
2201 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2202
2203 settings::StringsMap::const_iterator it = m->mapProperties.find(aName);
2204 if (it == m->mapProperties.end())
2205 {
2206 if (!aName.startsWith("Special/"))
2207 return setError(VBOX_E_OBJECT_NOT_FOUND,
2208 tr("Property '%s' does not exist"), aName.c_str());
2209 else
2210 /* be more silent here */
2211 return VBOX_E_OBJECT_NOT_FOUND;
2212 }
2213
2214 aValue = it->second;
2215
2216 return S_OK;
2217}
2218
2219HRESULT Medium::setProperty(const com::Utf8Str &aName,
2220 const com::Utf8Str &aValue)
2221{
2222 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
2223
2224 switch (m->state)
2225 {
2226 case MediumState_Created:
2227 case MediumState_Inaccessible:
2228 break;
2229 default:
2230 return i_setStateError();
2231 }
2232
2233 settings::StringsMap::iterator it = m->mapProperties.find(aName);
2234 if ( !aName.startsWith("Special/")
2235 && !i_isPropertyForFilter(aName))
2236 {
2237 if (it == m->mapProperties.end())
2238 return setError(VBOX_E_OBJECT_NOT_FOUND,
2239 tr("Property '%s' does not exist"),
2240 aName.c_str());
2241 it->second = aValue;
2242 }
2243 else
2244 {
2245 if (it == m->mapProperties.end())
2246 {
2247 if (!aValue.isEmpty())
2248 m->mapProperties[aName] = aValue;
2249 }
2250 else
2251 {
2252 if (!aValue.isEmpty())
2253 it->second = aValue;
2254 else
2255 m->mapProperties.erase(it);
2256 }
2257 }
2258
2259 // save the settings
2260 mlock.release();
2261 i_markRegistriesModified();
2262 m->pVirtualBox->i_saveModifiedRegistries();
2263
2264 return S_OK;
2265}
2266
2267HRESULT Medium::getProperties(const com::Utf8Str &aNames,
2268 std::vector<com::Utf8Str> &aReturnNames,
2269 std::vector<com::Utf8Str> &aReturnValues)
2270{
2271 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2272
2273 /// @todo make use of aNames according to the documentation
2274 NOREF(aNames);
2275
2276 aReturnNames.resize(m->mapProperties.size());
2277 aReturnValues.resize(m->mapProperties.size());
2278 size_t i = 0;
2279 for (settings::StringsMap::const_iterator it = m->mapProperties.begin();
2280 it != m->mapProperties.end();
2281 ++it, ++i)
2282 {
2283 aReturnNames[i] = it->first;
2284 aReturnValues[i] = it->second;
2285 }
2286 return S_OK;
2287}
2288
2289HRESULT Medium::setProperties(const std::vector<com::Utf8Str> &aNames,
2290 const std::vector<com::Utf8Str> &aValues)
2291{
2292 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
2293
2294 /* first pass: validate names */
2295 for (size_t i = 0;
2296 i < aNames.size();
2297 ++i)
2298 {
2299 Utf8Str strName(aNames[i]);
2300 if ( !strName.startsWith("Special/")
2301 && !i_isPropertyForFilter(strName)
2302 && m->mapProperties.find(strName) == m->mapProperties.end())
2303 return setError(VBOX_E_OBJECT_NOT_FOUND,
2304 tr("Property '%s' does not exist"), strName.c_str());
2305 }
2306
2307 /* second pass: assign */
2308 for (size_t i = 0;
2309 i < aNames.size();
2310 ++i)
2311 {
2312 Utf8Str strName(aNames[i]);
2313 Utf8Str strValue(aValues[i]);
2314 settings::StringsMap::iterator it = m->mapProperties.find(strName);
2315 if ( !strName.startsWith("Special/")
2316 && !i_isPropertyForFilter(strName))
2317 {
2318 AssertReturn(it != m->mapProperties.end(), E_FAIL);
2319 it->second = strValue;
2320 }
2321 else
2322 {
2323 if (it == m->mapProperties.end())
2324 {
2325 if (!strValue.isEmpty())
2326 m->mapProperties[strName] = strValue;
2327 }
2328 else
2329 {
2330 if (!strValue.isEmpty())
2331 it->second = strValue;
2332 else
2333 m->mapProperties.erase(it);
2334 }
2335 }
2336 }
2337
2338 // save the settings
2339 mlock.release();
2340 i_markRegistriesModified();
2341 m->pVirtualBox->i_saveModifiedRegistries();
2342
2343 return S_OK;
2344}
2345HRESULT Medium::createBaseStorage(LONG64 aLogicalSize,
2346 const std::vector<MediumVariant_T> &aVariant,
2347 ComPtr<IProgress> &aProgress)
2348{
2349 if (aLogicalSize < 0)
2350 return setError(E_INVALIDARG, tr("The medium size argument (%lld) is negative"), aLogicalSize);
2351
2352 HRESULT rc = S_OK;
2353 ComObjPtr<Progress> pProgress;
2354 Medium::Task *pTask = NULL;
2355
2356 try
2357 {
2358 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2359
2360 ULONG mediumVariantFlags = 0;
2361
2362 if (aVariant.size())
2363 {
2364 com::SafeArray<MediumVariant_T> variants(aVariant);
2365 for (size_t i = 0; i < variants.size(); i++)
2366 mediumVariantFlags |= variants[i];
2367 }
2368
2369 mediumVariantFlags &= ((unsigned)~MediumVariant_Diff);
2370
2371 if ( !(mediumVariantFlags & MediumVariant_Fixed)
2372 && !(m->formatObj->i_getCapabilities() & MediumFormatCapabilities_CreateDynamic))
2373 throw setError(VBOX_E_NOT_SUPPORTED,
2374 tr("Medium format '%s' does not support dynamic storage creation"),
2375 m->strFormat.c_str());
2376
2377 if ( (mediumVariantFlags & MediumVariant_Fixed)
2378 && !(m->formatObj->i_getCapabilities() & MediumFormatCapabilities_CreateDynamic))
2379 throw setError(VBOX_E_NOT_SUPPORTED,
2380 tr("Medium format '%s' does not support fixed storage creation"),
2381 m->strFormat.c_str());
2382
2383 if (m->state != MediumState_NotCreated)
2384 throw i_setStateError();
2385
2386 pProgress.createObject();
2387 rc = pProgress->init(m->pVirtualBox,
2388 static_cast<IMedium*>(this),
2389 (mediumVariantFlags & MediumVariant_Fixed)
2390 ? BstrFmt(tr("Creating fixed medium storage unit '%s'"), m->strLocationFull.c_str()).raw()
2391 : BstrFmt(tr("Creating dynamic medium storage unit '%s'"), m->strLocationFull.c_str()).raw(),
2392 TRUE /* aCancelable */);
2393 if (FAILED(rc))
2394 throw rc;
2395
2396 /* setup task object to carry out the operation asynchronously */
2397 pTask = new Medium::CreateBaseTask(this, pProgress, aLogicalSize,
2398 (MediumVariant_T)mediumVariantFlags);
2399 //(MediumVariant_T)aVariant);
2400 rc = pTask->rc();
2401 AssertComRC(rc);
2402 if (FAILED(rc))
2403 throw rc;
2404
2405 m->state = MediumState_Creating;
2406 }
2407 catch (HRESULT aRC) { rc = aRC; }
2408
2409 if (SUCCEEDED(rc))
2410 {
2411 rc = i_startThread(pTask);
2412
2413 if (SUCCEEDED(rc))
2414 pProgress.queryInterfaceTo(aProgress.asOutParam());
2415 }
2416 else if (pTask != NULL)
2417 delete pTask;
2418
2419 return rc;
2420}
2421
2422HRESULT Medium::deleteStorage(ComPtr<IProgress> &aProgress)
2423{
2424 ComObjPtr<Progress> pProgress;
2425
2426 MultiResult mrc = i_deleteStorage(&pProgress,
2427 false /* aWait */);
2428 /* Must save the registries in any case, since an entry was removed. */
2429 m->pVirtualBox->i_saveModifiedRegistries();
2430
2431 if (SUCCEEDED(mrc))
2432 pProgress.queryInterfaceTo(aProgress.asOutParam());
2433
2434 return mrc;
2435}
2436
2437HRESULT Medium::createDiffStorage(const ComPtr<IMedium> &aTarget,
2438 const std::vector<MediumVariant_T> &aVariant,
2439 ComPtr<IProgress> &aProgress)
2440{
2441 IMedium *aT = aTarget;
2442 ComObjPtr<Medium> diff = static_cast<Medium*>(aT);
2443
2444 // locking: we need the tree lock first because we access parent pointers
2445 AutoMultiWriteLock3 alock(&m->pVirtualBox->i_getMediaTreeLockHandle(),
2446 this->lockHandle(), diff->lockHandle() COMMA_LOCKVAL_SRC_POS);
2447
2448 if (m->type == MediumType_Writethrough)
2449 return setError(VBOX_E_INVALID_OBJECT_STATE,
2450 tr("Medium type of '%s' is Writethrough"),
2451 m->strLocationFull.c_str());
2452 else if (m->type == MediumType_Shareable)
2453 return setError(VBOX_E_INVALID_OBJECT_STATE,
2454 tr("Medium type of '%s' is Shareable"),
2455 m->strLocationFull.c_str());
2456 else if (m->type == MediumType_Readonly)
2457 return setError(VBOX_E_INVALID_OBJECT_STATE,
2458 tr("Medium type of '%s' is Readonly"),
2459 m->strLocationFull.c_str());
2460
2461 /* Apply the normal locking logic to the entire chain. */
2462 MediumLockList *pMediumLockList(new MediumLockList());
2463 alock.release();
2464 HRESULT rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
2465 true /* fMediumLockWrite */,
2466 this,
2467 *pMediumLockList);
2468 alock.acquire();
2469 if (FAILED(rc))
2470 {
2471 delete pMediumLockList;
2472 return rc;
2473 }
2474
2475 alock.release();
2476 rc = pMediumLockList->Lock();
2477 alock.acquire();
2478 if (FAILED(rc))
2479 {
2480 delete pMediumLockList;
2481
2482 return setError(rc, tr("Could not lock medium when creating diff '%s'"),
2483 diff->i_getLocationFull().c_str());
2484 }
2485
2486 Guid parentMachineRegistry;
2487 if (i_getFirstRegistryMachineId(parentMachineRegistry))
2488 {
2489 /* since this medium has been just created it isn't associated yet */
2490 diff->m->llRegistryIDs.push_back(parentMachineRegistry);
2491 alock.release();
2492 diff->i_markRegistriesModified();
2493 alock.acquire();
2494 }
2495
2496 alock.release();
2497
2498 ComObjPtr<Progress> pProgress;
2499
2500 ULONG mediumVariantFlags = 0;
2501
2502 if (aVariant.size())
2503 {
2504 com::SafeArray<MediumVariant_T> variants(aVariant);
2505 for (size_t i = 0; i < variants.size(); i++)
2506 mediumVariantFlags |= variants[i];
2507 }
2508
2509 rc = i_createDiffStorage(diff, (MediumVariant_T)mediumVariantFlags, pMediumLockList,
2510 &pProgress, false /* aWait */);
2511 if (FAILED(rc))
2512 delete pMediumLockList;
2513 else
2514 pProgress.queryInterfaceTo(aProgress.asOutParam());
2515
2516 return rc;
2517}
2518
2519HRESULT Medium::mergeTo(const ComPtr<IMedium> &aTarget,
2520 ComPtr<IProgress> &aProgress)
2521{
2522
2523 IMedium *aT = aTarget;
2524
2525 ComAssertRet(aT != this, E_INVALIDARG);
2526
2527 ComObjPtr<Medium> pTarget = static_cast<Medium*>(aT);
2528
2529 bool fMergeForward = false;
2530 ComObjPtr<Medium> pParentForTarget;
2531 MediumLockList *pChildrenToReparent = NULL;
2532 MediumLockList *pMediumLockList = NULL;
2533
2534 HRESULT rc = S_OK;
2535
2536 rc = i_prepareMergeTo(pTarget, NULL, NULL, true, fMergeForward,
2537 pParentForTarget, pChildrenToReparent, pMediumLockList);
2538 if (FAILED(rc)) return rc;
2539
2540 ComObjPtr<Progress> pProgress;
2541
2542 rc = i_mergeTo(pTarget, fMergeForward, pParentForTarget, pChildrenToReparent,
2543 pMediumLockList, &pProgress, false /* aWait */);
2544 if (FAILED(rc))
2545 i_cancelMergeTo(pChildrenToReparent, pMediumLockList);
2546 else
2547 pProgress.queryInterfaceTo(aProgress.asOutParam());
2548
2549 return rc;
2550}
2551
2552HRESULT Medium::cloneToBase(const ComPtr<IMedium> &aTarget,
2553 const std::vector<MediumVariant_T> &aVariant,
2554 ComPtr<IProgress> &aProgress)
2555{
2556 int rc = S_OK;
2557
2558 rc = cloneTo(aTarget, aVariant, NULL, aProgress);
2559 return rc;
2560}
2561
2562HRESULT Medium::cloneTo(const ComPtr<IMedium> &aTarget,
2563 const std::vector<MediumVariant_T> &aVariant,
2564 const ComPtr<IMedium> &aParent,
2565 ComPtr<IProgress> &aProgress)
2566{
2567 ComAssertRet(aTarget != this, E_INVALIDARG);
2568
2569 IMedium *aT = aTarget;
2570 ComObjPtr<Medium> pTarget = static_cast<Medium*>(aT);
2571 ComObjPtr<Medium> pParent;
2572 if (aParent)
2573 {
2574 IMedium *aP = aParent;
2575 pParent = static_cast<Medium*>(aP);
2576 }
2577
2578 HRESULT rc = S_OK;
2579 ComObjPtr<Progress> pProgress;
2580 Medium::Task *pTask = NULL;
2581
2582 try
2583 {
2584 // locking: we need the tree lock first because we access parent pointers
2585 // and we need to write-lock the media involved
2586 uint32_t cHandles = 3;
2587 LockHandle* pHandles[4] = { &m->pVirtualBox->i_getMediaTreeLockHandle(),
2588 this->lockHandle(),
2589 pTarget->lockHandle() };
2590 /* Only add parent to the lock if it is not null */
2591 if (!pParent.isNull())
2592 pHandles[cHandles++] = pParent->lockHandle();
2593 AutoWriteLock alock(cHandles,
2594 pHandles
2595 COMMA_LOCKVAL_SRC_POS);
2596
2597 if ( pTarget->m->state != MediumState_NotCreated
2598 && pTarget->m->state != MediumState_Created)
2599 throw pTarget->i_setStateError();
2600
2601 /* Build the source lock list. */
2602 MediumLockList *pSourceMediumLockList(new MediumLockList());
2603 alock.release();
2604 rc = i_createMediumLockList(true /* fFailIfInaccessible */,
2605 false /* fMediumLockWrite */,
2606 NULL,
2607 *pSourceMediumLockList);
2608 alock.acquire();
2609 if (FAILED(rc))
2610 {
2611 delete pSourceMediumLockList;
2612 throw rc;
2613 }
2614
2615 /* Build the target lock list (including the to-be parent chain). */
2616 MediumLockList *pTargetMediumLockList(new MediumLockList());
2617 alock.release();
2618 rc = pTarget->i_createMediumLockList(true /* fFailIfInaccessible */,
2619 true /* fMediumLockWrite */,
2620 pParent,
2621 *pTargetMediumLockList);
2622 alock.acquire();
2623 if (FAILED(rc))
2624 {
2625 delete pSourceMediumLockList;
2626 delete pTargetMediumLockList;
2627 throw rc;
2628 }
2629
2630 alock.release();
2631 rc = pSourceMediumLockList->Lock();
2632 alock.acquire();
2633 if (FAILED(rc))
2634 {
2635 delete pSourceMediumLockList;
2636 delete pTargetMediumLockList;
2637 throw setError(rc,
2638 tr("Failed to lock source media '%s'"),
2639 i_getLocationFull().c_str());
2640 }
2641 alock.release();
2642 rc = pTargetMediumLockList->Lock();
2643 alock.acquire();
2644 if (FAILED(rc))
2645 {
2646 delete pSourceMediumLockList;
2647 delete pTargetMediumLockList;
2648 throw setError(rc,
2649 tr("Failed to lock target media '%s'"),
2650 pTarget->i_getLocationFull().c_str());
2651 }
2652
2653 pProgress.createObject();
2654 rc = pProgress->init(m->pVirtualBox,
2655 static_cast <IMedium *>(this),
2656 BstrFmt(tr("Creating clone medium '%s'"), pTarget->m->strLocationFull.c_str()).raw(),
2657 TRUE /* aCancelable */);
2658 if (FAILED(rc))
2659 {
2660 delete pSourceMediumLockList;
2661 delete pTargetMediumLockList;
2662 throw rc;
2663 }
2664
2665 ULONG mediumVariantFlags = 0;
2666
2667 if (aVariant.size())
2668 {
2669 com::SafeArray<MediumVariant_T> variants(aVariant);
2670 for (size_t i = 0; i < variants.size(); i++)
2671 mediumVariantFlags |= variants[i];
2672 }
2673
2674 /* setup task object to carry out the operation asynchronously */
2675 pTask = new Medium::CloneTask(this, pProgress, pTarget,
2676 (MediumVariant_T)mediumVariantFlags,
2677 pParent, UINT32_MAX, UINT32_MAX,
2678 pSourceMediumLockList, pTargetMediumLockList);
2679 rc = pTask->rc();
2680 AssertComRC(rc);
2681 if (FAILED(rc))
2682 throw rc;
2683
2684 if (pTarget->m->state == MediumState_NotCreated)
2685 pTarget->m->state = MediumState_Creating;
2686 }
2687 catch (HRESULT aRC) { rc = aRC; }
2688
2689 if (SUCCEEDED(rc))
2690 {
2691 rc = i_startThread(pTask);
2692
2693 if (SUCCEEDED(rc))
2694 pProgress.queryInterfaceTo(aProgress.asOutParam());
2695 }
2696 else if (pTask != NULL)
2697 delete pTask;
2698
2699 return rc;
2700}
2701
2702HRESULT Medium::setLocation(const com::Utf8Str &aLocation, ComPtr<IProgress> &aProgress)
2703{
2704 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2705 NOREF(aLocation);
2706 NOREF(aProgress);
2707
2708 /// @todo NEWMEDIA for file names, add the default extension if no extension
2709 /// is present (using the information from the VD backend which also implies
2710 /// that one more parameter should be passed to setLocation() requesting
2711 /// that functionality since it is only allowed when called from this method
2712
2713 /// @todo NEWMEDIA rename the file and set m->location on success, then save
2714 /// the global registry (and local registries of portable VMs referring to
2715 /// this medium), this will also require to add the mRegistered flag to data
2716
2717 ReturnComNotImplemented();
2718}
2719
2720HRESULT Medium::compact(ComPtr<IProgress> &aProgress)
2721{
2722 HRESULT rc = S_OK;
2723 ComObjPtr<Progress> pProgress;
2724 Medium::Task *pTask = NULL;
2725
2726 try
2727 {
2728 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2729
2730 /* Build the medium lock list. */
2731 MediumLockList *pMediumLockList(new MediumLockList());
2732 alock.release();
2733 rc = i_createMediumLockList(true /* fFailIfInaccessible */ ,
2734 true /* fMediumLockWrite */,
2735 NULL,
2736 *pMediumLockList);
2737 alock.acquire();
2738 if (FAILED(rc))
2739 {
2740 delete pMediumLockList;
2741 throw rc;
2742 }
2743
2744 alock.release();
2745 rc = pMediumLockList->Lock();
2746 alock.acquire();
2747 if (FAILED(rc))
2748 {
2749 delete pMediumLockList;
2750 throw setError(rc,
2751 tr("Failed to lock media when compacting '%s'"),
2752 i_getLocationFull().c_str());
2753 }
2754
2755 pProgress.createObject();
2756 rc = pProgress->init(m->pVirtualBox,
2757 static_cast <IMedium *>(this),
2758 BstrFmt(tr("Compacting medium '%s'"), m->strLocationFull.c_str()).raw(),
2759 TRUE /* aCancelable */);
2760 if (FAILED(rc))
2761 {
2762 delete pMediumLockList;
2763 throw rc;
2764 }
2765
2766 /* setup task object to carry out the operation asynchronously */
2767 pTask = new Medium::CompactTask(this, pProgress, pMediumLockList);
2768 rc = pTask->rc();
2769 AssertComRC(rc);
2770 if (FAILED(rc))
2771 throw rc;
2772 }
2773 catch (HRESULT aRC) { rc = aRC; }
2774
2775 if (SUCCEEDED(rc))
2776 {
2777 rc = i_startThread(pTask);
2778
2779 if (SUCCEEDED(rc))
2780 pProgress.queryInterfaceTo(aProgress.asOutParam());
2781 }
2782 else if (pTask != NULL)
2783 delete pTask;
2784
2785 return rc;
2786}
2787
2788HRESULT Medium::resize(LONG64 aLogicalSize,
2789 ComPtr<IProgress> &aProgress)
2790{
2791 HRESULT rc = S_OK;
2792 ComObjPtr<Progress> pProgress;
2793 Medium::Task *pTask = NULL;
2794
2795 try
2796 {
2797 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2798
2799 /* Build the medium lock list. */
2800 MediumLockList *pMediumLockList(new MediumLockList());
2801 alock.release();
2802 rc = i_createMediumLockList(true /* fFailIfInaccessible */ ,
2803 true /* fMediumLockWrite */,
2804 NULL,
2805 *pMediumLockList);
2806 alock.acquire();
2807 if (FAILED(rc))
2808 {
2809 delete pMediumLockList;
2810 throw rc;
2811 }
2812
2813 alock.release();
2814 rc = pMediumLockList->Lock();
2815 alock.acquire();
2816 if (FAILED(rc))
2817 {
2818 delete pMediumLockList;
2819 throw setError(rc,
2820 tr("Failed to lock media when compacting '%s'"),
2821 i_getLocationFull().c_str());
2822 }
2823
2824 pProgress.createObject();
2825 rc = pProgress->init(m->pVirtualBox,
2826 static_cast <IMedium *>(this),
2827 BstrFmt(tr("Compacting medium '%s'"), m->strLocationFull.c_str()).raw(),
2828 TRUE /* aCancelable */);
2829 if (FAILED(rc))
2830 {
2831 delete pMediumLockList;
2832 throw rc;
2833 }
2834
2835 /* setup task object to carry out the operation asynchronously */
2836 pTask = new Medium::ResizeTask(this, aLogicalSize, pProgress, pMediumLockList);
2837 rc = pTask->rc();
2838 AssertComRC(rc);
2839 if (FAILED(rc))
2840 throw rc;
2841 }
2842 catch (HRESULT aRC) { rc = aRC; }
2843
2844 if (SUCCEEDED(rc))
2845 {
2846 rc = i_startThread(pTask);
2847
2848 if (SUCCEEDED(rc))
2849 pProgress.queryInterfaceTo(aProgress.asOutParam());
2850 }
2851 else if (pTask != NULL)
2852 delete pTask;
2853
2854 return rc;
2855}
2856
2857HRESULT Medium::reset(ComPtr<IProgress> &aProgress)
2858{
2859 HRESULT rc = S_OK;
2860 ComObjPtr<Progress> pProgress;
2861 Medium::Task *pTask = NULL;
2862
2863 try
2864 {
2865 /* canClose() needs the tree lock */
2866 AutoMultiWriteLock2 multilock(&m->pVirtualBox->i_getMediaTreeLockHandle(),
2867 this->lockHandle()
2868 COMMA_LOCKVAL_SRC_POS);
2869
2870 LogFlowThisFunc(("ENTER for medium %s\n", m->strLocationFull.c_str()));
2871
2872 if (m->pParent.isNull())
2873 throw setError(VBOX_E_NOT_SUPPORTED,
2874 tr("Medium type of '%s' is not differencing"),
2875 m->strLocationFull.c_str());
2876
2877 rc = i_canClose();
2878 if (FAILED(rc))
2879 throw rc;
2880
2881 /* Build the medium lock list. */
2882 MediumLockList *pMediumLockList(new MediumLockList());
2883 multilock.release();
2884 rc = i_createMediumLockList(true /* fFailIfInaccessible */,
2885 true /* fMediumLockWrite */,
2886 NULL,
2887 *pMediumLockList);
2888 multilock.acquire();
2889 if (FAILED(rc))
2890 {
2891 delete pMediumLockList;
2892 throw rc;
2893 }
2894
2895 multilock.release();
2896 rc = pMediumLockList->Lock();
2897 multilock.acquire();
2898 if (FAILED(rc))
2899 {
2900 delete pMediumLockList;
2901 throw setError(rc,
2902 tr("Failed to lock media when resetting '%s'"),
2903 i_getLocationFull().c_str());
2904 }
2905
2906 pProgress.createObject();
2907 rc = pProgress->init(m->pVirtualBox,
2908 static_cast<IMedium*>(this),
2909 BstrFmt(tr("Resetting differencing medium '%s'"), m->strLocationFull.c_str()).raw(),
2910 FALSE /* aCancelable */);
2911 if (FAILED(rc))
2912 throw rc;
2913
2914 /* setup task object to carry out the operation asynchronously */
2915 pTask = new Medium::ResetTask(this, pProgress, pMediumLockList);
2916 rc = pTask->rc();
2917 AssertComRC(rc);
2918 if (FAILED(rc))
2919 throw rc;
2920 }
2921 catch (HRESULT aRC) { rc = aRC; }
2922
2923 if (SUCCEEDED(rc))
2924 {
2925 rc = i_startThread(pTask);
2926
2927 if (SUCCEEDED(rc))
2928 pProgress.queryInterfaceTo(aProgress.asOutParam());
2929 }
2930 else if (pTask != NULL)
2931 delete pTask;
2932
2933 LogFlowThisFunc(("LEAVE, rc=%Rhrc\n", rc));
2934
2935 return rc;
2936}
2937
2938////////////////////////////////////////////////////////////////////////////////
2939//
2940// Medium public internal methods
2941//
2942////////////////////////////////////////////////////////////////////////////////
2943
2944/**
2945 * Internal method to return the medium's parent medium. Must have caller + locking!
2946 * @return
2947 */
2948const ComObjPtr<Medium>& Medium::i_getParent() const
2949{
2950 return m->pParent;
2951}
2952
2953/**
2954 * Internal method to return the medium's list of child media. Must have caller + locking!
2955 * @return
2956 */
2957const MediaList& Medium::i_getChildren() const
2958{
2959 return m->llChildren;
2960}
2961
2962/**
2963 * Internal method to return the medium's GUID. Must have caller + locking!
2964 * @return
2965 */
2966const Guid& Medium::i_getId() const
2967{
2968 return m->id;
2969}
2970
2971/**
2972 * Internal method to return the medium's state. Must have caller + locking!
2973 * @return
2974 */
2975MediumState_T Medium::i_getState() const
2976{
2977 return m->state;
2978}
2979
2980/**
2981 * Internal method to return the medium's variant. Must have caller + locking!
2982 * @return
2983 */
2984MediumVariant_T Medium::i_getVariant() const
2985{
2986 return m->variant;
2987}
2988
2989/**
2990 * Internal method which returns true if this medium represents a host drive.
2991 * @return
2992 */
2993bool Medium::i_isHostDrive() const
2994{
2995 return m->hostDrive;
2996}
2997
2998/**
2999 * Internal method to return the medium's full location. Must have caller + locking!
3000 * @return
3001 */
3002const Utf8Str& Medium::i_getLocationFull() const
3003{
3004 return m->strLocationFull;
3005}
3006
3007/**
3008 * Internal method to return the medium's format string. Must have caller + locking!
3009 * @return
3010 */
3011const Utf8Str& Medium::i_getFormat() const
3012{
3013 return m->strFormat;
3014}
3015
3016/**
3017 * Internal method to return the medium's format object. Must have caller + locking!
3018 * @return
3019 */
3020const ComObjPtr<MediumFormat>& Medium::i_getMediumFormat() const
3021{
3022 return m->formatObj;
3023}
3024
3025/**
3026 * Internal method that returns true if the medium is represented by a file on the host disk
3027 * (and not iSCSI or something).
3028 * @return
3029 */
3030bool Medium::i_isMediumFormatFile() const
3031{
3032 if ( m->formatObj
3033 && (m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File)
3034 )
3035 return true;
3036 return false;
3037}
3038
3039/**
3040 * Internal method to return the medium's size. Must have caller + locking!
3041 * @return
3042 */
3043uint64_t Medium::i_getSize() const
3044{
3045 return m->size;
3046}
3047
3048/**
3049 * Returns the medium device type. Must have caller + locking!
3050 * @return
3051 */
3052DeviceType_T Medium::i_getDeviceType() const
3053{
3054 return m->devType;
3055}
3056
3057/**
3058 * Returns the medium type. Must have caller + locking!
3059 * @return
3060 */
3061MediumType_T Medium::i_getType() const
3062{
3063 return m->type;
3064}
3065
3066/**
3067 * Returns a short version of the location attribute.
3068 *
3069 * @note Must be called from under this object's read or write lock.
3070 */
3071Utf8Str Medium::i_getName()
3072{
3073 Utf8Str name = RTPathFilename(m->strLocationFull.c_str());
3074 return name;
3075}
3076
3077/**
3078 * This adds the given UUID to the list of media registries in which this
3079 * medium should be registered. The UUID can either be a machine UUID,
3080 * to add a machine registry, or the global registry UUID as returned by
3081 * VirtualBox::getGlobalRegistryId().
3082 *
3083 * Note that for hard disks, this method does nothing if the medium is
3084 * already in another registry to avoid having hard disks in more than
3085 * one registry, which causes trouble with keeping diff images in sync.
3086 * See getFirstRegistryMachineId() for details.
3087 *
3088 * If fRecurse == true, then the media tree lock must be held for reading.
3089 *
3090 * @param id
3091 * @param fRecurse If true, recurses into child media to make sure the whole tree has registries in sync.
3092 * @return true if the registry was added; false if the given id was already on the list.
3093 */
3094bool Medium::i_addRegistry(const Guid& id, bool fRecurse)
3095{
3096 AutoCaller autoCaller(this);
3097 if (FAILED(autoCaller.rc()))
3098 return false;
3099 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3100
3101 bool fAdd = true;
3102
3103 // hard disks cannot be in more than one registry
3104 if ( m->devType == DeviceType_HardDisk
3105 && m->llRegistryIDs.size() > 0)
3106 fAdd = false;
3107
3108 // no need to add the UUID twice
3109 if (fAdd)
3110 {
3111 for (GuidList::const_iterator it = m->llRegistryIDs.begin();
3112 it != m->llRegistryIDs.end();
3113 ++it)
3114 {
3115 if ((*it) == id)
3116 {
3117 fAdd = false;
3118 break;
3119 }
3120 }
3121 }
3122
3123 if (fAdd)
3124 m->llRegistryIDs.push_back(id);
3125
3126 if (fRecurse)
3127 {
3128 // Get private list of children and release medium lock straight away.
3129 MediaList llChildren(m->llChildren);
3130 alock.release();
3131
3132 for (MediaList::iterator it = llChildren.begin();
3133 it != llChildren.end();
3134 ++it)
3135 {
3136 Medium *pChild = *it;
3137 fAdd |= pChild->i_addRegistry(id, true);
3138 }
3139 }
3140
3141 return fAdd;
3142}
3143
3144/**
3145 * Removes the given UUID from the list of media registry UUIDs. Returns true
3146 * if found or false if not.
3147 *
3148 * If fRecurse == true, then the media tree lock must be held for reading.
3149 *
3150 * @param id
3151 * @param fRecurse If true, recurses into child media to make sure the whole tree has registries in sync.
3152 * @return
3153 */
3154bool Medium::i_removeRegistry(const Guid& id, bool fRecurse)
3155{
3156 AutoCaller autoCaller(this);
3157 if (FAILED(autoCaller.rc()))
3158 return false;
3159 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3160
3161 bool fRemove = false;
3162
3163 for (GuidList::iterator it = m->llRegistryIDs.begin();
3164 it != m->llRegistryIDs.end();
3165 ++it)
3166 {
3167 if ((*it) == id)
3168 {
3169 m->llRegistryIDs.erase(it);
3170 fRemove = true;
3171 break;
3172 }
3173 }
3174
3175 if (fRecurse)
3176 {
3177 // Get private list of children and release medium lock straight away.
3178 MediaList llChildren(m->llChildren);
3179 alock.release();
3180
3181 for (MediaList::iterator it = llChildren.begin();
3182 it != llChildren.end();
3183 ++it)
3184 {
3185 Medium *pChild = *it;
3186 fRemove |= pChild->i_removeRegistry(id, true);
3187 }
3188 }
3189
3190 return fRemove;
3191}
3192
3193/**
3194 * Returns true if id is in the list of media registries for this medium.
3195 *
3196 * Must have caller + read locking!
3197 *
3198 * @param id
3199 * @return
3200 */
3201bool Medium::i_isInRegistry(const Guid& id)
3202{
3203 for (GuidList::const_iterator it = m->llRegistryIDs.begin();
3204 it != m->llRegistryIDs.end();
3205 ++it)
3206 {
3207 if (*it == id)
3208 return true;
3209 }
3210
3211 return false;
3212}
3213
3214/**
3215 * Internal method to return the medium's first registry machine (i.e. the machine in whose
3216 * machine XML this medium is listed).
3217 *
3218 * Every attached medium must now (4.0) reside in at least one media registry, which is identified
3219 * by a UUID. This is either a machine UUID if the machine is from 4.0 or newer, in which case
3220 * machines have their own media registries, or it is the pseudo-UUID of the VirtualBox
3221 * object if the machine is old and still needs the global registry in VirtualBox.xml.
3222 *
3223 * By definition, hard disks may only be in one media registry, in which all its children
3224 * will be stored as well. Otherwise we run into problems with having keep multiple registries
3225 * in sync. (This is the "cloned VM" case in which VM1 may link to the disks of VM2; in this
3226 * case, only VM2's registry is used for the disk in question.)
3227 *
3228 * If there is no medium registry, particularly if the medium has not been attached yet, this
3229 * does not modify uuid and returns false.
3230 *
3231 * ISOs and RAWs, by contrast, can be in more than one repository to make things easier for
3232 * the user.
3233 *
3234 * Must have caller + locking!
3235 *
3236 * @param uuid Receives first registry machine UUID, if available.
3237 * @return true if uuid was set.
3238 */
3239bool Medium::i_getFirstRegistryMachineId(Guid &uuid) const
3240{
3241 if (m->llRegistryIDs.size())
3242 {
3243 uuid = m->llRegistryIDs.front();
3244 return true;
3245 }
3246 return false;
3247}
3248
3249/**
3250 * Marks all the registries in which this medium is registered as modified.
3251 */
3252void Medium::i_markRegistriesModified()
3253{
3254 AutoCaller autoCaller(this);
3255 if (FAILED(autoCaller.rc())) return;
3256
3257 // Get local copy, as keeping the lock over VirtualBox::markRegistryModified
3258 // causes trouble with the lock order
3259 GuidList llRegistryIDs;
3260 {
3261 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3262 llRegistryIDs = m->llRegistryIDs;
3263 }
3264
3265 autoCaller.release();
3266
3267 /* Save the error information now, the implicit restore when this goes
3268 * out of scope will throw away spurious additional errors created below. */
3269 ErrorInfoKeeper eik;
3270 for (GuidList::const_iterator it = llRegistryIDs.begin();
3271 it != llRegistryIDs.end();
3272 ++it)
3273 {
3274 m->pVirtualBox->i_markRegistryModified(*it);
3275 }
3276}
3277
3278/**
3279 * Adds the given machine and optionally the snapshot to the list of the objects
3280 * this medium is attached to.
3281 *
3282 * @param aMachineId Machine ID.
3283 * @param aSnapshotId Snapshot ID; when non-empty, adds a snapshot attachment.
3284 */
3285HRESULT Medium::i_addBackReference(const Guid &aMachineId,
3286 const Guid &aSnapshotId /*= Guid::Empty*/)
3287{
3288 AssertReturn(aMachineId.isValid(), E_FAIL);
3289
3290 LogFlowThisFunc(("ENTER, aMachineId: {%RTuuid}, aSnapshotId: {%RTuuid}\n", aMachineId.raw(), aSnapshotId.raw()));
3291
3292 AutoCaller autoCaller(this);
3293 AssertComRCReturnRC(autoCaller.rc());
3294
3295 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3296
3297 switch (m->state)
3298 {
3299 case MediumState_Created:
3300 case MediumState_Inaccessible:
3301 case MediumState_LockedRead:
3302 case MediumState_LockedWrite:
3303 break;
3304
3305 default:
3306 return i_setStateError();
3307 }
3308
3309 if (m->numCreateDiffTasks > 0)
3310 return setError(VBOX_E_OBJECT_IN_USE,
3311 tr("Cannot attach medium '%s' {%RTuuid}: %u differencing child media are being created"),
3312 m->strLocationFull.c_str(),
3313 m->id.raw(),
3314 m->numCreateDiffTasks);
3315
3316 BackRefList::iterator it = std::find_if(m->backRefs.begin(),
3317 m->backRefs.end(),
3318 BackRef::EqualsTo(aMachineId));
3319 if (it == m->backRefs.end())
3320 {
3321 BackRef ref(aMachineId, aSnapshotId);
3322 m->backRefs.push_back(ref);
3323
3324 return S_OK;
3325 }
3326
3327 // if the caller has not supplied a snapshot ID, then we're attaching
3328 // to a machine a medium which represents the machine's current state,
3329 // so set the flag
3330
3331 if (aSnapshotId.isZero())
3332 {
3333 /* sanity: no duplicate attachments */
3334 if (it->fInCurState)
3335 return setError(VBOX_E_OBJECT_IN_USE,
3336 tr("Cannot attach medium '%s' {%RTuuid}: medium is already associated with the current state of machine uuid {%RTuuid}!"),
3337 m->strLocationFull.c_str(),
3338 m->id.raw(),
3339 aMachineId.raw());
3340 it->fInCurState = true;
3341
3342 return S_OK;
3343 }
3344
3345 // otherwise: a snapshot medium is being attached
3346
3347 /* sanity: no duplicate attachments */
3348 for (GuidList::const_iterator jt = it->llSnapshotIds.begin();
3349 jt != it->llSnapshotIds.end();
3350 ++jt)
3351 {
3352 const Guid &idOldSnapshot = *jt;
3353
3354 if (idOldSnapshot == aSnapshotId)
3355 {
3356#ifdef DEBUG
3357 i_dumpBackRefs();
3358#endif
3359 return setError(VBOX_E_OBJECT_IN_USE,
3360 tr("Cannot attach medium '%s' {%RTuuid} from snapshot '%RTuuid': medium is already in use by this snapshot!"),
3361 m->strLocationFull.c_str(),
3362 m->id.raw(),
3363 aSnapshotId.raw());
3364 }
3365 }
3366
3367 it->llSnapshotIds.push_back(aSnapshotId);
3368 // Do not touch fInCurState, as the image may be attached to the current
3369 // state *and* a snapshot, otherwise we lose the current state association!
3370
3371 LogFlowThisFuncLeave();
3372
3373 return S_OK;
3374}
3375
3376/**
3377 * Removes the given machine and optionally the snapshot from the list of the
3378 * objects this medium is attached to.
3379 *
3380 * @param aMachineId Machine ID.
3381 * @param aSnapshotId Snapshot ID; when non-empty, removes the snapshot
3382 * attachment.
3383 */
3384HRESULT Medium::i_removeBackReference(const Guid &aMachineId,
3385 const Guid &aSnapshotId /*= Guid::Empty*/)
3386{
3387 AssertReturn(aMachineId.isValid(), E_FAIL);
3388
3389 AutoCaller autoCaller(this);
3390 AssertComRCReturnRC(autoCaller.rc());
3391
3392 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3393
3394 BackRefList::iterator it =
3395 std::find_if(m->backRefs.begin(), m->backRefs.end(),
3396 BackRef::EqualsTo(aMachineId));
3397 AssertReturn(it != m->backRefs.end(), E_FAIL);
3398
3399 if (aSnapshotId.isZero())
3400 {
3401 /* remove the current state attachment */
3402 it->fInCurState = false;
3403 }
3404 else
3405 {
3406 /* remove the snapshot attachment */
3407 GuidList::iterator jt = std::find(it->llSnapshotIds.begin(),
3408 it->llSnapshotIds.end(),
3409 aSnapshotId);
3410
3411 AssertReturn(jt != it->llSnapshotIds.end(), E_FAIL);
3412 it->llSnapshotIds.erase(jt);
3413 }
3414
3415 /* if the backref becomes empty, remove it */
3416 if (it->fInCurState == false && it->llSnapshotIds.size() == 0)
3417 m->backRefs.erase(it);
3418
3419 return S_OK;
3420}
3421
3422/**
3423 * Internal method to return the medium's list of backrefs. Must have caller + locking!
3424 * @return
3425 */
3426const Guid* Medium::i_getFirstMachineBackrefId() const
3427{
3428 if (!m->backRefs.size())
3429 return NULL;
3430
3431 return &m->backRefs.front().machineId;
3432}
3433
3434/**
3435 * Internal method which returns a machine that either this medium or one of its children
3436 * is attached to. This is used for finding a replacement media registry when an existing
3437 * media registry is about to be deleted in VirtualBox::unregisterMachine().
3438 *
3439 * Must have caller + locking, *and* caller must hold the media tree lock!
3440 * @return
3441 */
3442const Guid* Medium::i_getAnyMachineBackref() const
3443{
3444 if (m->backRefs.size())
3445 return &m->backRefs.front().machineId;
3446
3447 for (MediaList::iterator it = m->llChildren.begin();
3448 it != m->llChildren.end();
3449 ++it)
3450 {
3451 Medium *pChild = *it;
3452 // recurse for this child
3453 const Guid* puuid;
3454 if ((puuid = pChild->i_getAnyMachineBackref()))
3455 return puuid;
3456 }
3457
3458 return NULL;
3459}
3460
3461const Guid* Medium::i_getFirstMachineBackrefSnapshotId() const
3462{
3463 if (!m->backRefs.size())
3464 return NULL;
3465
3466 const BackRef &ref = m->backRefs.front();
3467 if (!ref.llSnapshotIds.size())
3468 return NULL;
3469
3470 return &ref.llSnapshotIds.front();
3471}
3472
3473size_t Medium::i_getMachineBackRefCount() const
3474{
3475 return m->backRefs.size();
3476}
3477
3478#ifdef DEBUG
3479/**
3480 * Debugging helper that gets called after VirtualBox initialization that writes all
3481 * machine backreferences to the debug log.
3482 */
3483void Medium::i_dumpBackRefs()
3484{
3485 AutoCaller autoCaller(this);
3486 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3487
3488 LogFlowThisFunc(("Dumping backrefs for medium '%s':\n", m->strLocationFull.c_str()));
3489
3490 for (BackRefList::iterator it2 = m->backRefs.begin();
3491 it2 != m->backRefs.end();
3492 ++it2)
3493 {
3494 const BackRef &ref = *it2;
3495 LogFlowThisFunc((" Backref from machine {%RTuuid} (fInCurState: %d)\n", ref.machineId.raw(), ref.fInCurState));
3496
3497 for (GuidList::const_iterator jt2 = it2->llSnapshotIds.begin();
3498 jt2 != it2->llSnapshotIds.end();
3499 ++jt2)
3500 {
3501 const Guid &id = *jt2;
3502 LogFlowThisFunc((" Backref from snapshot {%RTuuid}\n", id.raw()));
3503 }
3504 }
3505}
3506#endif
3507
3508/**
3509 * Checks if the given change of \a aOldPath to \a aNewPath affects the location
3510 * of this media and updates it if necessary to reflect the new location.
3511 *
3512 * @param aOldPath Old path (full).
3513 * @param aNewPath New path (full).
3514 *
3515 * @note Locks this object for writing.
3516 */
3517HRESULT Medium::i_updatePath(const Utf8Str &strOldPath, const Utf8Str &strNewPath)
3518{
3519 AssertReturn(!strOldPath.isEmpty(), E_FAIL);
3520 AssertReturn(!strNewPath.isEmpty(), E_FAIL);
3521
3522 AutoCaller autoCaller(this);
3523 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3524
3525 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3526
3527 LogFlowThisFunc(("locationFull.before='%s'\n", m->strLocationFull.c_str()));
3528
3529 const char *pcszMediumPath = m->strLocationFull.c_str();
3530
3531 if (RTPathStartsWith(pcszMediumPath, strOldPath.c_str()))
3532 {
3533 Utf8Str newPath(strNewPath);
3534 newPath.append(pcszMediumPath + strOldPath.length());
3535 unconst(m->strLocationFull) = newPath;
3536
3537 LogFlowThisFunc(("locationFull.after='%s'\n", m->strLocationFull.c_str()));
3538 // we changed something
3539 return S_OK;
3540 }
3541
3542 // no change was necessary, signal error which the caller needs to interpret
3543 return VBOX_E_FILE_ERROR;
3544}
3545
3546/**
3547 * Returns the base medium of the media chain this medium is part of.
3548 *
3549 * The base medium is found by walking up the parent-child relationship axis.
3550 * If the medium doesn't have a parent (i.e. it's a base medium), it
3551 * returns itself in response to this method.
3552 *
3553 * @param aLevel Where to store the number of ancestors of this medium
3554 * (zero for the base), may be @c NULL.
3555 *
3556 * @note Locks medium tree for reading.
3557 */
3558ComObjPtr<Medium> Medium::i_getBase(uint32_t *aLevel /*= NULL*/)
3559{
3560 ComObjPtr<Medium> pBase;
3561
3562 /* it is possible that some previous/concurrent uninit has already cleared
3563 * the pVirtualBox reference, and in this case we don't need to continue */
3564 ComObjPtr<VirtualBox> pVirtualBox(m->pVirtualBox);
3565 if (!pVirtualBox)
3566 return pBase;
3567
3568 /* we access mParent */
3569 AutoReadLock treeLock(pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3570
3571 AutoCaller autoCaller(this);
3572 AssertReturn(autoCaller.isOk(), pBase);
3573
3574 pBase = this;
3575 uint32_t level = 0;
3576
3577 if (m->pParent)
3578 {
3579 for (;;)
3580 {
3581 AutoCaller baseCaller(pBase);
3582 AssertReturn(baseCaller.isOk(), pBase);
3583
3584 if (pBase->m->pParent.isNull())
3585 break;
3586
3587 pBase = pBase->m->pParent;
3588 ++level;
3589 }
3590 }
3591
3592 if (aLevel != NULL)
3593 *aLevel = level;
3594
3595 return pBase;
3596}
3597
3598/**
3599 * Returns @c true if this medium cannot be modified because it has
3600 * dependents (children) or is part of the snapshot. Related to the medium
3601 * type and posterity, not to the current media state.
3602 *
3603 * @note Locks this object and medium tree for reading.
3604 */
3605bool Medium::i_isReadOnly()
3606{
3607 AutoCaller autoCaller(this);
3608 AssertComRCReturn(autoCaller.rc(), false);
3609
3610 /* we access children */
3611 AutoReadLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3612
3613 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3614
3615 switch (m->type)
3616 {
3617 case MediumType_Normal:
3618 {
3619 if (i_getChildren().size() != 0)
3620 return true;
3621
3622 for (BackRefList::const_iterator it = m->backRefs.begin();
3623 it != m->backRefs.end(); ++it)
3624 if (it->llSnapshotIds.size() != 0)
3625 return true;
3626
3627 if (m->variant & MediumVariant_VmdkStreamOptimized)
3628 return true;
3629
3630 return false;
3631 }
3632 case MediumType_Immutable:
3633 case MediumType_MultiAttach:
3634 return true;
3635 case MediumType_Writethrough:
3636 case MediumType_Shareable:
3637 case MediumType_Readonly: /* explicit readonly media has no diffs */
3638 return false;
3639 default:
3640 break;
3641 }
3642
3643 AssertFailedReturn(false);
3644}
3645
3646/**
3647 * Internal method to return the medium's size. Must have caller + locking!
3648 * @return
3649 */
3650void Medium::i_updateId(const Guid &id)
3651{
3652 unconst(m->id) = id;
3653}
3654
3655/**
3656 * Saves medium data by appending a new child node to the given
3657 * parent XML settings node.
3658 *
3659 * @param data Settings struct to be updated.
3660 * @param strHardDiskFolder Folder for which paths should be relative.
3661 *
3662 * @note Locks this object, medium tree and children for reading.
3663 */
3664HRESULT Medium::i_saveSettings(settings::Medium &data,
3665 const Utf8Str &strHardDiskFolder)
3666{
3667 AutoCaller autoCaller(this);
3668 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3669
3670 /* we access mParent */
3671 AutoReadLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3672
3673 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3674
3675 data.uuid = m->id;
3676
3677 // make path relative if needed
3678 if ( !strHardDiskFolder.isEmpty()
3679 && RTPathStartsWith(m->strLocationFull.c_str(), strHardDiskFolder.c_str())
3680 )
3681 data.strLocation = m->strLocationFull.substr(strHardDiskFolder.length() + 1);
3682 else
3683 data.strLocation = m->strLocationFull;
3684 data.strFormat = m->strFormat;
3685
3686 /* optional, only for diffs, default is false */
3687 if (m->pParent)
3688 data.fAutoReset = m->autoReset;
3689 else
3690 data.fAutoReset = false;
3691
3692 /* optional */
3693 data.strDescription = m->strDescription;
3694
3695 /* optional properties */
3696 data.properties.clear();
3697
3698 /* handle iSCSI initiator secrets transparently */
3699 bool fHaveInitiatorSecretEncrypted = false;
3700 Utf8Str strCiphertext;
3701 settings::StringsMap::const_iterator itPln = m->mapProperties.find("InitiatorSecret");
3702 if ( itPln != m->mapProperties.end()
3703 && !itPln->second.isEmpty())
3704 {
3705 /* Encrypt the plain secret. If that does not work (i.e. no or wrong settings key
3706 * specified), just use the encrypted secret (if there is any). */
3707 int rc = m->pVirtualBox->i_encryptSetting(itPln->second, &strCiphertext);
3708 if (RT_SUCCESS(rc))
3709 fHaveInitiatorSecretEncrypted = true;
3710 }
3711 for (settings::StringsMap::const_iterator it = m->mapProperties.begin();
3712 it != m->mapProperties.end();
3713 ++it)
3714 {
3715 /* only save properties that have non-default values */
3716 if (!it->second.isEmpty())
3717 {
3718 const Utf8Str &name = it->first;
3719 const Utf8Str &value = it->second;
3720 /* do NOT store the plain InitiatorSecret */
3721 if ( !fHaveInitiatorSecretEncrypted
3722 || !name.equals("InitiatorSecret"))
3723 data.properties[name] = value;
3724 }
3725 }
3726 if (fHaveInitiatorSecretEncrypted)
3727 data.properties["InitiatorSecretEncrypted"] = strCiphertext;
3728
3729 /* only for base media */
3730 if (m->pParent.isNull())
3731 data.hdType = m->type;
3732
3733 /* save all children */
3734 for (MediaList::const_iterator it = i_getChildren().begin();
3735 it != i_getChildren().end();
3736 ++it)
3737 {
3738 settings::Medium med;
3739 HRESULT rc = (*it)->i_saveSettings(med, strHardDiskFolder);
3740 AssertComRCReturnRC(rc);
3741 data.llChildren.push_back(med);
3742 }
3743
3744 return S_OK;
3745}
3746
3747/**
3748 * Constructs a medium lock list for this medium. The lock is not taken.
3749 *
3750 * @note Caller MUST NOT hold the media tree or medium lock.
3751 *
3752 * @param fFailIfInaccessible If true, this fails with an error if a medium is inaccessible. If false,
3753 * inaccessible media are silently skipped and not locked (i.e. their state remains "Inaccessible");
3754 * this is necessary for a VM's removable media VM startup for which we do not want to fail.
3755 * @param fMediumLockWrite Whether to associate a write lock with this medium.
3756 * @param pToBeParent Medium which will become the parent of this medium.
3757 * @param mediumLockList Where to store the resulting list.
3758 */
3759HRESULT Medium::i_createMediumLockList(bool fFailIfInaccessible,
3760 bool fMediumLockWrite,
3761 Medium *pToBeParent,
3762 MediumLockList &mediumLockList)
3763{
3764 Assert(!m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3765 Assert(!isWriteLockOnCurrentThread());
3766
3767 AutoCaller autoCaller(this);
3768 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3769
3770 HRESULT rc = S_OK;
3771
3772 /* paranoid sanity checking if the medium has a to-be parent medium */
3773 if (pToBeParent)
3774 {
3775 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3776 ComAssertRet(i_getParent().isNull(), E_FAIL);
3777 ComAssertRet(i_getChildren().size() == 0, E_FAIL);
3778 }
3779
3780 ErrorInfoKeeper eik;
3781 MultiResult mrc(S_OK);
3782
3783 ComObjPtr<Medium> pMedium = this;
3784 while (!pMedium.isNull())
3785 {
3786 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
3787
3788 /* Accessibility check must be first, otherwise locking interferes
3789 * with getting the medium state. Lock lists are not created for
3790 * fun, and thus getting the medium status is no luxury. */
3791 MediumState_T mediumState = pMedium->i_getState();
3792 if (mediumState == MediumState_Inaccessible)
3793 {
3794 alock.release();
3795 rc = pMedium->i_queryInfo(false /* fSetImageId */, false /* fSetParentId */,
3796 autoCaller);
3797 alock.acquire();
3798 if (FAILED(rc)) return rc;
3799
3800 mediumState = pMedium->i_getState();
3801 if (mediumState == MediumState_Inaccessible)
3802 {
3803 // ignore inaccessible ISO media and silently return S_OK,
3804 // otherwise VM startup (esp. restore) may fail without good reason
3805 if (!fFailIfInaccessible)
3806 return S_OK;
3807
3808 // otherwise report an error
3809 Bstr error;
3810 rc = pMedium->COMGETTER(LastAccessError)(error.asOutParam());
3811 if (FAILED(rc)) return rc;
3812
3813 /* collect multiple errors */
3814 eik.restore();
3815 Assert(!error.isEmpty());
3816 mrc = setError(E_FAIL,
3817 "%ls",
3818 error.raw());
3819 // error message will be something like
3820 // "Could not open the medium ... VD: error VERR_FILE_NOT_FOUND opening image file ... (VERR_FILE_NOT_FOUND).
3821 eik.fetch();
3822 }
3823 }
3824
3825 if (pMedium == this)
3826 mediumLockList.Prepend(pMedium, fMediumLockWrite);
3827 else
3828 mediumLockList.Prepend(pMedium, false);
3829
3830 pMedium = pMedium->i_getParent();
3831 if (pMedium.isNull() && pToBeParent)
3832 {
3833 pMedium = pToBeParent;
3834 pToBeParent = NULL;
3835 }
3836 }
3837
3838 return mrc;
3839}
3840
3841/**
3842 * Creates a new differencing storage unit using the format of the given target
3843 * medium and the location. Note that @c aTarget must be NotCreated.
3844 *
3845 * The @a aMediumLockList parameter contains the associated medium lock list,
3846 * which must be in locked state. If @a aWait is @c true then the caller is
3847 * responsible for unlocking.
3848 *
3849 * If @a aProgress is not NULL but the object it points to is @c null then a
3850 * new progress object will be created and assigned to @a *aProgress on
3851 * success, otherwise the existing progress object is used. If @a aProgress is
3852 * NULL, then no progress object is created/used at all.
3853 *
3854 * When @a aWait is @c false, this method will create a thread to perform the
3855 * create operation asynchronously and will return immediately. Otherwise, it
3856 * will perform the operation on the calling thread and will not return to the
3857 * caller until the operation is completed. Note that @a aProgress cannot be
3858 * NULL when @a aWait is @c false (this method will assert in this case).
3859 *
3860 * @param aTarget Target medium.
3861 * @param aVariant Precise medium variant to create.
3862 * @param aMediumLockList List of media which should be locked.
3863 * @param aProgress Where to find/store a Progress object to track
3864 * operation completion.
3865 * @param aWait @c true if this method should block instead of
3866 * creating an asynchronous thread.
3867 *
3868 * @note Locks this object and @a aTarget for writing.
3869 */
3870HRESULT Medium::i_createDiffStorage(ComObjPtr<Medium> &aTarget,
3871 MediumVariant_T aVariant,
3872 MediumLockList *aMediumLockList,
3873 ComObjPtr<Progress> *aProgress,
3874 bool aWait)
3875{
3876 AssertReturn(!aTarget.isNull(), E_FAIL);
3877 AssertReturn(aMediumLockList, E_FAIL);
3878 AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
3879
3880 AutoCaller autoCaller(this);
3881 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3882
3883 AutoCaller targetCaller(aTarget);
3884 if (FAILED(targetCaller.rc())) return targetCaller.rc();
3885
3886 HRESULT rc = S_OK;
3887 ComObjPtr<Progress> pProgress;
3888 Medium::Task *pTask = NULL;
3889
3890 try
3891 {
3892 AutoMultiWriteLock2 alock(this, aTarget COMMA_LOCKVAL_SRC_POS);
3893
3894 ComAssertThrow( m->type != MediumType_Writethrough
3895 && m->type != MediumType_Shareable
3896 && m->type != MediumType_Readonly, E_FAIL);
3897 ComAssertThrow(m->state == MediumState_LockedRead, E_FAIL);
3898
3899 if (aTarget->m->state != MediumState_NotCreated)
3900 throw aTarget->i_setStateError();
3901
3902 /* Check that the medium is not attached to the current state of
3903 * any VM referring to it. */
3904 for (BackRefList::const_iterator it = m->backRefs.begin();
3905 it != m->backRefs.end();
3906 ++it)
3907 {
3908 if (it->fInCurState)
3909 {
3910 /* Note: when a VM snapshot is being taken, all normal media
3911 * attached to the VM in the current state will be, as an
3912 * exception, also associated with the snapshot which is about
3913 * to create (see SnapshotMachine::init()) before deassociating
3914 * them from the current state (which takes place only on
3915 * success in Machine::fixupHardDisks()), so that the size of
3916 * snapshotIds will be 1 in this case. The extra condition is
3917 * used to filter out this legal situation. */
3918 if (it->llSnapshotIds.size() == 0)
3919 throw setError(VBOX_E_INVALID_OBJECT_STATE,
3920 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"),
3921 m->strLocationFull.c_str(), it->machineId.raw());
3922
3923 Assert(it->llSnapshotIds.size() == 1);
3924 }
3925 }
3926
3927 if (aProgress != NULL)
3928 {
3929 /* use the existing progress object... */
3930 pProgress = *aProgress;
3931
3932 /* ...but create a new one if it is null */
3933 if (pProgress.isNull())
3934 {
3935 pProgress.createObject();
3936 rc = pProgress->init(m->pVirtualBox,
3937 static_cast<IMedium*>(this),
3938 BstrFmt(tr("Creating differencing medium storage unit '%s'"),
3939 aTarget->m->strLocationFull.c_str()).raw(),
3940 TRUE /* aCancelable */);
3941 if (FAILED(rc))
3942 throw rc;
3943 }
3944 }
3945
3946 /* setup task object to carry out the operation sync/async */
3947 pTask = new Medium::CreateDiffTask(this, pProgress, aTarget, aVariant,
3948 aMediumLockList,
3949 aWait /* fKeepMediumLockList */);
3950 rc = pTask->rc();
3951 AssertComRC(rc);
3952 if (FAILED(rc))
3953 throw rc;
3954
3955 /* register a task (it will deregister itself when done) */
3956 ++m->numCreateDiffTasks;
3957 Assert(m->numCreateDiffTasks != 0); /* overflow? */
3958
3959 aTarget->m->state = MediumState_Creating;
3960 }
3961 catch (HRESULT aRC) { rc = aRC; }
3962
3963 if (SUCCEEDED(rc))
3964 {
3965 if (aWait)
3966 rc = i_runNow(pTask);
3967 else
3968 rc = i_startThread(pTask);
3969
3970 if (SUCCEEDED(rc) && aProgress != NULL)
3971 *aProgress = pProgress;
3972 }
3973 else if (pTask != NULL)
3974 delete pTask;
3975
3976 return rc;
3977}
3978
3979/**
3980 * Returns a preferred format for differencing media.
3981 */
3982Utf8Str Medium::i_getPreferredDiffFormat()
3983{
3984 AutoCaller autoCaller(this);
3985 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
3986
3987 /* check that our own format supports diffs */
3988 if (!(m->formatObj->i_getCapabilities() & MediumFormatCapabilities_Differencing))
3989 {
3990 /* use the default format if not */
3991 Utf8Str tmp;
3992 m->pVirtualBox->i_getDefaultHardDiskFormat(tmp);
3993 return tmp;
3994 }
3995
3996 /* m->strFormat is const, no need to lock */
3997 return m->strFormat;
3998}
3999
4000/**
4001 * Implementation for the public Medium::Close() with the exception of calling
4002 * VirtualBox::saveRegistries(), in case someone wants to call this for several
4003 * media.
4004 *
4005 * After this returns with success, uninit() has been called on the medium, and
4006 * the object is no longer usable ("not ready" state).
4007 *
4008 * @param autoCaller AutoCaller instance which must have been created on the caller's
4009 * stack for this medium. This gets released hereupon
4010 * which the Medium instance gets uninitialized.
4011 * @return
4012 */
4013HRESULT Medium::i_close(AutoCaller &autoCaller)
4014{
4015 // must temporarily drop the caller, need the tree lock first
4016 autoCaller.release();
4017
4018 // we're accessing parent/child and backrefs, so lock the tree first, then ourselves
4019 AutoMultiWriteLock2 multilock(&m->pVirtualBox->i_getMediaTreeLockHandle(),
4020 this->lockHandle()
4021 COMMA_LOCKVAL_SRC_POS);
4022
4023 autoCaller.add();
4024 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4025
4026 LogFlowFunc(("ENTER for %s\n", i_getLocationFull().c_str()));
4027
4028 bool wasCreated = true;
4029
4030 switch (m->state)
4031 {
4032 case MediumState_NotCreated:
4033 wasCreated = false;
4034 break;
4035 case MediumState_Created:
4036 case MediumState_Inaccessible:
4037 break;
4038 default:
4039 return i_setStateError();
4040 }
4041
4042 if (m->backRefs.size() != 0)
4043 return setError(VBOX_E_OBJECT_IN_USE,
4044 tr("Medium '%s' cannot be closed because it is still attached to %d virtual machines"),
4045 m->strLocationFull.c_str(), m->backRefs.size());
4046
4047 // perform extra media-dependent close checks
4048 HRESULT rc = i_canClose();
4049 if (FAILED(rc)) return rc;
4050
4051 if (wasCreated)
4052 {
4053 // remove from the list of known media before performing actual
4054 // uninitialization (to keep the media registry consistent on
4055 // failure to do so)
4056 rc = i_unregisterWithVirtualBox();
4057 if (FAILED(rc)) return rc;
4058
4059 multilock.release();
4060 // Release the AutoCaller now, as otherwise uninit() will simply hang.
4061 // Needs to be done before mark the registries as modified and saving
4062 // the registry, as otherwise there may be a deadlock with someone else
4063 // closing this object while we're in i_saveModifiedRegistries(), which
4064 // needs the media tree lock, which the other thread holds until after
4065 // uninit() below.
4066 autoCaller.release();
4067 i_markRegistriesModified();
4068 m->pVirtualBox->i_saveModifiedRegistries();
4069 }
4070 else
4071 {
4072 multilock.release();
4073 // release the AutoCaller, as otherwise uninit() will simply hang
4074 autoCaller.release();
4075 }
4076
4077 // Keep the locks held until after uninit, as otherwise the consistency
4078 // of the medium tree cannot be guaranteed.
4079 uninit();
4080
4081 LogFlowFuncLeave();
4082
4083 return rc;
4084}
4085
4086/**
4087 * Deletes the medium storage unit.
4088 *
4089 * If @a aProgress is not NULL but the object it points to is @c null then a new
4090 * progress object will be created and assigned to @a *aProgress on success,
4091 * otherwise the existing progress object is used. If Progress is NULL, then no
4092 * progress object is created/used at all.
4093 *
4094 * When @a aWait is @c false, this method will create a thread to perform the
4095 * delete operation asynchronously and will return immediately. Otherwise, it
4096 * will perform the operation on the calling thread and will not return to the
4097 * caller until the operation is completed. Note that @a aProgress cannot be
4098 * NULL when @a aWait is @c false (this method will assert in this case).
4099 *
4100 * @param aProgress Where to find/store a Progress object to track operation
4101 * completion.
4102 * @param aWait @c true if this method should block instead of creating
4103 * an asynchronous thread.
4104 *
4105 * @note Locks mVirtualBox and this object for writing. Locks medium tree for
4106 * writing.
4107 */
4108HRESULT Medium::i_deleteStorage(ComObjPtr<Progress> *aProgress,
4109 bool aWait)
4110{
4111 AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
4112
4113 AutoCaller autoCaller(this);
4114 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4115
4116 HRESULT rc = S_OK;
4117 ComObjPtr<Progress> pProgress;
4118 Medium::Task *pTask = NULL;
4119
4120 try
4121 {
4122 /* we're accessing the media tree, and canClose() needs it too */
4123 AutoMultiWriteLock2 multilock(&m->pVirtualBox->i_getMediaTreeLockHandle(),
4124 this->lockHandle()
4125 COMMA_LOCKVAL_SRC_POS);
4126 LogFlowThisFunc(("aWait=%RTbool locationFull=%s\n", aWait, i_getLocationFull().c_str() ));
4127
4128 if ( !(m->formatObj->i_getCapabilities() & ( MediumFormatCapabilities_CreateDynamic
4129 | MediumFormatCapabilities_CreateFixed)))
4130 throw setError(VBOX_E_NOT_SUPPORTED,
4131 tr("Medium format '%s' does not support storage deletion"),
4132 m->strFormat.c_str());
4133
4134 /* Note that we are fine with Inaccessible state too: a) for symmetry
4135 * with create calls and b) because it doesn't really harm to try, if
4136 * it is really inaccessible, the delete operation will fail anyway.
4137 * Accepting Inaccessible state is especially important because all
4138 * registered media are initially Inaccessible upon VBoxSVC startup
4139 * until COMGETTER(RefreshState) is called. Accept Deleting state
4140 * because some callers need to put the medium in this state early
4141 * to prevent races. */
4142 switch (m->state)
4143 {
4144 case MediumState_Created:
4145 case MediumState_Deleting:
4146 case MediumState_Inaccessible:
4147 break;
4148 default:
4149 throw i_setStateError();
4150 }
4151
4152 if (m->backRefs.size() != 0)
4153 {
4154 Utf8Str strMachines;
4155 for (BackRefList::const_iterator it = m->backRefs.begin();
4156 it != m->backRefs.end();
4157 ++it)
4158 {
4159 const BackRef &b = *it;
4160 if (strMachines.length())
4161 strMachines.append(", ");
4162 strMachines.append(b.machineId.toString().c_str());
4163 }
4164#ifdef DEBUG
4165 i_dumpBackRefs();
4166#endif
4167 throw setError(VBOX_E_OBJECT_IN_USE,
4168 tr("Cannot delete storage: medium '%s' is still attached to the following %d virtual machine(s): %s"),
4169 m->strLocationFull.c_str(),
4170 m->backRefs.size(),
4171 strMachines.c_str());
4172 }
4173
4174 rc = i_canClose();
4175 if (FAILED(rc))
4176 throw rc;
4177
4178 /* go to Deleting state, so that the medium is not actually locked */
4179 if (m->state != MediumState_Deleting)
4180 {
4181 rc = i_markForDeletion();
4182 if (FAILED(rc))
4183 throw rc;
4184 }
4185
4186 /* Build the medium lock list. */
4187 MediumLockList *pMediumLockList(new MediumLockList());
4188 multilock.release();
4189 rc = i_createMediumLockList(true /* fFailIfInaccessible */,
4190 true /* fMediumLockWrite */,
4191 NULL,
4192 *pMediumLockList);
4193 multilock.acquire();
4194 if (FAILED(rc))
4195 {
4196 delete pMediumLockList;
4197 throw rc;
4198 }
4199
4200 multilock.release();
4201 rc = pMediumLockList->Lock();
4202 multilock.acquire();
4203 if (FAILED(rc))
4204 {
4205 delete pMediumLockList;
4206 throw setError(rc,
4207 tr("Failed to lock media when deleting '%s'"),
4208 i_getLocationFull().c_str());
4209 }
4210
4211 /* try to remove from the list of known media before performing
4212 * actual deletion (we favor the consistency of the media registry
4213 * which would have been broken if unregisterWithVirtualBox() failed
4214 * after we successfully deleted the storage) */
4215 rc = i_unregisterWithVirtualBox();
4216 if (FAILED(rc))
4217 throw rc;
4218 // no longer need lock
4219 multilock.release();
4220 i_markRegistriesModified();
4221
4222 if (aProgress != NULL)
4223 {
4224 /* use the existing progress object... */
4225 pProgress = *aProgress;
4226
4227 /* ...but create a new one if it is null */
4228 if (pProgress.isNull())
4229 {
4230 pProgress.createObject();
4231 rc = pProgress->init(m->pVirtualBox,
4232 static_cast<IMedium*>(this),
4233 BstrFmt(tr("Deleting medium storage unit '%s'"), m->strLocationFull.c_str()).raw(),
4234 FALSE /* aCancelable */);
4235 if (FAILED(rc))
4236 throw rc;
4237 }
4238 }
4239
4240 /* setup task object to carry out the operation sync/async */
4241 pTask = new Medium::DeleteTask(this, pProgress, pMediumLockList);
4242 rc = pTask->rc();
4243 AssertComRC(rc);
4244 if (FAILED(rc))
4245 throw rc;
4246 }
4247 catch (HRESULT aRC) { rc = aRC; }
4248
4249 if (SUCCEEDED(rc))
4250 {
4251 if (aWait)
4252 rc = i_runNow(pTask);
4253 else
4254 rc = i_startThread(pTask);
4255
4256 if (SUCCEEDED(rc) && aProgress != NULL)
4257 *aProgress = pProgress;
4258
4259 }
4260 else
4261 {
4262 if (pTask)
4263 delete pTask;
4264
4265 /* Undo deleting state if necessary. */
4266 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4267 /* Make sure that any error signalled by unmarkForDeletion() is not
4268 * ending up in the error list (if the caller uses MultiResult). It
4269 * usually is spurious, as in most cases the medium hasn't been marked
4270 * for deletion when the error was thrown above. */
4271 ErrorInfoKeeper eik;
4272 i_unmarkForDeletion();
4273 }
4274
4275 return rc;
4276}
4277
4278/**
4279 * Mark a medium for deletion.
4280 *
4281 * @note Caller must hold the write lock on this medium!
4282 */
4283HRESULT Medium::i_markForDeletion()
4284{
4285 ComAssertRet(isWriteLockOnCurrentThread(), E_FAIL);
4286 switch (m->state)
4287 {
4288 case MediumState_Created:
4289 case MediumState_Inaccessible:
4290 m->preLockState = m->state;
4291 m->state = MediumState_Deleting;
4292 return S_OK;
4293 default:
4294 return i_setStateError();
4295 }
4296}
4297
4298/**
4299 * Removes the "mark for deletion".
4300 *
4301 * @note Caller must hold the write lock on this medium!
4302 */
4303HRESULT Medium::i_unmarkForDeletion()
4304{
4305 ComAssertRet(isWriteLockOnCurrentThread(), E_FAIL);
4306 switch (m->state)
4307 {
4308 case MediumState_Deleting:
4309 m->state = m->preLockState;
4310 return S_OK;
4311 default:
4312 return i_setStateError();
4313 }
4314}
4315
4316/**
4317 * Mark a medium for deletion which is in locked state.
4318 *
4319 * @note Caller must hold the write lock on this medium!
4320 */
4321HRESULT Medium::i_markLockedForDeletion()
4322{
4323 ComAssertRet(isWriteLockOnCurrentThread(), E_FAIL);
4324 if ( ( m->state == MediumState_LockedRead
4325 || m->state == MediumState_LockedWrite)
4326 && m->preLockState == MediumState_Created)
4327 {
4328 m->preLockState = MediumState_Deleting;
4329 return S_OK;
4330 }
4331 else
4332 return i_setStateError();
4333}
4334
4335/**
4336 * Removes the "mark for deletion" for a medium in locked state.
4337 *
4338 * @note Caller must hold the write lock on this medium!
4339 */
4340HRESULT Medium::i_unmarkLockedForDeletion()
4341{
4342 ComAssertRet(isWriteLockOnCurrentThread(), E_FAIL);
4343 if ( ( m->state == MediumState_LockedRead
4344 || m->state == MediumState_LockedWrite)
4345 && m->preLockState == MediumState_Deleting)
4346 {
4347 m->preLockState = MediumState_Created;
4348 return S_OK;
4349 }
4350 else
4351 return i_setStateError();
4352}
4353
4354/**
4355 * Queries the preferred merge direction from this to the other medium, i.e.
4356 * the one which requires the least amount of I/O and therefore time and
4357 * disk consumption.
4358 *
4359 * @returns Status code.
4360 * @retval E_FAIL in case determining the merge direction fails for some reason,
4361 * for example if getting the size of the media fails. There is no
4362 * error set though and the caller is free to continue to find out
4363 * what was going wrong later. Leaves fMergeForward unset.
4364 * @retval VBOX_E_INVALID_OBJECT_STATE if both media are not related to each other
4365 * An error is set.
4366 * @param pOther The other medium to merge with.
4367 * @param fMergeForward Resulting preferred merge direction (out).
4368 */
4369HRESULT Medium::i_queryPreferredMergeDirection(const ComObjPtr<Medium> &pOther,
4370 bool &fMergeForward)
4371{
4372 AssertReturn(pOther != NULL, E_FAIL);
4373 AssertReturn(pOther != this, E_FAIL);
4374
4375 AutoCaller autoCaller(this);
4376 AssertComRCReturnRC(autoCaller.rc());
4377
4378 AutoCaller otherCaller(pOther);
4379 AssertComRCReturnRC(otherCaller.rc());
4380
4381 HRESULT rc = S_OK;
4382 bool fThisParent = false; /**<< Flag whether this medium is the parent of pOther. */
4383
4384 try
4385 {
4386 // locking: we need the tree lock first because we access parent pointers
4387 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4388
4389 /* more sanity checking and figuring out the current merge direction */
4390 ComObjPtr<Medium> pMedium = i_getParent();
4391 while (!pMedium.isNull() && pMedium != pOther)
4392 pMedium = pMedium->i_getParent();
4393 if (pMedium == pOther)
4394 fThisParent = false;
4395 else
4396 {
4397 pMedium = pOther->i_getParent();
4398 while (!pMedium.isNull() && pMedium != this)
4399 pMedium = pMedium->i_getParent();
4400 if (pMedium == this)
4401 fThisParent = true;
4402 else
4403 {
4404 Utf8Str tgtLoc;
4405 {
4406 AutoReadLock alock(pOther COMMA_LOCKVAL_SRC_POS);
4407 tgtLoc = pOther->i_getLocationFull();
4408 }
4409
4410 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4411 throw setError(VBOX_E_INVALID_OBJECT_STATE,
4412 tr("Media '%s' and '%s' are unrelated"),
4413 m->strLocationFull.c_str(), tgtLoc.c_str());
4414 }
4415 }
4416
4417 /*
4418 * Figure out the preferred merge direction. The current way is to
4419 * get the current sizes of file based images and select the merge
4420 * direction depending on the size.
4421 *
4422 * Can't use the VD API to get current size here as the media might
4423 * be write locked by a running VM. Resort to RTFileQuerySize().
4424 */
4425 int vrc = VINF_SUCCESS;
4426 uint64_t cbMediumThis = 0;
4427 uint64_t cbMediumOther = 0;
4428
4429 if (i_isMediumFormatFile() && pOther->i_isMediumFormatFile())
4430 {
4431 vrc = RTFileQuerySize(this->i_getLocationFull().c_str(), &cbMediumThis);
4432 if (RT_SUCCESS(vrc))
4433 {
4434 vrc = RTFileQuerySize(pOther->i_getLocationFull().c_str(),
4435 &cbMediumOther);
4436 }
4437
4438 if (RT_FAILURE(vrc))
4439 rc = E_FAIL;
4440 else
4441 {
4442 /*
4443 * Check which merge direction might be more optimal.
4444 * This method is not bullet proof of course as there might
4445 * be overlapping blocks in the images so the file size is
4446 * not the best indicator but it is good enough for our purpose
4447 * and everything else is too complicated, especially when the
4448 * media are used by a running VM.
4449 */
4450 bool fMergeIntoThis = cbMediumThis > cbMediumOther;
4451 fMergeForward = fMergeIntoThis ^ fThisParent;
4452 }
4453 }
4454 }
4455 catch (HRESULT aRC) { rc = aRC; }
4456
4457 return rc;
4458}
4459
4460/**
4461 * Prepares this (source) medium, target medium and all intermediate media
4462 * for the merge operation.
4463 *
4464 * This method is to be called prior to calling the #mergeTo() to perform
4465 * necessary consistency checks and place involved media to appropriate
4466 * states. If #mergeTo() is not called or fails, the state modifications
4467 * performed by this method must be undone by #cancelMergeTo().
4468 *
4469 * See #mergeTo() for more information about merging.
4470 *
4471 * @param pTarget Target medium.
4472 * @param aMachineId Allowed machine attachment. NULL means do not check.
4473 * @param aSnapshotId Allowed snapshot attachment. NULL or empty UUID means
4474 * do not check.
4475 * @param fLockMedia Flag whether to lock the medium lock list or not.
4476 * If set to false and the medium lock list locking fails
4477 * later you must call #cancelMergeTo().
4478 * @param fMergeForward Resulting merge direction (out).
4479 * @param pParentForTarget New parent for target medium after merge (out).
4480 * @param aChildrenToReparent Medium lock list containing all children of the
4481 * source which will have to be reparented to the target
4482 * after merge (out).
4483 * @param aMediumLockList Medium locking information (out).
4484 *
4485 * @note Locks medium tree for reading. Locks this object, aTarget and all
4486 * intermediate media for writing.
4487 */
4488HRESULT Medium::i_prepareMergeTo(const ComObjPtr<Medium> &pTarget,
4489 const Guid *aMachineId,
4490 const Guid *aSnapshotId,
4491 bool fLockMedia,
4492 bool &fMergeForward,
4493 ComObjPtr<Medium> &pParentForTarget,
4494 MediumLockList * &aChildrenToReparent,
4495 MediumLockList * &aMediumLockList)
4496{
4497 AssertReturn(pTarget != NULL, E_FAIL);
4498 AssertReturn(pTarget != this, E_FAIL);
4499
4500 AutoCaller autoCaller(this);
4501 AssertComRCReturnRC(autoCaller.rc());
4502
4503 AutoCaller targetCaller(pTarget);
4504 AssertComRCReturnRC(targetCaller.rc());
4505
4506 HRESULT rc = S_OK;
4507 fMergeForward = false;
4508 pParentForTarget.setNull();
4509 Assert(aChildrenToReparent == NULL);
4510 aChildrenToReparent = NULL;
4511 Assert(aMediumLockList == NULL);
4512 aMediumLockList = NULL;
4513
4514 try
4515 {
4516 // locking: we need the tree lock first because we access parent pointers
4517 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4518
4519 /* more sanity checking and figuring out the merge direction */
4520 ComObjPtr<Medium> pMedium = i_getParent();
4521 while (!pMedium.isNull() && pMedium != pTarget)
4522 pMedium = pMedium->i_getParent();
4523 if (pMedium == pTarget)
4524 fMergeForward = false;
4525 else
4526 {
4527 pMedium = pTarget->i_getParent();
4528 while (!pMedium.isNull() && pMedium != this)
4529 pMedium = pMedium->i_getParent();
4530 if (pMedium == this)
4531 fMergeForward = true;
4532 else
4533 {
4534 Utf8Str tgtLoc;
4535 {
4536 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
4537 tgtLoc = pTarget->i_getLocationFull();
4538 }
4539
4540 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4541 throw setError(VBOX_E_INVALID_OBJECT_STATE,
4542 tr("Media '%s' and '%s' are unrelated"),
4543 m->strLocationFull.c_str(), tgtLoc.c_str());
4544 }
4545 }
4546
4547 /* Build the lock list. */
4548 aMediumLockList = new MediumLockList();
4549 treeLock.release();
4550 if (fMergeForward)
4551 rc = pTarget->i_createMediumLockList(true /* fFailIfInaccessible */,
4552 true /* fMediumLockWrite */,
4553 NULL,
4554 *aMediumLockList);
4555 else
4556 rc = i_createMediumLockList(true /* fFailIfInaccessible */,
4557 false /* fMediumLockWrite */,
4558 NULL,
4559 *aMediumLockList);
4560 treeLock.acquire();
4561 if (FAILED(rc))
4562 throw rc;
4563
4564 /* Sanity checking, must be after lock list creation as it depends on
4565 * valid medium states. The medium objects must be accessible. Only
4566 * do this if immediate locking is requested, otherwise it fails when
4567 * we construct a medium lock list for an already running VM. Snapshot
4568 * deletion uses this to simplify its life. */
4569 if (fLockMedia)
4570 {
4571 {
4572 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4573 if (m->state != MediumState_Created)
4574 throw i_setStateError();
4575 }
4576 {
4577 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
4578 if (pTarget->m->state != MediumState_Created)
4579 throw pTarget->i_setStateError();
4580 }
4581 }
4582
4583 /* check medium attachment and other sanity conditions */
4584 if (fMergeForward)
4585 {
4586 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4587 if (i_getChildren().size() > 1)
4588 {
4589 throw setError(VBOX_E_INVALID_OBJECT_STATE,
4590 tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
4591 m->strLocationFull.c_str(), i_getChildren().size());
4592 }
4593 /* One backreference is only allowed if the machine ID is not empty
4594 * and it matches the machine the medium is attached to (including
4595 * the snapshot ID if not empty). */
4596 if ( m->backRefs.size() != 0
4597 && ( !aMachineId
4598 || m->backRefs.size() != 1
4599 || aMachineId->isZero()
4600 || *i_getFirstMachineBackrefId() != *aMachineId
4601 || ( (!aSnapshotId || !aSnapshotId->isZero())
4602 && *i_getFirstMachineBackrefSnapshotId() != *aSnapshotId)))
4603 throw setError(VBOX_E_OBJECT_IN_USE,
4604 tr("Medium '%s' is attached to %d virtual machines"),
4605 m->strLocationFull.c_str(), m->backRefs.size());
4606 if (m->type == MediumType_Immutable)
4607 throw setError(VBOX_E_INVALID_OBJECT_STATE,
4608 tr("Medium '%s' is immutable"),
4609 m->strLocationFull.c_str());
4610 if (m->type == MediumType_MultiAttach)
4611 throw setError(VBOX_E_INVALID_OBJECT_STATE,
4612 tr("Medium '%s' is multi-attach"),
4613 m->strLocationFull.c_str());
4614 }
4615 else
4616 {
4617 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
4618 if (pTarget->i_getChildren().size() > 1)
4619 {
4620 throw setError(VBOX_E_OBJECT_IN_USE,
4621 tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
4622 pTarget->m->strLocationFull.c_str(),
4623 pTarget->i_getChildren().size());
4624 }
4625 if (pTarget->m->type == MediumType_Immutable)
4626 throw setError(VBOX_E_INVALID_OBJECT_STATE,
4627 tr("Medium '%s' is immutable"),
4628 pTarget->m->strLocationFull.c_str());
4629 if (pTarget->m->type == MediumType_MultiAttach)
4630 throw setError(VBOX_E_INVALID_OBJECT_STATE,
4631 tr("Medium '%s' is multi-attach"),
4632 pTarget->m->strLocationFull.c_str());
4633 }
4634 ComObjPtr<Medium> pLast(fMergeForward ? (Medium *)pTarget : this);
4635 ComObjPtr<Medium> pLastIntermediate = pLast->i_getParent();
4636 for (pLast = pLastIntermediate;
4637 !pLast.isNull() && pLast != pTarget && pLast != this;
4638 pLast = pLast->i_getParent())
4639 {
4640 AutoReadLock alock(pLast COMMA_LOCKVAL_SRC_POS);
4641 if (pLast->i_getChildren().size() > 1)
4642 {
4643 throw setError(VBOX_E_OBJECT_IN_USE,
4644 tr("Medium '%s' involved in the merge operation has more than one child medium (%d)"),
4645 pLast->m->strLocationFull.c_str(),
4646 pLast->i_getChildren().size());
4647 }
4648 if (pLast->m->backRefs.size() != 0)
4649 throw setError(VBOX_E_OBJECT_IN_USE,
4650 tr("Medium '%s' is attached to %d virtual machines"),
4651 pLast->m->strLocationFull.c_str(),
4652 pLast->m->backRefs.size());
4653
4654 }
4655
4656 /* Update medium states appropriately */
4657 {
4658 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4659
4660 if (m->state == MediumState_Created)
4661 {
4662 rc = i_markForDeletion();
4663 if (FAILED(rc))
4664 throw rc;
4665 }
4666 else
4667 {
4668 if (fLockMedia)
4669 throw i_setStateError();
4670 else if ( m->state == MediumState_LockedWrite
4671 || m->state == MediumState_LockedRead)
4672 {
4673 /* Either mark it for deletion in locked state or allow
4674 * others to have done so. */
4675 if (m->preLockState == MediumState_Created)
4676 i_markLockedForDeletion();
4677 else if (m->preLockState != MediumState_Deleting)
4678 throw i_setStateError();
4679 }
4680 else
4681 throw i_setStateError();
4682 }
4683 }
4684
4685 if (fMergeForward)
4686 {
4687 /* we will need parent to reparent target */
4688 pParentForTarget = i_getParent();
4689 }
4690 else
4691 {
4692 /* we will need to reparent children of the source */
4693 aChildrenToReparent = new MediumLockList();
4694 for (MediaList::const_iterator it = i_getChildren().begin();
4695 it != i_getChildren().end();
4696 ++it)
4697 {
4698 pMedium = *it;
4699 aChildrenToReparent->Append(pMedium, true /* fLockWrite */);
4700 }
4701 if (fLockMedia && aChildrenToReparent)
4702 {
4703 treeLock.release();
4704 rc = aChildrenToReparent->Lock();
4705 treeLock.acquire();
4706 if (FAILED(rc))
4707 throw rc;
4708 }
4709 }
4710 for (pLast = pLastIntermediate;
4711 !pLast.isNull() && pLast != pTarget && pLast != this;
4712 pLast = pLast->i_getParent())
4713 {
4714 AutoWriteLock alock(pLast COMMA_LOCKVAL_SRC_POS);
4715 if (pLast->m->state == MediumState_Created)
4716 {
4717 rc = pLast->i_markForDeletion();
4718 if (FAILED(rc))
4719 throw rc;
4720 }
4721 else
4722 throw pLast->i_setStateError();
4723 }
4724
4725 /* Tweak the lock list in the backward merge case, as the target
4726 * isn't marked to be locked for writing yet. */
4727 if (!fMergeForward)
4728 {
4729 MediumLockList::Base::iterator lockListBegin =
4730 aMediumLockList->GetBegin();
4731 MediumLockList::Base::iterator lockListEnd =
4732 aMediumLockList->GetEnd();
4733 lockListEnd--;
4734 for (MediumLockList::Base::iterator it = lockListBegin;
4735 it != lockListEnd;
4736 ++it)
4737 {
4738 MediumLock &mediumLock = *it;
4739 if (mediumLock.GetMedium() == pTarget)
4740 {
4741 HRESULT rc2 = mediumLock.UpdateLock(true);
4742 AssertComRC(rc2);
4743 break;
4744 }
4745 }
4746 }
4747
4748 if (fLockMedia)
4749 {
4750 treeLock.release();
4751 rc = aMediumLockList->Lock();
4752 treeLock.acquire();
4753 if (FAILED(rc))
4754 {
4755 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
4756 throw setError(rc,
4757 tr("Failed to lock media when merging to '%s'"),
4758 pTarget->i_getLocationFull().c_str());
4759 }
4760 }
4761 }
4762 catch (HRESULT aRC) { rc = aRC; }
4763
4764 if (FAILED(rc))
4765 {
4766 if (aMediumLockList)
4767 {
4768 delete aMediumLockList;
4769 aMediumLockList = NULL;
4770 }
4771 if (aChildrenToReparent)
4772 {
4773 delete aChildrenToReparent;
4774 aChildrenToReparent = NULL;
4775 }
4776 }
4777
4778 return rc;
4779}
4780
4781/**
4782 * Merges this medium to the specified medium which must be either its
4783 * direct ancestor or descendant.
4784 *
4785 * Given this medium is SOURCE and the specified medium is TARGET, we will
4786 * get two variants of the merge operation:
4787 *
4788 * forward merge
4789 * ------------------------->
4790 * [Extra] <- SOURCE <- Intermediate <- TARGET
4791 * Any Del Del LockWr
4792 *
4793 *
4794 * backward merge
4795 * <-------------------------
4796 * TARGET <- Intermediate <- SOURCE <- [Extra]
4797 * LockWr Del Del LockWr
4798 *
4799 * Each diagram shows the involved media on the media chain where
4800 * SOURCE and TARGET belong. Under each medium there is a state value which
4801 * the medium must have at a time of the mergeTo() call.
4802 *
4803 * The media in the square braces may be absent (e.g. when the forward
4804 * operation takes place and SOURCE is the base medium, or when the backward
4805 * merge operation takes place and TARGET is the last child in the chain) but if
4806 * they present they are involved too as shown.
4807 *
4808 * Neither the source medium nor intermediate media may be attached to
4809 * any VM directly or in the snapshot, otherwise this method will assert.
4810 *
4811 * The #prepareMergeTo() method must be called prior to this method to place all
4812 * involved to necessary states and perform other consistency checks.
4813 *
4814 * If @a aWait is @c true then this method will perform the operation on the
4815 * calling thread and will not return to the caller until the operation is
4816 * completed. When this method succeeds, all intermediate medium objects in
4817 * the chain will be uninitialized, the state of the target medium (and all
4818 * involved extra media) will be restored. @a aMediumLockList will not be
4819 * deleted, whether the operation is successful or not. The caller has to do
4820 * this if appropriate. Note that this (source) medium is not uninitialized
4821 * because of possible AutoCaller instances held by the caller of this method
4822 * on the current thread. It's therefore the responsibility of the caller to
4823 * call Medium::uninit() after releasing all callers.
4824 *
4825 * If @a aWait is @c false then this method will create a thread to perform the
4826 * operation asynchronously and will return immediately. If the operation
4827 * succeeds, the thread will uninitialize the source medium object and all
4828 * intermediate medium objects in the chain, reset the state of the target
4829 * medium (and all involved extra media) and delete @a aMediumLockList.
4830 * If the operation fails, the thread will only reset the states of all
4831 * involved media and delete @a aMediumLockList.
4832 *
4833 * When this method fails (regardless of the @a aWait mode), it is a caller's
4834 * responsibility to undo state changes and delete @a aMediumLockList using
4835 * #cancelMergeTo().
4836 *
4837 * If @a aProgress is not NULL but the object it points to is @c null then a new
4838 * progress object will be created and assigned to @a *aProgress on success,
4839 * otherwise the existing progress object is used. If Progress is NULL, then no
4840 * progress object is created/used at all. Note that @a aProgress cannot be
4841 * NULL when @a aWait is @c false (this method will assert in this case).
4842 *
4843 * @param pTarget Target medium.
4844 * @param fMergeForward Merge direction.
4845 * @param pParentForTarget New parent for target medium after merge.
4846 * @param aChildrenToReparent List of children of the source which will have
4847 * to be reparented to the target after merge.
4848 * @param aMediumLockList Medium locking information.
4849 * @param aProgress Where to find/store a Progress object to track operation
4850 * completion.
4851 * @param aWait @c true if this method should block instead of creating
4852 * an asynchronous thread.
4853 *
4854 * @note Locks the tree lock for writing. Locks the media from the chain
4855 * for writing.
4856 */
4857HRESULT Medium::i_mergeTo(const ComObjPtr<Medium> &pTarget,
4858 bool fMergeForward,
4859 const ComObjPtr<Medium> &pParentForTarget,
4860 MediumLockList *aChildrenToReparent,
4861 MediumLockList *aMediumLockList,
4862 ComObjPtr<Progress> *aProgress,
4863 bool aWait)
4864{
4865 AssertReturn(pTarget != NULL, E_FAIL);
4866 AssertReturn(pTarget != this, E_FAIL);
4867 AssertReturn(aMediumLockList != NULL, E_FAIL);
4868 AssertReturn(aProgress != NULL || aWait == true, E_FAIL);
4869
4870 AutoCaller autoCaller(this);
4871 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4872
4873 AutoCaller targetCaller(pTarget);
4874 AssertComRCReturnRC(targetCaller.rc());
4875
4876 HRESULT rc = S_OK;
4877 ComObjPtr<Progress> pProgress;
4878 Medium::Task *pTask = NULL;
4879
4880 try
4881 {
4882 if (aProgress != NULL)
4883 {
4884 /* use the existing progress object... */
4885 pProgress = *aProgress;
4886
4887 /* ...but create a new one if it is null */
4888 if (pProgress.isNull())
4889 {
4890 Utf8Str tgtName;
4891 {
4892 AutoReadLock alock(pTarget COMMA_LOCKVAL_SRC_POS);
4893 tgtName = pTarget->i_getName();
4894 }
4895
4896 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4897
4898 pProgress.createObject();
4899 rc = pProgress->init(m->pVirtualBox,
4900 static_cast<IMedium*>(this),
4901 BstrFmt(tr("Merging medium '%s' to '%s'"),
4902 i_getName().c_str(),
4903 tgtName.c_str()).raw(),
4904 TRUE /* aCancelable */);
4905 if (FAILED(rc))
4906 throw rc;
4907 }
4908 }
4909
4910 /* setup task object to carry out the operation sync/async */
4911 pTask = new Medium::MergeTask(this, pTarget, fMergeForward,
4912 pParentForTarget, aChildrenToReparent,
4913 pProgress, aMediumLockList,
4914 aWait /* fKeepMediumLockList */);
4915 rc = pTask->rc();
4916 AssertComRC(rc);
4917 if (FAILED(rc))
4918 throw rc;
4919 }
4920 catch (HRESULT aRC) { rc = aRC; }
4921
4922 if (SUCCEEDED(rc))
4923 {
4924 if (aWait)
4925 rc = i_runNow(pTask);
4926 else
4927 rc = i_startThread(pTask);
4928
4929 if (SUCCEEDED(rc) && aProgress != NULL)
4930 *aProgress = pProgress;
4931 }
4932 else if (pTask != NULL)
4933 delete pTask;
4934
4935 return rc;
4936}
4937
4938/**
4939 * Undoes what #prepareMergeTo() did. Must be called if #mergeTo() is not
4940 * called or fails. Frees memory occupied by @a aMediumLockList and unlocks
4941 * the medium objects in @a aChildrenToReparent.
4942 *
4943 * @param aChildrenToReparent List of children of the source which will have
4944 * to be reparented to the target after merge.
4945 * @param aMediumLockList Medium locking information.
4946 *
4947 * @note Locks the media from the chain for writing.
4948 */
4949void Medium::i_cancelMergeTo(MediumLockList *aChildrenToReparent,
4950 MediumLockList *aMediumLockList)
4951{
4952 AutoCaller autoCaller(this);
4953 AssertComRCReturnVoid(autoCaller.rc());
4954
4955 AssertReturnVoid(aMediumLockList != NULL);
4956
4957 /* Revert media marked for deletion to previous state. */
4958 HRESULT rc;
4959 MediumLockList::Base::const_iterator mediumListBegin =
4960 aMediumLockList->GetBegin();
4961 MediumLockList::Base::const_iterator mediumListEnd =
4962 aMediumLockList->GetEnd();
4963 for (MediumLockList::Base::const_iterator it = mediumListBegin;
4964 it != mediumListEnd;
4965 ++it)
4966 {
4967 const MediumLock &mediumLock = *it;
4968 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
4969 AutoWriteLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
4970
4971 if (pMedium->m->state == MediumState_Deleting)
4972 {
4973 rc = pMedium->i_unmarkForDeletion();
4974 AssertComRC(rc);
4975 }
4976 }
4977
4978 /* the destructor will do the work */
4979 delete aMediumLockList;
4980
4981 /* unlock the children which had to be reparented, the destructor will do
4982 * the work */
4983 if (aChildrenToReparent)
4984 delete aChildrenToReparent;
4985}
4986
4987/**
4988 * Fix the parent UUID of all children to point to this medium as their
4989 * parent.
4990 */
4991HRESULT Medium::i_fixParentUuidOfChildren(MediumLockList *pChildrenToReparent)
4992{
4993 Assert(!isWriteLockOnCurrentThread());
4994 Assert(!m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
4995 MediumLockList mediumLockList;
4996 HRESULT rc = i_createMediumLockList(true /* fFailIfInaccessible */,
4997 false /* fMediumLockWrite */,
4998 this,
4999 mediumLockList);
5000 AssertComRCReturnRC(rc);
5001
5002 try
5003 {
5004 PVBOXHDD hdd;
5005 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
5006 ComAssertRCThrow(vrc, E_FAIL);
5007
5008 try
5009 {
5010 MediumLockList::Base::iterator lockListBegin =
5011 mediumLockList.GetBegin();
5012 MediumLockList::Base::iterator lockListEnd =
5013 mediumLockList.GetEnd();
5014 for (MediumLockList::Base::iterator it = lockListBegin;
5015 it != lockListEnd;
5016 ++it)
5017 {
5018 MediumLock &mediumLock = *it;
5019 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
5020 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
5021
5022 // open the medium
5023 vrc = VDOpen(hdd,
5024 pMedium->m->strFormat.c_str(),
5025 pMedium->m->strLocationFull.c_str(),
5026 VD_OPEN_FLAGS_READONLY | m->uOpenFlagsDef,
5027 pMedium->m->vdImageIfaces);
5028 if (RT_FAILURE(vrc))
5029 throw vrc;
5030 }
5031
5032 MediumLockList::Base::iterator childrenBegin = pChildrenToReparent->GetBegin();
5033 MediumLockList::Base::iterator childrenEnd = pChildrenToReparent->GetEnd();
5034 for (MediumLockList::Base::iterator it = childrenBegin;
5035 it != childrenEnd;
5036 ++it)
5037 {
5038 Medium *pMedium = it->GetMedium();
5039 /* VD_OPEN_FLAGS_INFO since UUID is wrong yet */
5040 vrc = VDOpen(hdd,
5041 pMedium->m->strFormat.c_str(),
5042 pMedium->m->strLocationFull.c_str(),
5043 VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
5044 pMedium->m->vdImageIfaces);
5045 if (RT_FAILURE(vrc))
5046 throw vrc;
5047
5048 vrc = VDSetParentUuid(hdd, VD_LAST_IMAGE, m->id.raw());
5049 if (RT_FAILURE(vrc))
5050 throw vrc;
5051
5052 vrc = VDClose(hdd, false /* fDelete */);
5053 if (RT_FAILURE(vrc))
5054 throw vrc;
5055 }
5056 }
5057 catch (HRESULT aRC) { rc = aRC; }
5058 catch (int aVRC)
5059 {
5060 rc = setError(E_FAIL,
5061 tr("Could not update medium UUID references to parent '%s' (%s)"),
5062 m->strLocationFull.c_str(),
5063 i_vdError(aVRC).c_str());
5064 }
5065
5066 VDDestroy(hdd);
5067 }
5068 catch (HRESULT aRC) { rc = aRC; }
5069
5070 return rc;
5071}
5072
5073/**
5074 * Used by IAppliance to export disk images.
5075 *
5076 * @param aFilename Filename to create (UTF8).
5077 * @param aFormat Medium format for creating @a aFilename.
5078 * @param aVariant Which exact image format variant to use
5079 * for the destination image.
5080 * @param aVDImageIOCallbacks Pointer to the callback table for a
5081 * VDINTERFACEIO interface. May be NULL.
5082 * @param aVDImageIOUser Opaque data for the callbacks.
5083 * @param aProgress Progress object to use.
5084 * @return
5085 * @note The source format is defined by the Medium instance.
5086 */
5087HRESULT Medium::i_exportFile(const char *aFilename,
5088 const ComObjPtr<MediumFormat> &aFormat,
5089 MediumVariant_T aVariant,
5090 PVDINTERFACEIO aVDImageIOIf, void *aVDImageIOUser,
5091 const ComObjPtr<Progress> &aProgress)
5092{
5093 AssertPtrReturn(aFilename, E_INVALIDARG);
5094 AssertReturn(!aFormat.isNull(), E_INVALIDARG);
5095 AssertReturn(!aProgress.isNull(), E_INVALIDARG);
5096
5097 AutoCaller autoCaller(this);
5098 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5099
5100 HRESULT rc = S_OK;
5101 Medium::Task *pTask = NULL;
5102
5103 try
5104 {
5105 // This needs no extra locks besides what is done in the called methods.
5106
5107 /* Build the source lock list. */
5108 MediumLockList *pSourceMediumLockList(new MediumLockList());
5109 rc = i_createMediumLockList(true /* fFailIfInaccessible */,
5110 false /* fMediumLockWrite */,
5111 NULL,
5112 *pSourceMediumLockList);
5113 if (FAILED(rc))
5114 {
5115 delete pSourceMediumLockList;
5116 throw rc;
5117 }
5118
5119 rc = pSourceMediumLockList->Lock();
5120 if (FAILED(rc))
5121 {
5122 delete pSourceMediumLockList;
5123 throw setError(rc,
5124 tr("Failed to lock source media '%s'"),
5125 i_getLocationFull().c_str());
5126 }
5127
5128 /* setup task object to carry out the operation asynchronously */
5129 pTask = new Medium::ExportTask(this, aProgress, aFilename, aFormat,
5130 aVariant, aVDImageIOIf,
5131 aVDImageIOUser, pSourceMediumLockList);
5132 rc = pTask->rc();
5133 AssertComRC(rc);
5134 if (FAILED(rc))
5135 throw rc;
5136 }
5137 catch (HRESULT aRC) { rc = aRC; }
5138
5139 if (SUCCEEDED(rc))
5140 rc = i_startThread(pTask);
5141 else if (pTask != NULL)
5142 delete pTask;
5143
5144 return rc;
5145}
5146
5147/**
5148 * Used by IAppliance to import disk images.
5149 *
5150 * @param aFilename Filename to read (UTF8).
5151 * @param aFormat Medium format for reading @a aFilename.
5152 * @param aVariant Which exact image format variant to use
5153 * for the destination image.
5154 * @param aVDImageIOCallbacks Pointer to the callback table for a
5155 * VDINTERFACEIO interface. May be NULL.
5156 * @param aVDImageIOUser Opaque data for the callbacks.
5157 * @param aParent Parent medium. May be NULL.
5158 * @param aProgress Progress object to use.
5159 * @return
5160 * @note The destination format is defined by the Medium instance.
5161 */
5162HRESULT Medium::i_importFile(const char *aFilename,
5163 const ComObjPtr<MediumFormat> &aFormat,
5164 MediumVariant_T aVariant,
5165 PVDINTERFACEIO aVDImageIOIf, void *aVDImageIOUser,
5166 const ComObjPtr<Medium> &aParent,
5167 const ComObjPtr<Progress> &aProgress)
5168{
5169 AssertPtrReturn(aFilename, E_INVALIDARG);
5170 AssertReturn(!aFormat.isNull(), E_INVALIDARG);
5171 AssertReturn(!aProgress.isNull(), E_INVALIDARG);
5172
5173 AutoCaller autoCaller(this);
5174 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5175
5176 HRESULT rc = S_OK;
5177 Medium::Task *pTask = NULL;
5178
5179 try
5180 {
5181 // locking: we need the tree lock first because we access parent pointers
5182 // and we need to write-lock the media involved
5183 uint32_t cHandles = 2;
5184 LockHandle* pHandles[3] = { &m->pVirtualBox->i_getMediaTreeLockHandle(),
5185 this->lockHandle() };
5186 /* Only add parent to the lock if it is not null */
5187 if (!aParent.isNull())
5188 pHandles[cHandles++] = aParent->lockHandle();
5189 AutoWriteLock alock(cHandles,
5190 pHandles
5191 COMMA_LOCKVAL_SRC_POS);
5192
5193 if ( m->state != MediumState_NotCreated
5194 && m->state != MediumState_Created)
5195 throw i_setStateError();
5196
5197 /* Build the target lock list. */
5198 MediumLockList *pTargetMediumLockList(new MediumLockList());
5199 alock.release();
5200 rc = i_createMediumLockList(true /* fFailIfInaccessible */,
5201 true /* fMediumLockWrite */,
5202 aParent,
5203 *pTargetMediumLockList);
5204 alock.acquire();
5205 if (FAILED(rc))
5206 {
5207 delete pTargetMediumLockList;
5208 throw rc;
5209 }
5210
5211 alock.release();
5212 rc = pTargetMediumLockList->Lock();
5213 alock.acquire();
5214 if (FAILED(rc))
5215 {
5216 delete pTargetMediumLockList;
5217 throw setError(rc,
5218 tr("Failed to lock target media '%s'"),
5219 i_getLocationFull().c_str());
5220 }
5221
5222 /* setup task object to carry out the operation asynchronously */
5223 pTask = new Medium::ImportTask(this, aProgress, aFilename, aFormat,
5224 aVariant, aVDImageIOIf,
5225 aVDImageIOUser, aParent,
5226 pTargetMediumLockList);
5227 rc = pTask->rc();
5228 AssertComRC(rc);
5229 if (FAILED(rc))
5230 throw rc;
5231
5232 if (m->state == MediumState_NotCreated)
5233 m->state = MediumState_Creating;
5234 }
5235 catch (HRESULT aRC) { rc = aRC; }
5236
5237 if (SUCCEEDED(rc))
5238 rc = i_startThread(pTask);
5239 else if (pTask != NULL)
5240 delete pTask;
5241
5242 return rc;
5243}
5244
5245/**
5246 * Internal version of the public CloneTo API which allows to enable certain
5247 * optimizations to improve speed during VM cloning.
5248 *
5249 * @param aTarget Target medium
5250 * @param aVariant Which exact image format variant to use
5251 * for the destination image.
5252 * @param aParent Parent medium. May be NULL.
5253 * @param aProgress Progress object to use.
5254 * @param idxSrcImageSame The last image in the source chain which has the
5255 * same content as the given image in the destination
5256 * chain. Use UINT32_MAX to disable this optimization.
5257 * @param idxDstImageSame The last image in the destination chain which has the
5258 * same content as the given image in the source chain.
5259 * Use UINT32_MAX to disable this optimization.
5260 * @return
5261 */
5262HRESULT Medium::i_cloneToEx(const ComObjPtr<Medium> &aTarget, ULONG aVariant,
5263 const ComObjPtr<Medium> &aParent, IProgress **aProgress,
5264 uint32_t idxSrcImageSame, uint32_t idxDstImageSame)
5265{
5266 CheckComArgNotNull(aTarget);
5267 CheckComArgOutPointerValid(aProgress);
5268 ComAssertRet(aTarget != this, E_INVALIDARG);
5269
5270 AutoCaller autoCaller(this);
5271 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5272
5273 HRESULT rc = S_OK;
5274 ComObjPtr<Progress> pProgress;
5275 Medium::Task *pTask = NULL;
5276
5277 try
5278 {
5279 // locking: we need the tree lock first because we access parent pointers
5280 // and we need to write-lock the media involved
5281 uint32_t cHandles = 3;
5282 LockHandle* pHandles[4] = { &m->pVirtualBox->i_getMediaTreeLockHandle(),
5283 this->lockHandle(),
5284 aTarget->lockHandle() };
5285 /* Only add parent to the lock if it is not null */
5286 if (!aParent.isNull())
5287 pHandles[cHandles++] = aParent->lockHandle();
5288 AutoWriteLock alock(cHandles,
5289 pHandles
5290 COMMA_LOCKVAL_SRC_POS);
5291
5292 if ( aTarget->m->state != MediumState_NotCreated
5293 && aTarget->m->state != MediumState_Created)
5294 throw aTarget->i_setStateError();
5295
5296 /* Build the source lock list. */
5297 MediumLockList *pSourceMediumLockList(new MediumLockList());
5298 alock.release();
5299 rc = i_createMediumLockList(true /* fFailIfInaccessible */,
5300 false /* fMediumLockWrite */,
5301 NULL,
5302 *pSourceMediumLockList);
5303 alock.acquire();
5304 if (FAILED(rc))
5305 {
5306 delete pSourceMediumLockList;
5307 throw rc;
5308 }
5309
5310 /* Build the target lock list (including the to-be parent chain). */
5311 MediumLockList *pTargetMediumLockList(new MediumLockList());
5312 alock.release();
5313 rc = aTarget->i_createMediumLockList(true /* fFailIfInaccessible */,
5314 true /* fMediumLockWrite */,
5315 aParent,
5316 *pTargetMediumLockList);
5317 alock.acquire();
5318 if (FAILED(rc))
5319 {
5320 delete pSourceMediumLockList;
5321 delete pTargetMediumLockList;
5322 throw rc;
5323 }
5324
5325 alock.release();
5326 rc = pSourceMediumLockList->Lock();
5327 alock.acquire();
5328 if (FAILED(rc))
5329 {
5330 delete pSourceMediumLockList;
5331 delete pTargetMediumLockList;
5332 throw setError(rc,
5333 tr("Failed to lock source media '%s'"),
5334 i_getLocationFull().c_str());
5335 }
5336 alock.release();
5337 rc = pTargetMediumLockList->Lock();
5338 alock.acquire();
5339 if (FAILED(rc))
5340 {
5341 delete pSourceMediumLockList;
5342 delete pTargetMediumLockList;
5343 throw setError(rc,
5344 tr("Failed to lock target media '%s'"),
5345 aTarget->i_getLocationFull().c_str());
5346 }
5347
5348 pProgress.createObject();
5349 rc = pProgress->init(m->pVirtualBox,
5350 static_cast <IMedium *>(this),
5351 BstrFmt(tr("Creating clone medium '%s'"), aTarget->m->strLocationFull.c_str()).raw(),
5352 TRUE /* aCancelable */);
5353 if (FAILED(rc))
5354 {
5355 delete pSourceMediumLockList;
5356 delete pTargetMediumLockList;
5357 throw rc;
5358 }
5359
5360 /* setup task object to carry out the operation asynchronously */
5361 pTask = new Medium::CloneTask(this, pProgress, aTarget,
5362 (MediumVariant_T)aVariant,
5363 aParent, idxSrcImageSame,
5364 idxDstImageSame, pSourceMediumLockList,
5365 pTargetMediumLockList);
5366 rc = pTask->rc();
5367 AssertComRC(rc);
5368 if (FAILED(rc))
5369 throw rc;
5370
5371 if (aTarget->m->state == MediumState_NotCreated)
5372 aTarget->m->state = MediumState_Creating;
5373 }
5374 catch (HRESULT aRC) { rc = aRC; }
5375
5376 if (SUCCEEDED(rc))
5377 {
5378 rc = i_startThread(pTask);
5379
5380 if (SUCCEEDED(rc))
5381 pProgress.queryInterfaceTo(aProgress);
5382 }
5383 else if (pTask != NULL)
5384 delete pTask;
5385
5386 return rc;
5387}
5388
5389////////////////////////////////////////////////////////////////////////////////
5390//
5391// Private methods
5392//
5393////////////////////////////////////////////////////////////////////////////////
5394
5395/**
5396 * Queries information from the medium.
5397 *
5398 * As a result of this call, the accessibility state and data members such as
5399 * size and description will be updated with the current information.
5400 *
5401 * @note This method may block during a system I/O call that checks storage
5402 * accessibility.
5403 *
5404 * @note Caller MUST NOT hold the media tree or medium lock.
5405 *
5406 * @note Locks mParent for reading. Locks this object for writing.
5407 *
5408 * @param fSetImageId Whether to reset the UUID contained in the image file to the UUID in the medium instance data (see SetIDs())
5409 * @param fSetParentId Whether to reset the parent UUID contained in the image file to the parent
5410 * UUID in the medium instance data (see SetIDs())
5411 * @return
5412 */
5413HRESULT Medium::i_queryInfo(bool fSetImageId, bool fSetParentId, AutoCaller &autoCaller)
5414{
5415 Assert(!isWriteLockOnCurrentThread());
5416 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5417
5418 if ( m->state != MediumState_Created
5419 && m->state != MediumState_Inaccessible
5420 && m->state != MediumState_LockedRead)
5421 return E_FAIL;
5422
5423 HRESULT rc = S_OK;
5424
5425 int vrc = VINF_SUCCESS;
5426
5427 /* check if a blocking i_queryInfo() call is in progress on some other thread,
5428 * and wait for it to finish if so instead of querying data ourselves */
5429 if (m->queryInfoRunning)
5430 {
5431 Assert( m->state == MediumState_LockedRead
5432 || m->state == MediumState_LockedWrite);
5433
5434 while (m->queryInfoRunning)
5435 {
5436 alock.release();
5437 /* must not hold the object lock now */
5438 Assert(!isWriteLockOnCurrentThread());
5439 {
5440 AutoReadLock qlock(m->queryInfoSem COMMA_LOCKVAL_SRC_POS);
5441 }
5442 alock.acquire();
5443 }
5444
5445 return S_OK;
5446 }
5447
5448 bool success = false;
5449 Utf8Str lastAccessError;
5450
5451 /* are we dealing with a new medium constructed using the existing
5452 * location? */
5453 bool isImport = m->id.isZero();
5454 unsigned uOpenFlags = VD_OPEN_FLAGS_INFO;
5455
5456 /* Note that we don't use VD_OPEN_FLAGS_READONLY when opening new
5457 * media because that would prevent necessary modifications
5458 * when opening media of some third-party formats for the first
5459 * time in VirtualBox (such as VMDK for which VDOpen() needs to
5460 * generate an UUID if it is missing) */
5461 if ( m->hddOpenMode == OpenReadOnly
5462 || m->type == MediumType_Readonly
5463 || (!isImport && !fSetImageId && !fSetParentId)
5464 )
5465 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
5466
5467 /* Open shareable medium with the appropriate flags */
5468 if (m->type == MediumType_Shareable)
5469 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
5470
5471 /* Lock the medium, which makes the behavior much more consistent, must be
5472 * done before dropping the object lock and setting queryInfoRunning. */
5473 ComPtr<IToken> pToken;
5474 if (uOpenFlags & (VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SHAREABLE))
5475 rc = LockRead(pToken.asOutParam());
5476 else
5477 rc = LockWrite(pToken.asOutParam());
5478 if (FAILED(rc)) return rc;
5479
5480 /* Copies of the input state fields which are not read-only,
5481 * as we're dropping the lock. CAUTION: be extremely careful what
5482 * you do with the contents of this medium object, as you will
5483 * create races if there are concurrent changes. */
5484 Utf8Str format(m->strFormat);
5485 Utf8Str location(m->strLocationFull);
5486 ComObjPtr<MediumFormat> formatObj = m->formatObj;
5487
5488 /* "Output" values which can't be set because the lock isn't held
5489 * at the time the values are determined. */
5490 Guid mediumId = m->id;
5491 uint64_t mediumSize = 0;
5492 uint64_t mediumLogicalSize = 0;
5493
5494 /* Flag whether a base image has a non-zero parent UUID and thus
5495 * need repairing after it was closed again. */
5496 bool fRepairImageZeroParentUuid = false;
5497
5498 ComObjPtr<VirtualBox> pVirtualBox = m->pVirtualBox;
5499
5500 /* must be set before leaving the object lock the first time */
5501 m->queryInfoRunning = true;
5502
5503 /* must leave object lock now, because a lock from a higher lock class
5504 * is needed and also a lengthy operation is coming */
5505 alock.release();
5506 autoCaller.release();
5507
5508 /* Note that taking the queryInfoSem after leaving the object lock above
5509 * can lead to short spinning of the loops waiting for i_queryInfo() to
5510 * complete. This is unavoidable since the other order causes a lock order
5511 * violation: here it would be requesting the object lock (at the beginning
5512 * of the method), then queryInfoSem, and below the other way round. */
5513 AutoWriteLock qlock(m->queryInfoSem COMMA_LOCKVAL_SRC_POS);
5514
5515 /* take the opportunity to have a media tree lock, released initially */
5516 Assert(!isWriteLockOnCurrentThread());
5517 Assert(!pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
5518 AutoWriteLock treeLock(pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
5519 treeLock.release();
5520
5521 /* re-take the caller, but not the object lock, to keep uninit away */
5522 autoCaller.add();
5523 if (FAILED(autoCaller.rc()))
5524 {
5525 m->queryInfoRunning = false;
5526 return autoCaller.rc();
5527 }
5528
5529 try
5530 {
5531 /* skip accessibility checks for host drives */
5532 if (m->hostDrive)
5533 {
5534 success = true;
5535 throw S_OK;
5536 }
5537
5538 PVBOXHDD hdd;
5539 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
5540 ComAssertRCThrow(vrc, E_FAIL);
5541
5542 try
5543 {
5544 /** @todo This kind of opening of media is assuming that diff
5545 * media can be opened as base media. Should be documented that
5546 * it must work for all medium format backends. */
5547 vrc = VDOpen(hdd,
5548 format.c_str(),
5549 location.c_str(),
5550 uOpenFlags | m->uOpenFlagsDef,
5551 m->vdImageIfaces);
5552 if (RT_FAILURE(vrc))
5553 {
5554 lastAccessError = Utf8StrFmt(tr("Could not open the medium '%s'%s"),
5555 location.c_str(), i_vdError(vrc).c_str());
5556 throw S_OK;
5557 }
5558
5559 if (formatObj->i_getCapabilities() & MediumFormatCapabilities_Uuid)
5560 {
5561 /* Modify the UUIDs if necessary. The associated fields are
5562 * not modified by other code, so no need to copy. */
5563 if (fSetImageId)
5564 {
5565 alock.acquire();
5566 vrc = VDSetUuid(hdd, 0, m->uuidImage.raw());
5567 alock.release();
5568 if (RT_FAILURE(vrc))
5569 {
5570 lastAccessError = Utf8StrFmt(tr("Could not update the UUID of medium '%s'%s"),
5571 location.c_str(), i_vdError(vrc).c_str());
5572 throw S_OK;
5573 }
5574 mediumId = m->uuidImage;
5575 }
5576 if (fSetParentId)
5577 {
5578 alock.acquire();
5579 vrc = VDSetParentUuid(hdd, 0, m->uuidParentImage.raw());
5580 alock.release();
5581 if (RT_FAILURE(vrc))
5582 {
5583 lastAccessError = Utf8StrFmt(tr("Could not update the parent UUID of medium '%s'%s"),
5584 location.c_str(), i_vdError(vrc).c_str());
5585 throw S_OK;
5586 }
5587 }
5588 /* zap the information, these are no long-term members */
5589 alock.acquire();
5590 unconst(m->uuidImage).clear();
5591 unconst(m->uuidParentImage).clear();
5592 alock.release();
5593
5594 /* check the UUID */
5595 RTUUID uuid;
5596 vrc = VDGetUuid(hdd, 0, &uuid);
5597 ComAssertRCThrow(vrc, E_FAIL);
5598
5599 if (isImport)
5600 {
5601 mediumId = uuid;
5602
5603 if (mediumId.isZero() && (m->hddOpenMode == OpenReadOnly))
5604 // only when importing a VDMK that has no UUID, create one in memory
5605 mediumId.create();
5606 }
5607 else
5608 {
5609 Assert(!mediumId.isZero());
5610
5611 if (mediumId != uuid)
5612 {
5613 /** @todo r=klaus this always refers to VirtualBox.xml as the medium registry, even for new VMs */
5614 lastAccessError = Utf8StrFmt(
5615 tr("UUID {%RTuuid} of the medium '%s' does not match the value {%RTuuid} stored in the media registry ('%s')"),
5616 &uuid,
5617 location.c_str(),
5618 mediumId.raw(),
5619 pVirtualBox->i_settingsFilePath().c_str());
5620 throw S_OK;
5621 }
5622 }
5623 }
5624 else
5625 {
5626 /* the backend does not support storing UUIDs within the
5627 * underlying storage so use what we store in XML */
5628
5629 if (fSetImageId)
5630 {
5631 /* set the UUID if an API client wants to change it */
5632 alock.acquire();
5633 mediumId = m->uuidImage;
5634 alock.release();
5635 }
5636 else if (isImport)
5637 {
5638 /* generate an UUID for an imported UUID-less medium */
5639 mediumId.create();
5640 }
5641 }
5642
5643 /* set the image uuid before the below parent uuid handling code
5644 * might place it somewhere in the media tree, so that the medium
5645 * UUID is valid at this point */
5646 alock.acquire();
5647 if (isImport || fSetImageId)
5648 unconst(m->id) = mediumId;
5649 alock.release();
5650
5651 /* get the medium variant */
5652 unsigned uImageFlags;
5653 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
5654 ComAssertRCThrow(vrc, E_FAIL);
5655 alock.acquire();
5656 m->variant = (MediumVariant_T)uImageFlags;
5657 alock.release();
5658
5659 /* check/get the parent uuid and update corresponding state */
5660 if (uImageFlags & VD_IMAGE_FLAGS_DIFF)
5661 {
5662 RTUUID parentId;
5663 vrc = VDGetParentUuid(hdd, 0, &parentId);
5664 ComAssertRCThrow(vrc, E_FAIL);
5665
5666 /* streamOptimized VMDK images are only accepted as base
5667 * images, as this allows automatic repair of OVF appliances.
5668 * Since such images don't support random writes they will not
5669 * be created for diff images. Only an overly smart user might
5670 * manually create this case. Too bad for him. */
5671 if ( (isImport || fSetParentId)
5672 && !(uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED))
5673 {
5674 /* the parent must be known to us. Note that we freely
5675 * call locking methods of mVirtualBox and parent, as all
5676 * relevant locks must be already held. There may be no
5677 * concurrent access to the just opened medium on other
5678 * threads yet (and init() will fail if this method reports
5679 * MediumState_Inaccessible) */
5680
5681 ComObjPtr<Medium> pParent;
5682 if (RTUuidIsNull(&parentId))
5683 rc = VBOX_E_OBJECT_NOT_FOUND;
5684 else
5685 rc = pVirtualBox->i_findHardDiskById(Guid(parentId), false /* aSetError */, &pParent);
5686 if (FAILED(rc))
5687 {
5688 if (fSetImageId && !fSetParentId)
5689 {
5690 /* If the image UUID gets changed for an existing
5691 * image then the parent UUID can be stale. In such
5692 * cases clear the parent information. The parent
5693 * information may/will be re-set later if the
5694 * API client wants to adjust a complete medium
5695 * hierarchy one by one. */
5696 rc = S_OK;
5697 alock.acquire();
5698 RTUuidClear(&parentId);
5699 vrc = VDSetParentUuid(hdd, 0, &parentId);
5700 alock.release();
5701 ComAssertRCThrow(vrc, E_FAIL);
5702 }
5703 else
5704 {
5705 lastAccessError = Utf8StrFmt(tr("Parent medium with UUID {%RTuuid} of the medium '%s' is not found in the media registry ('%s')"),
5706 &parentId, location.c_str(),
5707 pVirtualBox->i_settingsFilePath().c_str());
5708 throw S_OK;
5709 }
5710 }
5711
5712 /* must drop the caller before taking the tree lock */
5713 autoCaller.release();
5714 /* we set mParent & children() */
5715 treeLock.acquire();
5716 autoCaller.add();
5717 if (FAILED(autoCaller.rc()))
5718 throw autoCaller.rc();
5719
5720 if (m->pParent)
5721 i_deparent();
5722 i_setParent(pParent);
5723
5724 treeLock.release();
5725 }
5726 else
5727 {
5728 /* must drop the caller before taking the tree lock */
5729 autoCaller.release();
5730 /* we access mParent */
5731 treeLock.acquire();
5732 autoCaller.add();
5733 if (FAILED(autoCaller.rc()))
5734 throw autoCaller.rc();
5735
5736 /* check that parent UUIDs match. Note that there's no need
5737 * for the parent's AutoCaller (our lifetime is bound to
5738 * it) */
5739
5740 if (m->pParent.isNull())
5741 {
5742 /* Due to a bug in VDCopy() in VirtualBox 3.0.0-3.0.14
5743 * and 3.1.0-3.1.8 there are base images out there
5744 * which have a non-zero parent UUID. No point in
5745 * complaining about them, instead automatically
5746 * repair the problem. Later we can bring back the
5747 * error message, but we should wait until really
5748 * most users have repaired their images, either with
5749 * VBoxFixHdd or this way. */
5750#if 1
5751 fRepairImageZeroParentUuid = true;
5752#else /* 0 */
5753 lastAccessError = Utf8StrFmt(
5754 tr("Medium type of '%s' is differencing but it is not associated with any parent medium in the media registry ('%s')"),
5755 location.c_str(),
5756 pVirtualBox->settingsFilePath().c_str());
5757 treeLock.release();
5758 throw S_OK;
5759#endif /* 0 */
5760 }
5761
5762 {
5763 autoCaller.release();
5764 AutoReadLock parentLock(m->pParent COMMA_LOCKVAL_SRC_POS);
5765 autoCaller.add();
5766 if (FAILED(autoCaller.rc()))
5767 throw autoCaller.rc();
5768
5769 if ( !fRepairImageZeroParentUuid
5770 && m->pParent->i_getState() != MediumState_Inaccessible
5771 && m->pParent->i_getId() != parentId)
5772 {
5773 /** @todo r=klaus this always refers to VirtualBox.xml as the medium registry, even for new VMs */
5774 lastAccessError = Utf8StrFmt(
5775 tr("Parent UUID {%RTuuid} of the medium '%s' does not match UUID {%RTuuid} of its parent medium stored in the media registry ('%s')"),
5776 &parentId, location.c_str(),
5777 m->pParent->i_getId().raw(),
5778 pVirtualBox->i_settingsFilePath().c_str());
5779 parentLock.release();
5780 treeLock.release();
5781 throw S_OK;
5782 }
5783 }
5784
5785 /// @todo NEWMEDIA what to do if the parent is not
5786 /// accessible while the diff is? Probably nothing. The
5787 /// real code will detect the mismatch anyway.
5788
5789 treeLock.release();
5790 }
5791 }
5792
5793 mediumSize = VDGetFileSize(hdd, 0);
5794 mediumLogicalSize = VDGetSize(hdd, 0);
5795
5796 success = true;
5797 }
5798 catch (HRESULT aRC)
5799 {
5800 rc = aRC;
5801 }
5802
5803 vrc = VDDestroy(hdd);
5804 if (RT_FAILURE(vrc))
5805 {
5806 lastAccessError = Utf8StrFmt(tr("Could not update and close the medium '%s'%s"),
5807 location.c_str(), i_vdError(vrc).c_str());
5808 success = false;
5809 throw S_OK;
5810 }
5811 }
5812 catch (HRESULT aRC)
5813 {
5814 rc = aRC;
5815 }
5816
5817 autoCaller.release();
5818 treeLock.acquire();
5819 autoCaller.add();
5820 if (FAILED(autoCaller.rc()))
5821 {
5822 m->queryInfoRunning = false;
5823 return autoCaller.rc();
5824 }
5825 alock.acquire();
5826
5827 if (success)
5828 {
5829 m->size = mediumSize;
5830 m->logicalSize = mediumLogicalSize;
5831 m->strLastAccessError.setNull();
5832 }
5833 else
5834 {
5835 m->strLastAccessError = lastAccessError;
5836 LogWarningFunc(("'%s' is not accessible (error='%s', rc=%Rhrc, vrc=%Rrc)\n",
5837 location.c_str(), m->strLastAccessError.c_str(),
5838 rc, vrc));
5839 }
5840
5841 /* Set the proper state according to the result of the check */
5842 if (success)
5843 m->preLockState = MediumState_Created;
5844 else
5845 m->preLockState = MediumState_Inaccessible;
5846
5847 /* unblock anyone waiting for the i_queryInfo results */
5848 qlock.release();
5849 m->queryInfoRunning = false;
5850
5851 pToken->Abandon();
5852 pToken.setNull();
5853
5854 if (FAILED(rc)) return rc;
5855
5856 /* If this is a base image which incorrectly has a parent UUID set,
5857 * repair the image now by zeroing the parent UUID. This is only done
5858 * when we have structural information from a config file, on import
5859 * this is not possible. If someone would accidentally call openMedium
5860 * with a diff image before the base is registered this would destroy
5861 * the diff. Not acceptable. */
5862 if (fRepairImageZeroParentUuid)
5863 {
5864 rc = LockWrite(pToken.asOutParam());
5865 if (FAILED(rc)) return rc;
5866
5867 alock.release();
5868
5869 try
5870 {
5871 PVBOXHDD hdd;
5872 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
5873 ComAssertRCThrow(vrc, E_FAIL);
5874
5875 try
5876 {
5877 vrc = VDOpen(hdd,
5878 format.c_str(),
5879 location.c_str(),
5880 (uOpenFlags & ~VD_OPEN_FLAGS_READONLY) | m->uOpenFlagsDef,
5881 m->vdImageIfaces);
5882 if (RT_FAILURE(vrc))
5883 throw S_OK;
5884
5885 RTUUID zeroParentUuid;
5886 RTUuidClear(&zeroParentUuid);
5887 vrc = VDSetParentUuid(hdd, 0, &zeroParentUuid);
5888 ComAssertRCThrow(vrc, E_FAIL);
5889 }
5890 catch (HRESULT aRC)
5891 {
5892 rc = aRC;
5893 }
5894
5895 VDDestroy(hdd);
5896 }
5897 catch (HRESULT aRC)
5898 {
5899 rc = aRC;
5900 }
5901
5902 pToken->Abandon();
5903 pToken.setNull();
5904 if (FAILED(rc)) return rc;
5905 }
5906
5907 return rc;
5908}
5909
5910/**
5911 * Performs extra checks if the medium can be closed and returns S_OK in
5912 * this case. Otherwise, returns a respective error message. Called by
5913 * Close() under the medium tree lock and the medium lock.
5914 *
5915 * @note Also reused by Medium::Reset().
5916 *
5917 * @note Caller must hold the media tree write lock!
5918 */
5919HRESULT Medium::i_canClose()
5920{
5921 Assert(m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
5922
5923 if (i_getChildren().size() != 0)
5924 return setError(VBOX_E_OBJECT_IN_USE,
5925 tr("Cannot close medium '%s' because it has %d child media"),
5926 m->strLocationFull.c_str(), i_getChildren().size());
5927
5928 return S_OK;
5929}
5930
5931/**
5932 * Unregisters this medium with mVirtualBox. Called by close() under the medium tree lock.
5933 *
5934 * @note Caller must have locked the media tree lock for writing!
5935 */
5936HRESULT Medium::i_unregisterWithVirtualBox()
5937{
5938 /* Note that we need to de-associate ourselves from the parent to let
5939 * unregisterMedium() properly save the registry */
5940
5941 /* we modify mParent and access children */
5942 Assert(m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
5943
5944 Medium *pParentBackup = m->pParent;
5945 AssertReturn(i_getChildren().size() == 0, E_FAIL);
5946 if (m->pParent)
5947 i_deparent();
5948
5949 HRESULT rc = m->pVirtualBox->i_unregisterMedium(this);
5950 if (FAILED(rc))
5951 {
5952 if (pParentBackup)
5953 {
5954 // re-associate with the parent as we are still relatives in the registry
5955 m->pParent = pParentBackup;
5956 m->pParent->m->llChildren.push_back(this);
5957 }
5958 }
5959
5960 return rc;
5961}
5962
5963/**
5964 * Like SetProperty but do not trigger a settings store. Only for internal use!
5965 */
5966HRESULT Medium::i_setPropertyDirect(const Utf8Str &aName, const Utf8Str &aValue)
5967{
5968 AutoCaller autoCaller(this);
5969 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5970
5971 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5972
5973 switch (m->state)
5974 {
5975 case MediumState_Created:
5976 case MediumState_Inaccessible:
5977 break;
5978 default:
5979 return i_setStateError();
5980 }
5981
5982 m->mapProperties[aName] = aValue;
5983
5984 return S_OK;
5985}
5986
5987/**
5988 * Sets the extended error info according to the current media state.
5989 *
5990 * @note Must be called from under this object's write or read lock.
5991 */
5992HRESULT Medium::i_setStateError()
5993{
5994 HRESULT rc = E_FAIL;
5995
5996 switch (m->state)
5997 {
5998 case MediumState_NotCreated:
5999 {
6000 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
6001 tr("Storage for the medium '%s' is not created"),
6002 m->strLocationFull.c_str());
6003 break;
6004 }
6005 case MediumState_Created:
6006 {
6007 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
6008 tr("Storage for the medium '%s' is already created"),
6009 m->strLocationFull.c_str());
6010 break;
6011 }
6012 case MediumState_LockedRead:
6013 {
6014 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
6015 tr("Medium '%s' is locked for reading by another task"),
6016 m->strLocationFull.c_str());
6017 break;
6018 }
6019 case MediumState_LockedWrite:
6020 {
6021 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
6022 tr("Medium '%s' is locked for writing by another task"),
6023 m->strLocationFull.c_str());
6024 break;
6025 }
6026 case MediumState_Inaccessible:
6027 {
6028 /* be in sync with Console::powerUpThread() */
6029 if (!m->strLastAccessError.isEmpty())
6030 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
6031 tr("Medium '%s' is not accessible. %s"),
6032 m->strLocationFull.c_str(), m->strLastAccessError.c_str());
6033 else
6034 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
6035 tr("Medium '%s' is not accessible"),
6036 m->strLocationFull.c_str());
6037 break;
6038 }
6039 case MediumState_Creating:
6040 {
6041 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
6042 tr("Storage for the medium '%s' is being created"),
6043 m->strLocationFull.c_str());
6044 break;
6045 }
6046 case MediumState_Deleting:
6047 {
6048 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
6049 tr("Storage for the medium '%s' is being deleted"),
6050 m->strLocationFull.c_str());
6051 break;
6052 }
6053 default:
6054 {
6055 AssertFailed();
6056 break;
6057 }
6058 }
6059
6060 return rc;
6061}
6062
6063/**
6064 * Sets the value of m->strLocationFull. The given location must be a fully
6065 * qualified path; relative paths are not supported here.
6066 *
6067 * As a special exception, if the specified location is a file path that ends with '/'
6068 * then the file name part will be generated by this method automatically in the format
6069 * '{<uuid>}.<ext>' where <uuid> is a fresh UUID that this method will generate
6070 * and assign to this medium, and <ext> is the default extension for this
6071 * medium's storage format. Note that this procedure requires the media state to
6072 * be NotCreated and will return a failure otherwise.
6073 *
6074 * @param aLocation Location of the storage unit. If the location is a FS-path,
6075 * then it can be relative to the VirtualBox home directory.
6076 * @param aFormat Optional fallback format if it is an import and the format
6077 * cannot be determined.
6078 *
6079 * @note Must be called from under this object's write lock.
6080 */
6081HRESULT Medium::i_setLocation(const Utf8Str &aLocation,
6082 const Utf8Str &aFormat /* = Utf8Str::Empty */)
6083{
6084 AssertReturn(!aLocation.isEmpty(), E_FAIL);
6085
6086 AutoCaller autoCaller(this);
6087 AssertComRCReturnRC(autoCaller.rc());
6088
6089 /* formatObj may be null only when initializing from an existing path and
6090 * no format is known yet */
6091 AssertReturn( (!m->strFormat.isEmpty() && !m->formatObj.isNull())
6092 || ( getObjectState().getState() == ObjectState::InInit
6093 && m->state != MediumState_NotCreated
6094 && m->id.isZero()
6095 && m->strFormat.isEmpty()
6096 && m->formatObj.isNull()),
6097 E_FAIL);
6098
6099 /* are we dealing with a new medium constructed using the existing
6100 * location? */
6101 bool isImport = m->strFormat.isEmpty();
6102
6103 if ( isImport
6104 || ( (m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File)
6105 && !m->hostDrive))
6106 {
6107 Guid id;
6108
6109 Utf8Str locationFull(aLocation);
6110
6111 if (m->state == MediumState_NotCreated)
6112 {
6113 /* must be a file (formatObj must be already known) */
6114 Assert(m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File);
6115
6116 if (RTPathFilename(aLocation.c_str()) == NULL)
6117 {
6118 /* no file name is given (either an empty string or ends with a
6119 * slash), generate a new UUID + file name if the state allows
6120 * this */
6121
6122 ComAssertMsgRet(!m->formatObj->i_getFileExtensions().empty(),
6123 ("Must be at least one extension if it is MediumFormatCapabilities_File\n"),
6124 E_FAIL);
6125
6126 Utf8Str strExt = m->formatObj->i_getFileExtensions().front();
6127 ComAssertMsgRet(!strExt.isEmpty(),
6128 ("Default extension must not be empty\n"),
6129 E_FAIL);
6130
6131 id.create();
6132
6133 locationFull = Utf8StrFmt("%s{%RTuuid}.%s",
6134 aLocation.c_str(), id.raw(), strExt.c_str());
6135 }
6136 }
6137
6138 // we must always have full paths now (if it refers to a file)
6139 if ( ( m->formatObj.isNull()
6140 || m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File)
6141 && !RTPathStartsWithRoot(locationFull.c_str()))
6142 return setError(VBOX_E_FILE_ERROR,
6143 tr("The given path '%s' is not fully qualified"),
6144 locationFull.c_str());
6145
6146 /* detect the backend from the storage unit if importing */
6147 if (isImport)
6148 {
6149 VDTYPE enmType = VDTYPE_INVALID;
6150 char *backendName = NULL;
6151
6152 int vrc = VINF_SUCCESS;
6153
6154 /* is it a file? */
6155 {
6156 RTFILE file;
6157 vrc = RTFileOpen(&file, locationFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
6158 if (RT_SUCCESS(vrc))
6159 RTFileClose(file);
6160 }
6161 if (RT_SUCCESS(vrc))
6162 {
6163 vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
6164 locationFull.c_str(), &backendName, &enmType);
6165 }
6166 else if ( vrc != VERR_FILE_NOT_FOUND
6167 && vrc != VERR_PATH_NOT_FOUND
6168 && vrc != VERR_ACCESS_DENIED
6169 && locationFull != aLocation)
6170 {
6171 /* assume it's not a file, restore the original location */
6172 locationFull = aLocation;
6173 vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
6174 locationFull.c_str(), &backendName, &enmType);
6175 }
6176
6177 if (RT_FAILURE(vrc))
6178 {
6179 if (vrc == VERR_ACCESS_DENIED)
6180 return setError(VBOX_E_FILE_ERROR,
6181 tr("Permission problem accessing the file for the medium '%s' (%Rrc)"),
6182 locationFull.c_str(), vrc);
6183 else if (vrc == VERR_FILE_NOT_FOUND || vrc == VERR_PATH_NOT_FOUND)
6184 return setError(VBOX_E_FILE_ERROR,
6185 tr("Could not find file for the medium '%s' (%Rrc)"),
6186 locationFull.c_str(), vrc);
6187 else if (aFormat.isEmpty())
6188 return setError(VBOX_E_IPRT_ERROR,
6189 tr("Could not get the storage format of the medium '%s' (%Rrc)"),
6190 locationFull.c_str(), vrc);
6191 else
6192 {
6193 HRESULT rc = i_setFormat(aFormat);
6194 /* setFormat() must not fail since we've just used the backend so
6195 * the format object must be there */
6196 AssertComRCReturnRC(rc);
6197 }
6198 }
6199 else if ( enmType == VDTYPE_INVALID
6200 || m->devType != i_convertToDeviceType(enmType))
6201 {
6202 /*
6203 * The user tried to use a image as a device which is not supported
6204 * by the backend.
6205 */
6206 return setError(E_FAIL,
6207 tr("The medium '%s' can't be used as the requested device type"),
6208 locationFull.c_str());
6209 }
6210 else
6211 {
6212 ComAssertRet(backendName != NULL && *backendName != '\0', E_FAIL);
6213
6214 HRESULT rc = i_setFormat(backendName);
6215 RTStrFree(backendName);
6216
6217 /* setFormat() must not fail since we've just used the backend so
6218 * the format object must be there */
6219 AssertComRCReturnRC(rc);
6220 }
6221 }
6222
6223 m->strLocationFull = locationFull;
6224
6225 /* is it still a file? */
6226 if ( (m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File)
6227 && (m->state == MediumState_NotCreated)
6228 )
6229 /* assign a new UUID (this UUID will be used when calling
6230 * VDCreateBase/VDCreateDiff as a wanted UUID). Note that we
6231 * also do that if we didn't generate it to make sure it is
6232 * either generated by us or reset to null */
6233 unconst(m->id) = id;
6234 }
6235 else
6236 m->strLocationFull = aLocation;
6237
6238 return S_OK;
6239}
6240
6241/**
6242 * Checks that the format ID is valid and sets it on success.
6243 *
6244 * Note that this method will caller-reference the format object on success!
6245 * This reference must be released somewhere to let the MediumFormat object be
6246 * uninitialized.
6247 *
6248 * @note Must be called from under this object's write lock.
6249 */
6250HRESULT Medium::i_setFormat(const Utf8Str &aFormat)
6251{
6252 /* get the format object first */
6253 {
6254 SystemProperties *pSysProps = m->pVirtualBox->i_getSystemProperties();
6255 AutoReadLock propsLock(pSysProps COMMA_LOCKVAL_SRC_POS);
6256
6257 unconst(m->formatObj) = pSysProps->i_mediumFormat(aFormat);
6258 if (m->formatObj.isNull())
6259 return setError(E_INVALIDARG,
6260 tr("Invalid medium storage format '%s'"),
6261 aFormat.c_str());
6262
6263 /* get properties (preinsert them as keys in the map). Note that the
6264 * map doesn't grow over the object life time since the set of
6265 * properties is meant to be constant. */
6266
6267 Assert(m->mapProperties.empty());
6268
6269 for (MediumFormat::PropertyArray::const_iterator it = m->formatObj->i_getProperties().begin();
6270 it != m->formatObj->i_getProperties().end();
6271 ++it)
6272 {
6273 m->mapProperties.insert(std::make_pair(it->strName, Utf8Str::Empty));
6274 }
6275 }
6276
6277 unconst(m->strFormat) = aFormat;
6278
6279 return S_OK;
6280}
6281
6282/**
6283 * Converts the Medium device type to the VD type.
6284 */
6285VDTYPE Medium::i_convertDeviceType()
6286{
6287 VDTYPE enmType;
6288
6289 switch (m->devType)
6290 {
6291 case DeviceType_HardDisk:
6292 enmType = VDTYPE_HDD;
6293 break;
6294 case DeviceType_DVD:
6295 enmType = VDTYPE_DVD;
6296 break;
6297 case DeviceType_Floppy:
6298 enmType = VDTYPE_FLOPPY;
6299 break;
6300 default:
6301 ComAssertFailedRet(VDTYPE_INVALID);
6302 }
6303
6304 return enmType;
6305}
6306
6307/**
6308 * Converts from the VD type to the medium type.
6309 */
6310DeviceType_T Medium::i_convertToDeviceType(VDTYPE enmType)
6311{
6312 DeviceType_T devType;
6313
6314 switch (enmType)
6315 {
6316 case VDTYPE_HDD:
6317 devType = DeviceType_HardDisk;
6318 break;
6319 case VDTYPE_DVD:
6320 devType = DeviceType_DVD;
6321 break;
6322 case VDTYPE_FLOPPY:
6323 devType = DeviceType_Floppy;
6324 break;
6325 default:
6326 ComAssertFailedRet(DeviceType_Null);
6327 }
6328
6329 return devType;
6330}
6331
6332/**
6333 * Internal method which checks whether a property name is for a filter plugin.
6334 */
6335bool Medium::i_isPropertyForFilter(const com::Utf8Str &aName)
6336{
6337 /* If the name contains "/" use the part before as a filter name and lookup the filter. */
6338 size_t offSlash;
6339 if ((offSlash = aName.find("/", 0)) != aName.npos)
6340 {
6341 com::Utf8Str strFilter;
6342 com::Utf8Str strKey;
6343
6344 HRESULT rc = strFilter.assignEx(aName, 0, offSlash);
6345 if (FAILED(rc))
6346 return false;
6347
6348 rc = strKey.assignEx(aName, offSlash + 1, aName.length() - offSlash - 1); /* Skip slash */
6349 if (FAILED(rc))
6350 return false;
6351
6352 VDFILTERINFO FilterInfo;
6353 int vrc = VDFilterInfoOne(strFilter.c_str(), &FilterInfo);
6354 if (RT_SUCCESS(vrc))
6355 {
6356 /* Check that the property exists. */
6357 PCVDCONFIGINFO paConfig = FilterInfo.paConfigInfo;
6358 while (paConfig->pszKey)
6359 {
6360 if (strKey.equals(paConfig->pszKey))
6361 return true;
6362 paConfig++;
6363 }
6364 }
6365 }
6366
6367 return false;
6368}
6369
6370/**
6371 * Returns the last error message collected by the i_vdErrorCall callback and
6372 * resets it.
6373 *
6374 * The error message is returned prepended with a dot and a space, like this:
6375 * <code>
6376 * ". <error_text> (%Rrc)"
6377 * </code>
6378 * to make it easily appendable to a more general error message. The @c %Rrc
6379 * format string is given @a aVRC as an argument.
6380 *
6381 * If there is no last error message collected by i_vdErrorCall or if it is a
6382 * null or empty string, then this function returns the following text:
6383 * <code>
6384 * " (%Rrc)"
6385 * </code>
6386 *
6387 * @note Doesn't do any object locking; it is assumed that the caller makes sure
6388 * the callback isn't called by more than one thread at a time.
6389 *
6390 * @param aVRC VBox error code to use when no error message is provided.
6391 */
6392Utf8Str Medium::i_vdError(int aVRC)
6393{
6394 Utf8Str error;
6395
6396 if (m->vdError.isEmpty())
6397 error = Utf8StrFmt(" (%Rrc)", aVRC);
6398 else
6399 error = Utf8StrFmt(".\n%s", m->vdError.c_str());
6400
6401 m->vdError.setNull();
6402
6403 return error;
6404}
6405
6406/**
6407 * Error message callback.
6408 *
6409 * Puts the reported error message to the m->vdError field.
6410 *
6411 * @note Doesn't do any object locking; it is assumed that the caller makes sure
6412 * the callback isn't called by more than one thread at a time.
6413 *
6414 * @param pvUser The opaque data passed on container creation.
6415 * @param rc The VBox error code.
6416 * @param RT_SRC_POS_DECL Use RT_SRC_POS.
6417 * @param pszFormat Error message format string.
6418 * @param va Error message arguments.
6419 */
6420/*static*/
6421DECLCALLBACK(void) Medium::i_vdErrorCall(void *pvUser, int rc, RT_SRC_POS_DECL,
6422 const char *pszFormat, va_list va)
6423{
6424 NOREF(pszFile); NOREF(iLine); NOREF(pszFunction); /* RT_SRC_POS_DECL */
6425
6426 Medium *that = static_cast<Medium*>(pvUser);
6427 AssertReturnVoid(that != NULL);
6428
6429 if (that->m->vdError.isEmpty())
6430 that->m->vdError =
6431 Utf8StrFmt("%s (%Rrc)", Utf8Str(pszFormat, va).c_str(), rc);
6432 else
6433 that->m->vdError =
6434 Utf8StrFmt("%s.\n%s (%Rrc)", that->m->vdError.c_str(),
6435 Utf8Str(pszFormat, va).c_str(), rc);
6436}
6437
6438/* static */
6439DECLCALLBACK(bool) Medium::i_vdConfigAreKeysValid(void *pvUser,
6440 const char * /* pszzValid */)
6441{
6442 Medium *that = static_cast<Medium*>(pvUser);
6443 AssertReturn(that != NULL, false);
6444
6445 /* we always return true since the only keys we have are those found in
6446 * VDBACKENDINFO */
6447 return true;
6448}
6449
6450/* static */
6451DECLCALLBACK(int) Medium::i_vdConfigQuerySize(void *pvUser,
6452 const char *pszName,
6453 size_t *pcbValue)
6454{
6455 AssertReturn(VALID_PTR(pcbValue), VERR_INVALID_POINTER);
6456
6457 Medium *that = static_cast<Medium*>(pvUser);
6458 AssertReturn(that != NULL, VERR_GENERAL_FAILURE);
6459
6460 settings::StringsMap::const_iterator it = that->m->mapProperties.find(Utf8Str(pszName));
6461 if (it == that->m->mapProperties.end())
6462 return VERR_CFGM_VALUE_NOT_FOUND;
6463
6464 /* we interpret null values as "no value" in Medium */
6465 if (it->second.isEmpty())
6466 return VERR_CFGM_VALUE_NOT_FOUND;
6467
6468 *pcbValue = it->second.length() + 1 /* include terminator */;
6469
6470 return VINF_SUCCESS;
6471}
6472
6473/* static */
6474DECLCALLBACK(int) Medium::i_vdConfigQuery(void *pvUser,
6475 const char *pszName,
6476 char *pszValue,
6477 size_t cchValue)
6478{
6479 AssertReturn(VALID_PTR(pszValue), VERR_INVALID_POINTER);
6480
6481 Medium *that = static_cast<Medium*>(pvUser);
6482 AssertReturn(that != NULL, VERR_GENERAL_FAILURE);
6483
6484 settings::StringsMap::const_iterator it = that->m->mapProperties.find(Utf8Str(pszName));
6485 if (it == that->m->mapProperties.end())
6486 return VERR_CFGM_VALUE_NOT_FOUND;
6487
6488 /* we interpret null values as "no value" in Medium */
6489 if (it->second.isEmpty())
6490 return VERR_CFGM_VALUE_NOT_FOUND;
6491
6492 const Utf8Str &value = it->second;
6493 if (value.length() >= cchValue)
6494 return VERR_CFGM_NOT_ENOUGH_SPACE;
6495
6496 memcpy(pszValue, value.c_str(), value.length() + 1);
6497
6498 return VINF_SUCCESS;
6499}
6500
6501DECLCALLBACK(int) Medium::i_vdTcpSocketCreate(uint32_t fFlags, PVDSOCKET pSock)
6502{
6503 PVDSOCKETINT pSocketInt = NULL;
6504
6505 if ((fFlags & VD_INTERFACETCPNET_CONNECT_EXTENDED_SELECT) != 0)
6506 return VERR_NOT_SUPPORTED;
6507
6508 pSocketInt = (PVDSOCKETINT)RTMemAllocZ(sizeof(VDSOCKETINT));
6509 if (!pSocketInt)
6510 return VERR_NO_MEMORY;
6511
6512 pSocketInt->hSocket = NIL_RTSOCKET;
6513 *pSock = pSocketInt;
6514 return VINF_SUCCESS;
6515}
6516
6517DECLCALLBACK(int) Medium::i_vdTcpSocketDestroy(VDSOCKET Sock)
6518{
6519 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
6520
6521 if (pSocketInt->hSocket != NIL_RTSOCKET)
6522 RTTcpClientCloseEx(pSocketInt->hSocket, false /*fGracefulShutdown*/);
6523
6524 RTMemFree(pSocketInt);
6525
6526 return VINF_SUCCESS;
6527}
6528
6529DECLCALLBACK(int) Medium::i_vdTcpClientConnect(VDSOCKET Sock, const char *pszAddress, uint32_t uPort)
6530{
6531 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
6532
6533 return RTTcpClientConnect(pszAddress, uPort, &pSocketInt->hSocket);
6534}
6535
6536DECLCALLBACK(int) Medium::i_vdTcpClientClose(VDSOCKET Sock)
6537{
6538 int rc = VINF_SUCCESS;
6539 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
6540
6541 rc = RTTcpClientCloseEx(pSocketInt->hSocket, false /*fGracefulShutdown*/);
6542 pSocketInt->hSocket = NIL_RTSOCKET;
6543 return rc;
6544}
6545
6546DECLCALLBACK(bool) Medium::i_vdTcpIsClientConnected(VDSOCKET Sock)
6547{
6548 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
6549 return pSocketInt->hSocket != NIL_RTSOCKET;
6550}
6551
6552DECLCALLBACK(int) Medium::i_vdTcpSelectOne(VDSOCKET Sock, RTMSINTERVAL cMillies)
6553{
6554 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
6555 return RTTcpSelectOne(pSocketInt->hSocket, cMillies);
6556}
6557
6558DECLCALLBACK(int) Medium::i_vdTcpRead(VDSOCKET Sock, void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
6559{
6560 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
6561 return RTTcpRead(pSocketInt->hSocket, pvBuffer, cbBuffer, pcbRead);
6562}
6563
6564DECLCALLBACK(int) Medium::i_vdTcpWrite(VDSOCKET Sock, const void *pvBuffer, size_t cbBuffer)
6565{
6566 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
6567 return RTTcpWrite(pSocketInt->hSocket, pvBuffer, cbBuffer);
6568}
6569
6570DECLCALLBACK(int) Medium::i_vdTcpSgWrite(VDSOCKET Sock, PCRTSGBUF pSgBuf)
6571{
6572 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
6573 return RTTcpSgWrite(pSocketInt->hSocket, pSgBuf);
6574}
6575
6576DECLCALLBACK(int) Medium::i_vdTcpFlush(VDSOCKET Sock)
6577{
6578 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
6579 return RTTcpFlush(pSocketInt->hSocket);
6580}
6581
6582DECLCALLBACK(int) Medium::i_vdTcpSetSendCoalescing(VDSOCKET Sock, bool fEnable)
6583{
6584 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
6585 return RTTcpSetSendCoalescing(pSocketInt->hSocket, fEnable);
6586}
6587
6588DECLCALLBACK(int) Medium::i_vdTcpGetLocalAddress(VDSOCKET Sock, PRTNETADDR pAddr)
6589{
6590 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
6591 return RTTcpGetLocalAddress(pSocketInt->hSocket, pAddr);
6592}
6593
6594DECLCALLBACK(int) Medium::i_vdTcpGetPeerAddress(VDSOCKET Sock, PRTNETADDR pAddr)
6595{
6596 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
6597 return RTTcpGetPeerAddress(pSocketInt->hSocket, pAddr);
6598}
6599
6600/**
6601 * Starts a new thread driven by the appropriate Medium::Task::handler() method.
6602 *
6603 * @note When the task is executed by this method, IProgress::notifyComplete()
6604 * is automatically called for the progress object associated with this
6605 * task when the task is finished to signal the operation completion for
6606 * other threads asynchronously waiting for it.
6607 */
6608HRESULT Medium::i_startThread(Medium::Task *pTask)
6609{
6610#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
6611 /* Extreme paranoia: The calling thread should not hold the medium
6612 * tree lock or any medium lock. Since there is no separate lock class
6613 * for medium objects be even more strict: no other object locks. */
6614 Assert(!AutoLockHoldsLocksInClass(LOCKCLASS_LISTOFMEDIA));
6615 Assert(!AutoLockHoldsLocksInClass(getLockingClass()));
6616#endif
6617
6618 /// @todo use a more descriptive task name
6619 int vrc = RTThreadCreate(NULL, Medium::Task::fntMediumTask, pTask,
6620 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
6621 "Medium::Task");
6622 if (RT_FAILURE(vrc))
6623 {
6624 delete pTask;
6625 return setError(E_FAIL, "Could not create Medium::Task thread (%Rrc)\n", vrc);
6626 }
6627
6628 return S_OK;
6629}
6630
6631/**
6632 * Runs Medium::Task::handler() on the current thread instead of creating
6633 * a new one.
6634 *
6635 * This call implies that it is made on another temporary thread created for
6636 * some asynchronous task. Avoid calling it from a normal thread since the task
6637 * operations are potentially lengthy and will block the calling thread in this
6638 * case.
6639 *
6640 * @note When the task is executed by this method, IProgress::notifyComplete()
6641 * is not called for the progress object associated with this task when
6642 * the task is finished. Instead, the result of the operation is returned
6643 * by this method directly and it's the caller's responsibility to
6644 * complete the progress object in this case.
6645 */
6646HRESULT Medium::i_runNow(Medium::Task *pTask)
6647{
6648#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
6649 /* Extreme paranoia: The calling thread should not hold the medium
6650 * tree lock or any medium lock. Since there is no separate lock class
6651 * for medium objects be even more strict: no other object locks. */
6652 Assert(!AutoLockHoldsLocksInClass(LOCKCLASS_LISTOFMEDIA));
6653 Assert(!AutoLockHoldsLocksInClass(getLockingClass()));
6654#endif
6655
6656 /* NIL_RTTHREAD indicates synchronous call. */
6657 return (HRESULT)Medium::Task::fntMediumTask(NIL_RTTHREAD, pTask);
6658}
6659
6660/**
6661 * Implementation code for the "create base" task.
6662 *
6663 * This only gets started from Medium::CreateBaseStorage() and always runs
6664 * asynchronously. As a result, we always save the VirtualBox.xml file when
6665 * we're done here.
6666 *
6667 * @param task
6668 * @return
6669 */
6670HRESULT Medium::i_taskCreateBaseHandler(Medium::CreateBaseTask &task)
6671{
6672 HRESULT rc = S_OK;
6673
6674 /* these parameters we need after creation */
6675 uint64_t size = 0, logicalSize = 0;
6676 MediumVariant_T variant = MediumVariant_Standard;
6677 bool fGenerateUuid = false;
6678
6679 try
6680 {
6681 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
6682
6683 /* The object may request a specific UUID (through a special form of
6684 * the setLocation() argument). Otherwise we have to generate it */
6685 Guid id = m->id;
6686
6687 fGenerateUuid = id.isZero();
6688 if (fGenerateUuid)
6689 {
6690 id.create();
6691 /* VirtualBox::registerMedium() will need UUID */
6692 unconst(m->id) = id;
6693 }
6694
6695 Utf8Str format(m->strFormat);
6696 Utf8Str location(m->strLocationFull);
6697 uint64_t capabilities = m->formatObj->i_getCapabilities();
6698 ComAssertThrow(capabilities & ( MediumFormatCapabilities_CreateFixed
6699 | MediumFormatCapabilities_CreateDynamic), E_FAIL);
6700 Assert(m->state == MediumState_Creating);
6701
6702 PVBOXHDD hdd;
6703 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
6704 ComAssertRCThrow(vrc, E_FAIL);
6705
6706 /* unlock before the potentially lengthy operation */
6707 thisLock.release();
6708
6709 try
6710 {
6711 /* ensure the directory exists */
6712 if (capabilities & MediumFormatCapabilities_File)
6713 {
6714 rc = VirtualBox::i_ensureFilePathExists(location, !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
6715 if (FAILED(rc))
6716 throw rc;
6717 }
6718
6719 VDGEOMETRY geo = { 0, 0, 0 }; /* auto-detect */
6720
6721 vrc = VDCreateBase(hdd,
6722 format.c_str(),
6723 location.c_str(),
6724 task.mSize,
6725 task.mVariant & ~MediumVariant_NoCreateDir,
6726 NULL,
6727 &geo,
6728 &geo,
6729 id.raw(),
6730 VD_OPEN_FLAGS_NORMAL | m->uOpenFlagsDef,
6731 m->vdImageIfaces,
6732 task.mVDOperationIfaces);
6733 if (RT_FAILURE(vrc))
6734 throw setError(VBOX_E_FILE_ERROR,
6735 tr("Could not create the medium storage unit '%s'%s"),
6736 location.c_str(), i_vdError(vrc).c_str());
6737
6738 size = VDGetFileSize(hdd, 0);
6739 logicalSize = VDGetSize(hdd, 0);
6740 unsigned uImageFlags;
6741 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
6742 if (RT_SUCCESS(vrc))
6743 variant = (MediumVariant_T)uImageFlags;
6744 }
6745 catch (HRESULT aRC) { rc = aRC; }
6746
6747 VDDestroy(hdd);
6748 }
6749 catch (HRESULT aRC) { rc = aRC; }
6750
6751 if (SUCCEEDED(rc))
6752 {
6753 /* register with mVirtualBox as the last step and move to
6754 * Created state only on success (leaving an orphan file is
6755 * better than breaking media registry consistency) */
6756 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
6757 ComObjPtr<Medium> pMedium;
6758 rc = m->pVirtualBox->i_registerMedium(this, &pMedium, DeviceType_HardDisk, treeLock);
6759 Assert(this == pMedium);
6760 }
6761
6762 // re-acquire the lock before changing state
6763 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
6764
6765 if (SUCCEEDED(rc))
6766 {
6767 m->state = MediumState_Created;
6768
6769 m->size = size;
6770 m->logicalSize = logicalSize;
6771 m->variant = variant;
6772
6773 thisLock.release();
6774 i_markRegistriesModified();
6775 if (task.isAsync())
6776 {
6777 // in asynchronous mode, save settings now
6778 m->pVirtualBox->i_saveModifiedRegistries();
6779 }
6780 }
6781 else
6782 {
6783 /* back to NotCreated on failure */
6784 m->state = MediumState_NotCreated;
6785
6786 /* reset UUID to prevent it from being reused next time */
6787 if (fGenerateUuid)
6788 unconst(m->id).clear();
6789 }
6790
6791 return rc;
6792}
6793
6794/**
6795 * Implementation code for the "create diff" task.
6796 *
6797 * This task always gets started from Medium::createDiffStorage() and can run
6798 * synchronously or asynchronously depending on the "wait" parameter passed to
6799 * that function. If we run synchronously, the caller expects the medium
6800 * registry modification to be set before returning; otherwise (in asynchronous
6801 * mode), we save the settings ourselves.
6802 *
6803 * @param task
6804 * @return
6805 */
6806HRESULT Medium::i_taskCreateDiffHandler(Medium::CreateDiffTask &task)
6807{
6808 HRESULT rcTmp = S_OK;
6809
6810 const ComObjPtr<Medium> &pTarget = task.mTarget;
6811
6812 uint64_t size = 0, logicalSize = 0;
6813 MediumVariant_T variant = MediumVariant_Standard;
6814 bool fGenerateUuid = false;
6815
6816 try
6817 {
6818 /* Lock both in {parent,child} order. */
6819 AutoMultiWriteLock2 mediaLock(this, pTarget COMMA_LOCKVAL_SRC_POS);
6820
6821 /* The object may request a specific UUID (through a special form of
6822 * the setLocation() argument). Otherwise we have to generate it */
6823 Guid targetId = pTarget->m->id;
6824
6825 fGenerateUuid = targetId.isZero();
6826 if (fGenerateUuid)
6827 {
6828 targetId.create();
6829 /* VirtualBox::registerMedium() will need UUID */
6830 unconst(pTarget->m->id) = targetId;
6831 }
6832
6833 Guid id = m->id;
6834
6835 Utf8Str targetFormat(pTarget->m->strFormat);
6836 Utf8Str targetLocation(pTarget->m->strLocationFull);
6837 uint64_t capabilities = pTarget->m->formatObj->i_getCapabilities();
6838 ComAssertThrow(capabilities & MediumFormatCapabilities_CreateDynamic, E_FAIL);
6839
6840 Assert(pTarget->m->state == MediumState_Creating);
6841 Assert(m->state == MediumState_LockedRead);
6842
6843 PVBOXHDD hdd;
6844 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
6845 ComAssertRCThrow(vrc, E_FAIL);
6846
6847 /* the two media are now protected by their non-default states;
6848 * unlock the media before the potentially lengthy operation */
6849 mediaLock.release();
6850
6851 try
6852 {
6853 /* Open all media in the target chain but the last. */
6854 MediumLockList::Base::const_iterator targetListBegin =
6855 task.mpMediumLockList->GetBegin();
6856 MediumLockList::Base::const_iterator targetListEnd =
6857 task.mpMediumLockList->GetEnd();
6858 for (MediumLockList::Base::const_iterator it = targetListBegin;
6859 it != targetListEnd;
6860 ++it)
6861 {
6862 const MediumLock &mediumLock = *it;
6863 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
6864
6865 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
6866
6867 /* Skip over the target diff medium */
6868 if (pMedium->m->state == MediumState_Creating)
6869 continue;
6870
6871 /* sanity check */
6872 Assert(pMedium->m->state == MediumState_LockedRead);
6873
6874 /* Open all media in appropriate mode. */
6875 vrc = VDOpen(hdd,
6876 pMedium->m->strFormat.c_str(),
6877 pMedium->m->strLocationFull.c_str(),
6878 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
6879 pMedium->m->vdImageIfaces);
6880 if (RT_FAILURE(vrc))
6881 throw setError(VBOX_E_FILE_ERROR,
6882 tr("Could not open the medium storage unit '%s'%s"),
6883 pMedium->m->strLocationFull.c_str(),
6884 i_vdError(vrc).c_str());
6885 }
6886
6887 /* ensure the target directory exists */
6888 if (capabilities & MediumFormatCapabilities_File)
6889 {
6890 HRESULT rc = VirtualBox::i_ensureFilePathExists(targetLocation,
6891 !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
6892 if (FAILED(rc))
6893 throw rc;
6894 }
6895
6896 vrc = VDCreateDiff(hdd,
6897 targetFormat.c_str(),
6898 targetLocation.c_str(),
6899 (task.mVariant & ~MediumVariant_NoCreateDir) | VD_IMAGE_FLAGS_DIFF,
6900 NULL,
6901 targetId.raw(),
6902 id.raw(),
6903 VD_OPEN_FLAGS_NORMAL | m->uOpenFlagsDef,
6904 pTarget->m->vdImageIfaces,
6905 task.mVDOperationIfaces);
6906 if (RT_FAILURE(vrc))
6907 throw setError(VBOX_E_FILE_ERROR,
6908 tr("Could not create the differencing medium storage unit '%s'%s"),
6909 targetLocation.c_str(), i_vdError(vrc).c_str());
6910
6911 size = VDGetFileSize(hdd, VD_LAST_IMAGE);
6912 logicalSize = VDGetSize(hdd, VD_LAST_IMAGE);
6913 unsigned uImageFlags;
6914 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
6915 if (RT_SUCCESS(vrc))
6916 variant = (MediumVariant_T)uImageFlags;
6917 }
6918 catch (HRESULT aRC) { rcTmp = aRC; }
6919
6920 VDDestroy(hdd);
6921 }
6922 catch (HRESULT aRC) { rcTmp = aRC; }
6923
6924 MultiResult mrc(rcTmp);
6925
6926 if (SUCCEEDED(mrc))
6927 {
6928 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
6929
6930 Assert(pTarget->m->pParent.isNull());
6931
6932 /* associate the child with the parent */
6933 pTarget->m->pParent = this;
6934 m->llChildren.push_back(pTarget);
6935
6936 /** @todo r=klaus neither target nor base() are locked,
6937 * potential race! */
6938 /* diffs for immutable media are auto-reset by default */
6939 pTarget->m->autoReset = (i_getBase()->m->type == MediumType_Immutable);
6940
6941 /* register with mVirtualBox as the last step and move to
6942 * Created state only on success (leaving an orphan file is
6943 * better than breaking media registry consistency) */
6944 ComObjPtr<Medium> pMedium;
6945 mrc = m->pVirtualBox->i_registerMedium(pTarget, &pMedium, DeviceType_HardDisk, treeLock);
6946 Assert(pTarget == pMedium);
6947
6948 if (FAILED(mrc))
6949 /* break the parent association on failure to register */
6950 i_deparent();
6951 }
6952
6953 AutoMultiWriteLock2 mediaLock(this, pTarget COMMA_LOCKVAL_SRC_POS);
6954
6955 if (SUCCEEDED(mrc))
6956 {
6957 pTarget->m->state = MediumState_Created;
6958
6959 pTarget->m->size = size;
6960 pTarget->m->logicalSize = logicalSize;
6961 pTarget->m->variant = variant;
6962 }
6963 else
6964 {
6965 /* back to NotCreated on failure */
6966 pTarget->m->state = MediumState_NotCreated;
6967
6968 pTarget->m->autoReset = false;
6969
6970 /* reset UUID to prevent it from being reused next time */
6971 if (fGenerateUuid)
6972 unconst(pTarget->m->id).clear();
6973 }
6974
6975 // deregister the task registered in createDiffStorage()
6976 Assert(m->numCreateDiffTasks != 0);
6977 --m->numCreateDiffTasks;
6978
6979 mediaLock.release();
6980 i_markRegistriesModified();
6981 if (task.isAsync())
6982 {
6983 // in asynchronous mode, save settings now
6984 m->pVirtualBox->i_saveModifiedRegistries();
6985 }
6986
6987 /* Note that in sync mode, it's the caller's responsibility to
6988 * unlock the medium. */
6989
6990 return mrc;
6991}
6992
6993/**
6994 * Implementation code for the "merge" task.
6995 *
6996 * This task always gets started from Medium::mergeTo() and can run
6997 * synchronously or asynchronously depending on the "wait" parameter passed to
6998 * that function. If we run synchronously, the caller expects the medium
6999 * registry modification to be set before returning; otherwise (in asynchronous
7000 * mode), we save the settings ourselves.
7001 *
7002 * @param task
7003 * @return
7004 */
7005HRESULT Medium::i_taskMergeHandler(Medium::MergeTask &task)
7006{
7007 HRESULT rcTmp = S_OK;
7008
7009 const ComObjPtr<Medium> &pTarget = task.mTarget;
7010
7011 try
7012 {
7013 PVBOXHDD hdd;
7014 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
7015 ComAssertRCThrow(vrc, E_FAIL);
7016
7017 try
7018 {
7019 // Similar code appears in SessionMachine::onlineMergeMedium, so
7020 // if you make any changes below check whether they are applicable
7021 // in that context as well.
7022
7023 unsigned uTargetIdx = VD_LAST_IMAGE;
7024 unsigned uSourceIdx = VD_LAST_IMAGE;
7025 /* Open all media in the chain. */
7026 MediumLockList::Base::iterator lockListBegin =
7027 task.mpMediumLockList->GetBegin();
7028 MediumLockList::Base::iterator lockListEnd =
7029 task.mpMediumLockList->GetEnd();
7030 unsigned i = 0;
7031 for (MediumLockList::Base::iterator it = lockListBegin;
7032 it != lockListEnd;
7033 ++it)
7034 {
7035 MediumLock &mediumLock = *it;
7036 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
7037
7038 if (pMedium == this)
7039 uSourceIdx = i;
7040 else if (pMedium == pTarget)
7041 uTargetIdx = i;
7042
7043 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
7044
7045 /*
7046 * complex sanity (sane complexity)
7047 *
7048 * The current medium must be in the Deleting (medium is merged)
7049 * or LockedRead (parent medium) state if it is not the target.
7050 * If it is the target it must be in the LockedWrite state.
7051 */
7052 Assert( ( pMedium != pTarget
7053 && ( pMedium->m->state == MediumState_Deleting
7054 || pMedium->m->state == MediumState_LockedRead))
7055 || ( pMedium == pTarget
7056 && pMedium->m->state == MediumState_LockedWrite));
7057
7058 /*
7059 * Medium must be the target, in the LockedRead state
7060 * or Deleting state where it is not allowed to be attached
7061 * to a virtual machine.
7062 */
7063 Assert( pMedium == pTarget
7064 || pMedium->m->state == MediumState_LockedRead
7065 || ( pMedium->m->backRefs.size() == 0
7066 && pMedium->m->state == MediumState_Deleting));
7067 /* The source medium must be in Deleting state. */
7068 Assert( pMedium != this
7069 || pMedium->m->state == MediumState_Deleting);
7070
7071 unsigned uOpenFlags = VD_OPEN_FLAGS_NORMAL;
7072
7073 if ( pMedium->m->state == MediumState_LockedRead
7074 || pMedium->m->state == MediumState_Deleting)
7075 uOpenFlags = VD_OPEN_FLAGS_READONLY;
7076 if (pMedium->m->type == MediumType_Shareable)
7077 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
7078
7079 /* Open the medium */
7080 vrc = VDOpen(hdd,
7081 pMedium->m->strFormat.c_str(),
7082 pMedium->m->strLocationFull.c_str(),
7083 uOpenFlags | m->uOpenFlagsDef,
7084 pMedium->m->vdImageIfaces);
7085 if (RT_FAILURE(vrc))
7086 throw vrc;
7087
7088 i++;
7089 }
7090
7091 ComAssertThrow( uSourceIdx != VD_LAST_IMAGE
7092 && uTargetIdx != VD_LAST_IMAGE, E_FAIL);
7093
7094 vrc = VDMerge(hdd, uSourceIdx, uTargetIdx,
7095 task.mVDOperationIfaces);
7096 if (RT_FAILURE(vrc))
7097 throw vrc;
7098
7099 /* update parent UUIDs */
7100 if (!task.mfMergeForward)
7101 {
7102 /* we need to update UUIDs of all source's children
7103 * which cannot be part of the container at once so
7104 * add each one in there individually */
7105 if (task.mpChildrenToReparent)
7106 {
7107 MediumLockList::Base::iterator childrenBegin = task.mpChildrenToReparent->GetBegin();
7108 MediumLockList::Base::iterator childrenEnd = task.mpChildrenToReparent->GetEnd();
7109 for (MediumLockList::Base::iterator it = childrenBegin;
7110 it != childrenEnd;
7111 ++it)
7112 {
7113 Medium *pMedium = it->GetMedium();
7114 /* VD_OPEN_FLAGS_INFO since UUID is wrong yet */
7115 vrc = VDOpen(hdd,
7116 pMedium->m->strFormat.c_str(),
7117 pMedium->m->strLocationFull.c_str(),
7118 VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
7119 pMedium->m->vdImageIfaces);
7120 if (RT_FAILURE(vrc))
7121 throw vrc;
7122
7123 vrc = VDSetParentUuid(hdd, VD_LAST_IMAGE,
7124 pTarget->m->id.raw());
7125 if (RT_FAILURE(vrc))
7126 throw vrc;
7127
7128 vrc = VDClose(hdd, false /* fDelete */);
7129 if (RT_FAILURE(vrc))
7130 throw vrc;
7131 }
7132 }
7133 }
7134 }
7135 catch (HRESULT aRC) { rcTmp = aRC; }
7136 catch (int aVRC)
7137 {
7138 rcTmp = setError(VBOX_E_FILE_ERROR,
7139 tr("Could not merge the medium '%s' to '%s'%s"),
7140 m->strLocationFull.c_str(),
7141 pTarget->m->strLocationFull.c_str(),
7142 i_vdError(aVRC).c_str());
7143 }
7144
7145 VDDestroy(hdd);
7146 }
7147 catch (HRESULT aRC) { rcTmp = aRC; }
7148
7149 ErrorInfoKeeper eik;
7150 MultiResult mrc(rcTmp);
7151 HRESULT rc2;
7152
7153 if (SUCCEEDED(mrc))
7154 {
7155 /* all media but the target were successfully deleted by
7156 * VDMerge; reparent the last one and uninitialize deleted media. */
7157
7158 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
7159
7160 if (task.mfMergeForward)
7161 {
7162 /* first, unregister the target since it may become a base
7163 * medium which needs re-registration */
7164 rc2 = m->pVirtualBox->i_unregisterMedium(pTarget);
7165 AssertComRC(rc2);
7166
7167 /* then, reparent it and disconnect the deleted branch at
7168 * both ends (chain->parent() is source's parent) */
7169 pTarget->i_deparent();
7170 pTarget->m->pParent = task.mParentForTarget;
7171 if (pTarget->m->pParent)
7172 {
7173 pTarget->m->pParent->m->llChildren.push_back(pTarget);
7174 i_deparent();
7175 }
7176
7177 /* then, register again */
7178 ComObjPtr<Medium> pMedium;
7179 rc2 = m->pVirtualBox->i_registerMedium(pTarget, &pMedium,
7180 DeviceType_HardDisk,
7181 treeLock);
7182 AssertComRC(rc2);
7183 }
7184 else
7185 {
7186 Assert(pTarget->i_getChildren().size() == 1);
7187 Medium *targetChild = pTarget->i_getChildren().front();
7188
7189 /* disconnect the deleted branch at the elder end */
7190 targetChild->i_deparent();
7191
7192 /* reparent source's children and disconnect the deleted
7193 * branch at the younger end */
7194 if (task.mpChildrenToReparent)
7195 {
7196 /* obey {parent,child} lock order */
7197 AutoWriteLock sourceLock(this COMMA_LOCKVAL_SRC_POS);
7198
7199 MediumLockList::Base::iterator childrenBegin = task.mpChildrenToReparent->GetBegin();
7200 MediumLockList::Base::iterator childrenEnd = task.mpChildrenToReparent->GetEnd();
7201 for (MediumLockList::Base::iterator it = childrenBegin;
7202 it != childrenEnd;
7203 ++it)
7204 {
7205 Medium *pMedium = it->GetMedium();
7206 AutoWriteLock childLock(pMedium COMMA_LOCKVAL_SRC_POS);
7207
7208 pMedium->i_deparent(); // removes pMedium from source
7209 pMedium->i_setParent(pTarget);
7210 }
7211 }
7212 }
7213
7214 /* unregister and uninitialize all media removed by the merge */
7215 MediumLockList::Base::iterator lockListBegin =
7216 task.mpMediumLockList->GetBegin();
7217 MediumLockList::Base::iterator lockListEnd =
7218 task.mpMediumLockList->GetEnd();
7219 for (MediumLockList::Base::iterator it = lockListBegin;
7220 it != lockListEnd;
7221 )
7222 {
7223 MediumLock &mediumLock = *it;
7224 /* Create a real copy of the medium pointer, as the medium
7225 * lock deletion below would invalidate the referenced object. */
7226 const ComObjPtr<Medium> pMedium = mediumLock.GetMedium();
7227
7228 /* The target and all media not merged (readonly) are skipped */
7229 if ( pMedium == pTarget
7230 || pMedium->m->state == MediumState_LockedRead)
7231 {
7232 ++it;
7233 continue;
7234 }
7235
7236 rc2 = pMedium->m->pVirtualBox->i_unregisterMedium(pMedium);
7237 AssertComRC(rc2);
7238
7239 /* now, uninitialize the deleted medium (note that
7240 * due to the Deleting state, uninit() will not touch
7241 * the parent-child relationship so we need to
7242 * uninitialize each disk individually) */
7243
7244 /* note that the operation initiator medium (which is
7245 * normally also the source medium) is a special case
7246 * -- there is one more caller added by Task to it which
7247 * we must release. Also, if we are in sync mode, the
7248 * caller may still hold an AutoCaller instance for it
7249 * and therefore we cannot uninit() it (it's therefore
7250 * the caller's responsibility) */
7251 if (pMedium == this)
7252 {
7253 Assert(i_getChildren().size() == 0);
7254 Assert(m->backRefs.size() == 0);
7255 task.mMediumCaller.release();
7256 }
7257
7258 /* Delete the medium lock list entry, which also releases the
7259 * caller added by MergeChain before uninit() and updates the
7260 * iterator to point to the right place. */
7261 rc2 = task.mpMediumLockList->RemoveByIterator(it);
7262 AssertComRC(rc2);
7263
7264 if (task.isAsync() || pMedium != this)
7265 {
7266 treeLock.release();
7267 pMedium->uninit();
7268 treeLock.acquire();
7269 }
7270 }
7271 }
7272
7273 i_markRegistriesModified();
7274 if (task.isAsync())
7275 {
7276 // in asynchronous mode, save settings now
7277 eik.restore();
7278 m->pVirtualBox->i_saveModifiedRegistries();
7279 eik.fetch();
7280 }
7281
7282 if (FAILED(mrc))
7283 {
7284 /* Here we come if either VDMerge() failed (in which case we
7285 * assume that it tried to do everything to make a further
7286 * retry possible -- e.g. not deleted intermediate media
7287 * and so on) or VirtualBox::saveRegistries() failed (where we
7288 * should have the original tree but with intermediate storage
7289 * units deleted by VDMerge()). We have to only restore states
7290 * (through the MergeChain dtor) unless we are run synchronously
7291 * in which case it's the responsibility of the caller as stated
7292 * in the mergeTo() docs. The latter also implies that we
7293 * don't own the merge chain, so release it in this case. */
7294 if (task.isAsync())
7295 i_cancelMergeTo(task.mpChildrenToReparent, task.mpMediumLockList);
7296 }
7297
7298 return mrc;
7299}
7300
7301/**
7302 * Implementation code for the "clone" task.
7303 *
7304 * This only gets started from Medium::CloneTo() and always runs asynchronously.
7305 * As a result, we always save the VirtualBox.xml file when we're done here.
7306 *
7307 * @param task
7308 * @return
7309 */
7310HRESULT Medium::i_taskCloneHandler(Medium::CloneTask &task)
7311{
7312 HRESULT rcTmp = S_OK;
7313
7314 const ComObjPtr<Medium> &pTarget = task.mTarget;
7315 const ComObjPtr<Medium> &pParent = task.mParent;
7316
7317 bool fCreatingTarget = false;
7318
7319 uint64_t size = 0, logicalSize = 0;
7320 MediumVariant_T variant = MediumVariant_Standard;
7321 bool fGenerateUuid = false;
7322
7323 try
7324 {
7325 /* Lock all in {parent,child} order. The lock is also used as a
7326 * signal from the task initiator (which releases it only after
7327 * RTThreadCreate()) that we can start the job. */
7328 AutoMultiWriteLock3 thisLock(this, pTarget, pParent COMMA_LOCKVAL_SRC_POS);
7329
7330 fCreatingTarget = pTarget->m->state == MediumState_Creating;
7331
7332 /* The object may request a specific UUID (through a special form of
7333 * the setLocation() argument). Otherwise we have to generate it */
7334 Guid targetId = pTarget->m->id;
7335
7336 fGenerateUuid = targetId.isZero();
7337 if (fGenerateUuid)
7338 {
7339 targetId.create();
7340 /* VirtualBox::registerMedium() will need UUID */
7341 unconst(pTarget->m->id) = targetId;
7342 }
7343
7344 PVBOXHDD hdd;
7345 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
7346 ComAssertRCThrow(vrc, E_FAIL);
7347
7348 try
7349 {
7350 /* Open all media in the source chain. */
7351 MediumLockList::Base::const_iterator sourceListBegin =
7352 task.mpSourceMediumLockList->GetBegin();
7353 MediumLockList::Base::const_iterator sourceListEnd =
7354 task.mpSourceMediumLockList->GetEnd();
7355 for (MediumLockList::Base::const_iterator it = sourceListBegin;
7356 it != sourceListEnd;
7357 ++it)
7358 {
7359 const MediumLock &mediumLock = *it;
7360 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
7361 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
7362
7363 /* sanity check */
7364 Assert(pMedium->m->state == MediumState_LockedRead);
7365
7366 /** Open all media in read-only mode. */
7367 vrc = VDOpen(hdd,
7368 pMedium->m->strFormat.c_str(),
7369 pMedium->m->strLocationFull.c_str(),
7370 VD_OPEN_FLAGS_READONLY | m->uOpenFlagsDef,
7371 pMedium->m->vdImageIfaces);
7372 if (RT_FAILURE(vrc))
7373 throw setError(VBOX_E_FILE_ERROR,
7374 tr("Could not open the medium storage unit '%s'%s"),
7375 pMedium->m->strLocationFull.c_str(),
7376 i_vdError(vrc).c_str());
7377 }
7378
7379 Utf8Str targetFormat(pTarget->m->strFormat);
7380 Utf8Str targetLocation(pTarget->m->strLocationFull);
7381 uint64_t capabilities = pTarget->m->formatObj->i_getCapabilities();
7382
7383 Assert( pTarget->m->state == MediumState_Creating
7384 || pTarget->m->state == MediumState_LockedWrite);
7385 Assert(m->state == MediumState_LockedRead);
7386 Assert( pParent.isNull()
7387 || pParent->m->state == MediumState_LockedRead);
7388
7389 /* unlock before the potentially lengthy operation */
7390 thisLock.release();
7391
7392 /* ensure the target directory exists */
7393 if (capabilities & MediumFormatCapabilities_File)
7394 {
7395 HRESULT rc = VirtualBox::i_ensureFilePathExists(targetLocation,
7396 !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
7397 if (FAILED(rc))
7398 throw rc;
7399 }
7400
7401 PVBOXHDD targetHdd;
7402 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &targetHdd);
7403 ComAssertRCThrow(vrc, E_FAIL);
7404
7405 try
7406 {
7407 /* Open all media in the target chain. */
7408 MediumLockList::Base::const_iterator targetListBegin =
7409 task.mpTargetMediumLockList->GetBegin();
7410 MediumLockList::Base::const_iterator targetListEnd =
7411 task.mpTargetMediumLockList->GetEnd();
7412 for (MediumLockList::Base::const_iterator it = targetListBegin;
7413 it != targetListEnd;
7414 ++it)
7415 {
7416 const MediumLock &mediumLock = *it;
7417 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
7418
7419 /* If the target medium is not created yet there's no
7420 * reason to open it. */
7421 if (pMedium == pTarget && fCreatingTarget)
7422 continue;
7423
7424 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
7425
7426 /* sanity check */
7427 Assert( pMedium->m->state == MediumState_LockedRead
7428 || pMedium->m->state == MediumState_LockedWrite);
7429
7430 unsigned uOpenFlags = VD_OPEN_FLAGS_NORMAL;
7431 if (pMedium->m->state != MediumState_LockedWrite)
7432 uOpenFlags = VD_OPEN_FLAGS_READONLY;
7433 if (pMedium->m->type == MediumType_Shareable)
7434 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
7435
7436 /* Open all media in appropriate mode. */
7437 vrc = VDOpen(targetHdd,
7438 pMedium->m->strFormat.c_str(),
7439 pMedium->m->strLocationFull.c_str(),
7440 uOpenFlags | m->uOpenFlagsDef,
7441 pMedium->m->vdImageIfaces);
7442 if (RT_FAILURE(vrc))
7443 throw setError(VBOX_E_FILE_ERROR,
7444 tr("Could not open the medium storage unit '%s'%s"),
7445 pMedium->m->strLocationFull.c_str(),
7446 i_vdError(vrc).c_str());
7447 }
7448
7449 /** @todo r=klaus target isn't locked, race getting the state */
7450 if (task.midxSrcImageSame == UINT32_MAX)
7451 {
7452 vrc = VDCopy(hdd,
7453 VD_LAST_IMAGE,
7454 targetHdd,
7455 targetFormat.c_str(),
7456 (fCreatingTarget) ? targetLocation.c_str() : (char *)NULL,
7457 false /* fMoveByRename */,
7458 0 /* cbSize */,
7459 task.mVariant & ~MediumVariant_NoCreateDir,
7460 targetId.raw(),
7461 VD_OPEN_FLAGS_NORMAL | m->uOpenFlagsDef,
7462 NULL /* pVDIfsOperation */,
7463 pTarget->m->vdImageIfaces,
7464 task.mVDOperationIfaces);
7465 }
7466 else
7467 {
7468 vrc = VDCopyEx(hdd,
7469 VD_LAST_IMAGE,
7470 targetHdd,
7471 targetFormat.c_str(),
7472 (fCreatingTarget) ? targetLocation.c_str() : (char *)NULL,
7473 false /* fMoveByRename */,
7474 0 /* cbSize */,
7475 task.midxSrcImageSame,
7476 task.midxDstImageSame,
7477 task.mVariant & ~MediumVariant_NoCreateDir,
7478 targetId.raw(),
7479 VD_OPEN_FLAGS_NORMAL | m->uOpenFlagsDef,
7480 NULL /* pVDIfsOperation */,
7481 pTarget->m->vdImageIfaces,
7482 task.mVDOperationIfaces);
7483 }
7484 if (RT_FAILURE(vrc))
7485 throw setError(VBOX_E_FILE_ERROR,
7486 tr("Could not create the clone medium '%s'%s"),
7487 targetLocation.c_str(), i_vdError(vrc).c_str());
7488
7489 size = VDGetFileSize(targetHdd, VD_LAST_IMAGE);
7490 logicalSize = VDGetSize(targetHdd, VD_LAST_IMAGE);
7491 unsigned uImageFlags;
7492 vrc = VDGetImageFlags(targetHdd, 0, &uImageFlags);
7493 if (RT_SUCCESS(vrc))
7494 variant = (MediumVariant_T)uImageFlags;
7495 }
7496 catch (HRESULT aRC) { rcTmp = aRC; }
7497
7498 VDDestroy(targetHdd);
7499 }
7500 catch (HRESULT aRC) { rcTmp = aRC; }
7501
7502 VDDestroy(hdd);
7503 }
7504 catch (HRESULT aRC) { rcTmp = aRC; }
7505
7506 ErrorInfoKeeper eik;
7507 MultiResult mrc(rcTmp);
7508
7509 /* Only do the parent changes for newly created media. */
7510 if (SUCCEEDED(mrc) && fCreatingTarget)
7511 {
7512 /* we set mParent & children() */
7513 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
7514
7515 Assert(pTarget->m->pParent.isNull());
7516
7517 if (pParent)
7518 {
7519 /* associate the clone with the parent and deassociate
7520 * from VirtualBox */
7521 pTarget->m->pParent = pParent;
7522 pParent->m->llChildren.push_back(pTarget);
7523
7524 /* register with mVirtualBox as the last step and move to
7525 * Created state only on success (leaving an orphan file is
7526 * better than breaking media registry consistency) */
7527 eik.restore();
7528 ComObjPtr<Medium> pMedium;
7529 mrc = pParent->m->pVirtualBox->i_registerMedium(pTarget, &pMedium,
7530 DeviceType_HardDisk,
7531 treeLock);
7532 Assert( FAILED(mrc)
7533 || pTarget == pMedium);
7534 eik.fetch();
7535
7536 if (FAILED(mrc))
7537 /* break parent association on failure to register */
7538 pTarget->i_deparent(); // removes target from parent
7539 }
7540 else
7541 {
7542 /* just register */
7543 eik.restore();
7544 ComObjPtr<Medium> pMedium;
7545 mrc = m->pVirtualBox->i_registerMedium(pTarget, &pMedium,
7546 DeviceType_HardDisk,
7547 treeLock);
7548 Assert( FAILED(mrc)
7549 || pTarget == pMedium);
7550 eik.fetch();
7551 }
7552 }
7553
7554 if (fCreatingTarget)
7555 {
7556 AutoWriteLock mLock(pTarget COMMA_LOCKVAL_SRC_POS);
7557
7558 if (SUCCEEDED(mrc))
7559 {
7560 pTarget->m->state = MediumState_Created;
7561
7562 pTarget->m->size = size;
7563 pTarget->m->logicalSize = logicalSize;
7564 pTarget->m->variant = variant;
7565 }
7566 else
7567 {
7568 /* back to NotCreated on failure */
7569 pTarget->m->state = MediumState_NotCreated;
7570
7571 /* reset UUID to prevent it from being reused next time */
7572 if (fGenerateUuid)
7573 unconst(pTarget->m->id).clear();
7574 }
7575 }
7576
7577 // now, at the end of this task (always asynchronous), save the settings
7578 if (SUCCEEDED(mrc))
7579 {
7580 // save the settings
7581 i_markRegistriesModified();
7582 /* collect multiple errors */
7583 eik.restore();
7584 m->pVirtualBox->i_saveModifiedRegistries();
7585 eik.fetch();
7586 }
7587
7588 /* Everything is explicitly unlocked when the task exits,
7589 * as the task destruction also destroys the source chain. */
7590
7591 /* Make sure the source chain is released early. It could happen
7592 * that we get a deadlock in Appliance::Import when Medium::Close
7593 * is called & the source chain is released at the same time. */
7594 task.mpSourceMediumLockList->Clear();
7595
7596 return mrc;
7597}
7598
7599/**
7600 * Implementation code for the "delete" task.
7601 *
7602 * This task always gets started from Medium::deleteStorage() and can run
7603 * synchronously or asynchronously depending on the "wait" parameter passed to
7604 * that function.
7605 *
7606 * @param task
7607 * @return
7608 */
7609HRESULT Medium::i_taskDeleteHandler(Medium::DeleteTask &task)
7610{
7611 NOREF(task);
7612 HRESULT rc = S_OK;
7613
7614 try
7615 {
7616 /* The lock is also used as a signal from the task initiator (which
7617 * releases it only after RTThreadCreate()) that we can start the job */
7618 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
7619
7620 PVBOXHDD hdd;
7621 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
7622 ComAssertRCThrow(vrc, E_FAIL);
7623
7624 Utf8Str format(m->strFormat);
7625 Utf8Str location(m->strLocationFull);
7626
7627 /* unlock before the potentially lengthy operation */
7628 Assert(m->state == MediumState_Deleting);
7629 thisLock.release();
7630
7631 try
7632 {
7633 vrc = VDOpen(hdd,
7634 format.c_str(),
7635 location.c_str(),
7636 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
7637 m->vdImageIfaces);
7638 if (RT_SUCCESS(vrc))
7639 vrc = VDClose(hdd, true /* fDelete */);
7640
7641 if (RT_FAILURE(vrc))
7642 throw setError(VBOX_E_FILE_ERROR,
7643 tr("Could not delete the medium storage unit '%s'%s"),
7644 location.c_str(), i_vdError(vrc).c_str());
7645
7646 }
7647 catch (HRESULT aRC) { rc = aRC; }
7648
7649 VDDestroy(hdd);
7650 }
7651 catch (HRESULT aRC) { rc = aRC; }
7652
7653 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
7654
7655 /* go to the NotCreated state even on failure since the storage
7656 * may have been already partially deleted and cannot be used any
7657 * more. One will be able to manually re-open the storage if really
7658 * needed to re-register it. */
7659 m->state = MediumState_NotCreated;
7660
7661 /* Reset UUID to prevent Create* from reusing it again */
7662 unconst(m->id).clear();
7663
7664 return rc;
7665}
7666
7667/**
7668 * Implementation code for the "reset" task.
7669 *
7670 * This always gets started asynchronously from Medium::Reset().
7671 *
7672 * @param task
7673 * @return
7674 */
7675HRESULT Medium::i_taskResetHandler(Medium::ResetTask &task)
7676{
7677 HRESULT rc = S_OK;
7678
7679 uint64_t size = 0, logicalSize = 0;
7680 MediumVariant_T variant = MediumVariant_Standard;
7681
7682 try
7683 {
7684 /* The lock is also used as a signal from the task initiator (which
7685 * releases it only after RTThreadCreate()) that we can start the job */
7686 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
7687
7688 /// @todo Below we use a pair of delete/create operations to reset
7689 /// the diff contents but the most efficient way will of course be
7690 /// to add a VDResetDiff() API call
7691
7692 PVBOXHDD hdd;
7693 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
7694 ComAssertRCThrow(vrc, E_FAIL);
7695
7696 Guid id = m->id;
7697 Utf8Str format(m->strFormat);
7698 Utf8Str location(m->strLocationFull);
7699
7700 Medium *pParent = m->pParent;
7701 Guid parentId = pParent->m->id;
7702 Utf8Str parentFormat(pParent->m->strFormat);
7703 Utf8Str parentLocation(pParent->m->strLocationFull);
7704
7705 Assert(m->state == MediumState_LockedWrite);
7706
7707 /* unlock before the potentially lengthy operation */
7708 thisLock.release();
7709
7710 try
7711 {
7712 /* Open all media in the target chain but the last. */
7713 MediumLockList::Base::const_iterator targetListBegin =
7714 task.mpMediumLockList->GetBegin();
7715 MediumLockList::Base::const_iterator targetListEnd =
7716 task.mpMediumLockList->GetEnd();
7717 for (MediumLockList::Base::const_iterator it = targetListBegin;
7718 it != targetListEnd;
7719 ++it)
7720 {
7721 const MediumLock &mediumLock = *it;
7722 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
7723
7724 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
7725
7726 /* sanity check, "this" is checked above */
7727 Assert( pMedium == this
7728 || pMedium->m->state == MediumState_LockedRead);
7729
7730 /* Open all media in appropriate mode. */
7731 vrc = VDOpen(hdd,
7732 pMedium->m->strFormat.c_str(),
7733 pMedium->m->strLocationFull.c_str(),
7734 VD_OPEN_FLAGS_READONLY | m->uOpenFlagsDef,
7735 pMedium->m->vdImageIfaces);
7736 if (RT_FAILURE(vrc))
7737 throw setError(VBOX_E_FILE_ERROR,
7738 tr("Could not open the medium storage unit '%s'%s"),
7739 pMedium->m->strLocationFull.c_str(),
7740 i_vdError(vrc).c_str());
7741
7742 /* Done when we hit the media which should be reset */
7743 if (pMedium == this)
7744 break;
7745 }
7746
7747 /* first, delete the storage unit */
7748 vrc = VDClose(hdd, true /* fDelete */);
7749 if (RT_FAILURE(vrc))
7750 throw setError(VBOX_E_FILE_ERROR,
7751 tr("Could not delete the medium storage unit '%s'%s"),
7752 location.c_str(), i_vdError(vrc).c_str());
7753
7754 /* next, create it again */
7755 vrc = VDOpen(hdd,
7756 parentFormat.c_str(),
7757 parentLocation.c_str(),
7758 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
7759 m->vdImageIfaces);
7760 if (RT_FAILURE(vrc))
7761 throw setError(VBOX_E_FILE_ERROR,
7762 tr("Could not open the medium storage unit '%s'%s"),
7763 parentLocation.c_str(), i_vdError(vrc).c_str());
7764
7765 vrc = VDCreateDiff(hdd,
7766 format.c_str(),
7767 location.c_str(),
7768 /// @todo use the same medium variant as before
7769 VD_IMAGE_FLAGS_NONE,
7770 NULL,
7771 id.raw(),
7772 parentId.raw(),
7773 VD_OPEN_FLAGS_NORMAL,
7774 m->vdImageIfaces,
7775 task.mVDOperationIfaces);
7776 if (RT_FAILURE(vrc))
7777 throw setError(VBOX_E_FILE_ERROR,
7778 tr("Could not create the differencing medium storage unit '%s'%s"),
7779 location.c_str(), i_vdError(vrc).c_str());
7780
7781 size = VDGetFileSize(hdd, VD_LAST_IMAGE);
7782 logicalSize = VDGetSize(hdd, VD_LAST_IMAGE);
7783 unsigned uImageFlags;
7784 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
7785 if (RT_SUCCESS(vrc))
7786 variant = (MediumVariant_T)uImageFlags;
7787 }
7788 catch (HRESULT aRC) { rc = aRC; }
7789
7790 VDDestroy(hdd);
7791 }
7792 catch (HRESULT aRC) { rc = aRC; }
7793
7794 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
7795
7796 m->size = size;
7797 m->logicalSize = logicalSize;
7798 m->variant = variant;
7799
7800 /* Everything is explicitly unlocked when the task exits,
7801 * as the task destruction also destroys the media chain. */
7802
7803 return rc;
7804}
7805
7806/**
7807 * Implementation code for the "compact" task.
7808 *
7809 * @param task
7810 * @return
7811 */
7812HRESULT Medium::i_taskCompactHandler(Medium::CompactTask &task)
7813{
7814 HRESULT rc = S_OK;
7815
7816 /* Lock all in {parent,child} order. The lock is also used as a
7817 * signal from the task initiator (which releases it only after
7818 * RTThreadCreate()) that we can start the job. */
7819 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
7820
7821 try
7822 {
7823 PVBOXHDD hdd;
7824 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
7825 ComAssertRCThrow(vrc, E_FAIL);
7826
7827 try
7828 {
7829 /* Open all media in the chain. */
7830 MediumLockList::Base::const_iterator mediumListBegin =
7831 task.mpMediumLockList->GetBegin();
7832 MediumLockList::Base::const_iterator mediumListEnd =
7833 task.mpMediumLockList->GetEnd();
7834 MediumLockList::Base::const_iterator mediumListLast =
7835 mediumListEnd;
7836 mediumListLast--;
7837 for (MediumLockList::Base::const_iterator it = mediumListBegin;
7838 it != mediumListEnd;
7839 ++it)
7840 {
7841 const MediumLock &mediumLock = *it;
7842 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
7843 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
7844
7845 /* sanity check */
7846 if (it == mediumListLast)
7847 Assert(pMedium->m->state == MediumState_LockedWrite);
7848 else
7849 Assert(pMedium->m->state == MediumState_LockedRead);
7850
7851 /* Open all media but last in read-only mode. Do not handle
7852 * shareable media, as compaction and sharing are mutually
7853 * exclusive. */
7854 vrc = VDOpen(hdd,
7855 pMedium->m->strFormat.c_str(),
7856 pMedium->m->strLocationFull.c_str(),
7857 m->uOpenFlagsDef | (it == mediumListLast ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY),
7858 pMedium->m->vdImageIfaces);
7859 if (RT_FAILURE(vrc))
7860 throw setError(VBOX_E_FILE_ERROR,
7861 tr("Could not open the medium storage unit '%s'%s"),
7862 pMedium->m->strLocationFull.c_str(),
7863 i_vdError(vrc).c_str());
7864 }
7865
7866 Assert(m->state == MediumState_LockedWrite);
7867
7868 Utf8Str location(m->strLocationFull);
7869
7870 /* unlock before the potentially lengthy operation */
7871 thisLock.release();
7872
7873 vrc = VDCompact(hdd, VD_LAST_IMAGE, task.mVDOperationIfaces);
7874 if (RT_FAILURE(vrc))
7875 {
7876 if (vrc == VERR_NOT_SUPPORTED)
7877 throw setError(VBOX_E_NOT_SUPPORTED,
7878 tr("Compacting is not yet supported for medium '%s'"),
7879 location.c_str());
7880 else if (vrc == VERR_NOT_IMPLEMENTED)
7881 throw setError(E_NOTIMPL,
7882 tr("Compacting is not implemented, medium '%s'"),
7883 location.c_str());
7884 else
7885 throw setError(VBOX_E_FILE_ERROR,
7886 tr("Could not compact medium '%s'%s"),
7887 location.c_str(),
7888 i_vdError(vrc).c_str());
7889 }
7890 }
7891 catch (HRESULT aRC) { rc = aRC; }
7892
7893 VDDestroy(hdd);
7894 }
7895 catch (HRESULT aRC) { rc = aRC; }
7896
7897 /* Everything is explicitly unlocked when the task exits,
7898 * as the task destruction also destroys the media chain. */
7899
7900 return rc;
7901}
7902
7903/**
7904 * Implementation code for the "resize" task.
7905 *
7906 * @param task
7907 * @return
7908 */
7909HRESULT Medium::i_taskResizeHandler(Medium::ResizeTask &task)
7910{
7911 HRESULT rc = S_OK;
7912
7913 uint64_t size = 0, logicalSize = 0;
7914
7915 try
7916 {
7917 /* The lock is also used as a signal from the task initiator (which
7918 * releases it only after RTThreadCreate()) that we can start the job */
7919 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
7920
7921 PVBOXHDD hdd;
7922 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
7923 ComAssertRCThrow(vrc, E_FAIL);
7924
7925 try
7926 {
7927 /* Open all media in the chain. */
7928 MediumLockList::Base::const_iterator mediumListBegin =
7929 task.mpMediumLockList->GetBegin();
7930 MediumLockList::Base::const_iterator mediumListEnd =
7931 task.mpMediumLockList->GetEnd();
7932 MediumLockList::Base::const_iterator mediumListLast =
7933 mediumListEnd;
7934 mediumListLast--;
7935 for (MediumLockList::Base::const_iterator it = mediumListBegin;
7936 it != mediumListEnd;
7937 ++it)
7938 {
7939 const MediumLock &mediumLock = *it;
7940 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
7941 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
7942
7943 /* sanity check */
7944 if (it == mediumListLast)
7945 Assert(pMedium->m->state == MediumState_LockedWrite);
7946 else
7947 Assert(pMedium->m->state == MediumState_LockedRead);
7948
7949 /* Open all media but last in read-only mode. Do not handle
7950 * shareable media, as compaction and sharing are mutually
7951 * exclusive. */
7952 vrc = VDOpen(hdd,
7953 pMedium->m->strFormat.c_str(),
7954 pMedium->m->strLocationFull.c_str(),
7955 m->uOpenFlagsDef | (it == mediumListLast ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY),
7956 pMedium->m->vdImageIfaces);
7957 if (RT_FAILURE(vrc))
7958 throw setError(VBOX_E_FILE_ERROR,
7959 tr("Could not open the medium storage unit '%s'%s"),
7960 pMedium->m->strLocationFull.c_str(),
7961 i_vdError(vrc).c_str());
7962 }
7963
7964 Assert(m->state == MediumState_LockedWrite);
7965
7966 Utf8Str location(m->strLocationFull);
7967
7968 /* unlock before the potentially lengthy operation */
7969 thisLock.release();
7970
7971 VDGEOMETRY geo = {0, 0, 0}; /* auto */
7972 vrc = VDResize(hdd, task.mSize, &geo, &geo, task.mVDOperationIfaces);
7973 if (RT_FAILURE(vrc))
7974 {
7975 if (vrc == VERR_NOT_SUPPORTED)
7976 throw setError(VBOX_E_NOT_SUPPORTED,
7977 tr("Resizing to new size %llu is not yet supported for medium '%s'"),
7978 task.mSize, location.c_str());
7979 else if (vrc == VERR_NOT_IMPLEMENTED)
7980 throw setError(E_NOTIMPL,
7981 tr("Resiting is not implemented, medium '%s'"),
7982 location.c_str());
7983 else
7984 throw setError(VBOX_E_FILE_ERROR,
7985 tr("Could not resize medium '%s'%s"),
7986 location.c_str(),
7987 i_vdError(vrc).c_str());
7988 }
7989 size = VDGetFileSize(hdd, VD_LAST_IMAGE);
7990 logicalSize = VDGetSize(hdd, VD_LAST_IMAGE);
7991 }
7992 catch (HRESULT aRC) { rc = aRC; }
7993
7994 VDDestroy(hdd);
7995 }
7996 catch (HRESULT aRC) { rc = aRC; }
7997
7998 if (SUCCEEDED(rc))
7999 {
8000 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
8001 m->size = size;
8002 m->logicalSize = logicalSize;
8003 }
8004
8005 /* Everything is explicitly unlocked when the task exits,
8006 * as the task destruction also destroys the media chain. */
8007
8008 return rc;
8009}
8010
8011/**
8012 * Implementation code for the "export" task.
8013 *
8014 * This only gets started from Medium::exportFile() and always runs
8015 * asynchronously. It doesn't touch anything configuration related, so
8016 * we never save the VirtualBox.xml file here.
8017 *
8018 * @param task
8019 * @return
8020 */
8021HRESULT Medium::i_taskExportHandler(Medium::ExportTask &task)
8022{
8023 HRESULT rc = S_OK;
8024
8025 try
8026 {
8027 /* Lock all in {parent,child} order. The lock is also used as a
8028 * signal from the task initiator (which releases it only after
8029 * RTThreadCreate()) that we can start the job. */
8030 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
8031
8032 PVBOXHDD hdd;
8033 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
8034 ComAssertRCThrow(vrc, E_FAIL);
8035
8036 try
8037 {
8038 /* Open all media in the source chain. */
8039 MediumLockList::Base::const_iterator sourceListBegin =
8040 task.mpSourceMediumLockList->GetBegin();
8041 MediumLockList::Base::const_iterator sourceListEnd =
8042 task.mpSourceMediumLockList->GetEnd();
8043 for (MediumLockList::Base::const_iterator it = sourceListBegin;
8044 it != sourceListEnd;
8045 ++it)
8046 {
8047 const MediumLock &mediumLock = *it;
8048 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
8049 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
8050
8051 /* sanity check */
8052 Assert(pMedium->m->state == MediumState_LockedRead);
8053
8054 /* Open all media in read-only mode. */
8055 vrc = VDOpen(hdd,
8056 pMedium->m->strFormat.c_str(),
8057 pMedium->m->strLocationFull.c_str(),
8058 VD_OPEN_FLAGS_READONLY | m->uOpenFlagsDef,
8059 pMedium->m->vdImageIfaces);
8060 if (RT_FAILURE(vrc))
8061 throw setError(VBOX_E_FILE_ERROR,
8062 tr("Could not open the medium storage unit '%s'%s"),
8063 pMedium->m->strLocationFull.c_str(),
8064 i_vdError(vrc).c_str());
8065 }
8066
8067 Utf8Str targetFormat(task.mFormat->i_getId());
8068 Utf8Str targetLocation(task.mFilename);
8069 uint64_t capabilities = task.mFormat->i_getCapabilities();
8070
8071 Assert(m->state == MediumState_LockedRead);
8072
8073 /* unlock before the potentially lengthy operation */
8074 thisLock.release();
8075
8076 /* ensure the target directory exists */
8077 if (capabilities & MediumFormatCapabilities_File)
8078 {
8079 rc = VirtualBox::i_ensureFilePathExists(targetLocation,
8080 !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
8081 if (FAILED(rc))
8082 throw rc;
8083 }
8084
8085 PVBOXHDD targetHdd;
8086 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &targetHdd);
8087 ComAssertRCThrow(vrc, E_FAIL);
8088
8089 try
8090 {
8091 vrc = VDCopy(hdd,
8092 VD_LAST_IMAGE,
8093 targetHdd,
8094 targetFormat.c_str(),
8095 targetLocation.c_str(),
8096 false /* fMoveByRename */,
8097 0 /* cbSize */,
8098 task.mVariant & ~MediumVariant_NoCreateDir,
8099 NULL /* pDstUuid */,
8100 VD_OPEN_FLAGS_NORMAL | VD_OPEN_FLAGS_SEQUENTIAL,
8101 NULL /* pVDIfsOperation */,
8102 task.mVDImageIfaces,
8103 task.mVDOperationIfaces);
8104 if (RT_FAILURE(vrc))
8105 throw setError(VBOX_E_FILE_ERROR,
8106 tr("Could not create the exported medium '%s'%s"),
8107 targetLocation.c_str(), i_vdError(vrc).c_str());
8108 }
8109 catch (HRESULT aRC) { rc = aRC; }
8110
8111 VDDestroy(targetHdd);
8112 }
8113 catch (HRESULT aRC) { rc = aRC; }
8114
8115 VDDestroy(hdd);
8116 }
8117 catch (HRESULT aRC) { rc = aRC; }
8118
8119 /* Everything is explicitly unlocked when the task exits,
8120 * as the task destruction also destroys the source chain. */
8121
8122 /* Make sure the source chain is released early, otherwise it can
8123 * lead to deadlocks with concurrent IAppliance activities. */
8124 task.mpSourceMediumLockList->Clear();
8125
8126 return rc;
8127}
8128
8129/**
8130 * Implementation code for the "import" task.
8131 *
8132 * This only gets started from Medium::importFile() and always runs
8133 * asynchronously. It potentially touches the media registry, so we
8134 * always save the VirtualBox.xml file when we're done here.
8135 *
8136 * @param task
8137 * @return
8138 */
8139HRESULT Medium::i_taskImportHandler(Medium::ImportTask &task)
8140{
8141 HRESULT rcTmp = S_OK;
8142
8143 const ComObjPtr<Medium> &pParent = task.mParent;
8144
8145 bool fCreatingTarget = false;
8146
8147 uint64_t size = 0, logicalSize = 0;
8148 MediumVariant_T variant = MediumVariant_Standard;
8149 bool fGenerateUuid = false;
8150
8151 try
8152 {
8153 /* Lock all in {parent,child} order. The lock is also used as a
8154 * signal from the task initiator (which releases it only after
8155 * RTThreadCreate()) that we can start the job. */
8156 AutoMultiWriteLock2 thisLock(this, pParent COMMA_LOCKVAL_SRC_POS);
8157
8158 fCreatingTarget = m->state == MediumState_Creating;
8159
8160 /* The object may request a specific UUID (through a special form of
8161 * the setLocation() argument). Otherwise we have to generate it */
8162 Guid targetId = m->id;
8163
8164 fGenerateUuid = targetId.isZero();
8165 if (fGenerateUuid)
8166 {
8167 targetId.create();
8168 /* VirtualBox::i_registerMedium() will need UUID */
8169 unconst(m->id) = targetId;
8170 }
8171
8172
8173 PVBOXHDD hdd;
8174 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
8175 ComAssertRCThrow(vrc, E_FAIL);
8176
8177 try
8178 {
8179 /* Open source medium. */
8180 vrc = VDOpen(hdd,
8181 task.mFormat->i_getId().c_str(),
8182 task.mFilename.c_str(),
8183 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SEQUENTIAL | m->uOpenFlagsDef,
8184 task.mVDImageIfaces);
8185 if (RT_FAILURE(vrc))
8186 throw setError(VBOX_E_FILE_ERROR,
8187 tr("Could not open the medium storage unit '%s'%s"),
8188 task.mFilename.c_str(),
8189 i_vdError(vrc).c_str());
8190
8191 Utf8Str targetFormat(m->strFormat);
8192 Utf8Str targetLocation(m->strLocationFull);
8193 uint64_t capabilities = task.mFormat->i_getCapabilities();
8194
8195 Assert( m->state == MediumState_Creating
8196 || m->state == MediumState_LockedWrite);
8197 Assert( pParent.isNull()
8198 || pParent->m->state == MediumState_LockedRead);
8199
8200 /* unlock before the potentially lengthy operation */
8201 thisLock.release();
8202
8203 /* ensure the target directory exists */
8204 if (capabilities & MediumFormatCapabilities_File)
8205 {
8206 HRESULT rc = VirtualBox::i_ensureFilePathExists(targetLocation,
8207 !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
8208 if (FAILED(rc))
8209 throw rc;
8210 }
8211
8212 PVBOXHDD targetHdd;
8213 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &targetHdd);
8214 ComAssertRCThrow(vrc, E_FAIL);
8215
8216 try
8217 {
8218 /* Open all media in the target chain. */
8219 MediumLockList::Base::const_iterator targetListBegin =
8220 task.mpTargetMediumLockList->GetBegin();
8221 MediumLockList::Base::const_iterator targetListEnd =
8222 task.mpTargetMediumLockList->GetEnd();
8223 for (MediumLockList::Base::const_iterator it = targetListBegin;
8224 it != targetListEnd;
8225 ++it)
8226 {
8227 const MediumLock &mediumLock = *it;
8228 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
8229
8230 /* If the target medium is not created yet there's no
8231 * reason to open it. */
8232 if (pMedium == this && fCreatingTarget)
8233 continue;
8234
8235 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
8236
8237 /* sanity check */
8238 Assert( pMedium->m->state == MediumState_LockedRead
8239 || pMedium->m->state == MediumState_LockedWrite);
8240
8241 unsigned uOpenFlags = VD_OPEN_FLAGS_NORMAL;
8242 if (pMedium->m->state != MediumState_LockedWrite)
8243 uOpenFlags = VD_OPEN_FLAGS_READONLY;
8244 if (pMedium->m->type == MediumType_Shareable)
8245 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
8246
8247 /* Open all media in appropriate mode. */
8248 vrc = VDOpen(targetHdd,
8249 pMedium->m->strFormat.c_str(),
8250 pMedium->m->strLocationFull.c_str(),
8251 uOpenFlags | m->uOpenFlagsDef,
8252 pMedium->m->vdImageIfaces);
8253 if (RT_FAILURE(vrc))
8254 throw setError(VBOX_E_FILE_ERROR,
8255 tr("Could not open the medium storage unit '%s'%s"),
8256 pMedium->m->strLocationFull.c_str(),
8257 i_vdError(vrc).c_str());
8258 }
8259
8260 /** @todo r=klaus target isn't locked, race getting the state */
8261 vrc = VDCopy(hdd,
8262 VD_LAST_IMAGE,
8263 targetHdd,
8264 targetFormat.c_str(),
8265 (fCreatingTarget) ? targetLocation.c_str() : (char *)NULL,
8266 false /* fMoveByRename */,
8267 0 /* cbSize */,
8268 task.mVariant & ~MediumVariant_NoCreateDir,
8269 targetId.raw(),
8270 VD_OPEN_FLAGS_NORMAL,
8271 NULL /* pVDIfsOperation */,
8272 m->vdImageIfaces,
8273 task.mVDOperationIfaces);
8274 if (RT_FAILURE(vrc))
8275 throw setError(VBOX_E_FILE_ERROR,
8276 tr("Could not create the imported medium '%s'%s"),
8277 targetLocation.c_str(), i_vdError(vrc).c_str());
8278
8279 size = VDGetFileSize(targetHdd, VD_LAST_IMAGE);
8280 logicalSize = VDGetSize(targetHdd, VD_LAST_IMAGE);
8281 unsigned uImageFlags;
8282 vrc = VDGetImageFlags(targetHdd, 0, &uImageFlags);
8283 if (RT_SUCCESS(vrc))
8284 variant = (MediumVariant_T)uImageFlags;
8285 }
8286 catch (HRESULT aRC) { rcTmp = aRC; }
8287
8288 VDDestroy(targetHdd);
8289 }
8290 catch (HRESULT aRC) { rcTmp = aRC; }
8291
8292 VDDestroy(hdd);
8293 }
8294 catch (HRESULT aRC) { rcTmp = aRC; }
8295
8296 ErrorInfoKeeper eik;
8297 MultiResult mrc(rcTmp);
8298
8299 /* Only do the parent changes for newly created media. */
8300 if (SUCCEEDED(mrc) && fCreatingTarget)
8301 {
8302 /* we set mParent & children() */
8303 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
8304
8305 Assert(m->pParent.isNull());
8306
8307 if (pParent)
8308 {
8309 /* associate the imported medium with the parent and deassociate
8310 * from VirtualBox */
8311 m->pParent = pParent;
8312 pParent->m->llChildren.push_back(this);
8313
8314 /* register with mVirtualBox as the last step and move to
8315 * Created state only on success (leaving an orphan file is
8316 * better than breaking media registry consistency) */
8317 eik.restore();
8318 ComObjPtr<Medium> pMedium;
8319 mrc = pParent->m->pVirtualBox->i_registerMedium(this, &pMedium,
8320 DeviceType_HardDisk,
8321 treeLock);
8322 Assert(this == pMedium);
8323 eik.fetch();
8324
8325 if (FAILED(mrc))
8326 /* break parent association on failure to register */
8327 this->i_deparent(); // removes target from parent
8328 }
8329 else
8330 {
8331 /* just register */
8332 eik.restore();
8333 ComObjPtr<Medium> pMedium;
8334 mrc = m->pVirtualBox->i_registerMedium(this, &pMedium, DeviceType_HardDisk, treeLock);
8335 Assert(this == pMedium);
8336 eik.fetch();
8337 }
8338 }
8339
8340 if (fCreatingTarget)
8341 {
8342 AutoWriteLock mLock(this COMMA_LOCKVAL_SRC_POS);
8343
8344 if (SUCCEEDED(mrc))
8345 {
8346 m->state = MediumState_Created;
8347
8348 m->size = size;
8349 m->logicalSize = logicalSize;
8350 m->variant = variant;
8351 }
8352 else
8353 {
8354 /* back to NotCreated on failure */
8355 m->state = MediumState_NotCreated;
8356
8357 /* reset UUID to prevent it from being reused next time */
8358 if (fGenerateUuid)
8359 unconst(m->id).clear();
8360 }
8361 }
8362
8363 // now, at the end of this task (always asynchronous), save the settings
8364 {
8365 // save the settings
8366 i_markRegistriesModified();
8367 /* collect multiple errors */
8368 eik.restore();
8369 m->pVirtualBox->i_saveModifiedRegistries();
8370 eik.fetch();
8371 }
8372
8373 /* Everything is explicitly unlocked when the task exits,
8374 * as the task destruction also destroys the target chain. */
8375
8376 /* Make sure the target chain is released early, otherwise it can
8377 * lead to deadlocks with concurrent IAppliance activities. */
8378 task.mpTargetMediumLockList->Clear();
8379
8380 return mrc;
8381}
8382
8383/* 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