VirtualBox

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

Last change on this file since 52267 was 52168, checked in by vboxsync, 11 years ago

Main/VirtualBox+Medium+Snapshot: fix lock inconsistency which crept into the previous fixes

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 280.8 KB
Line 
1/* $Id: MediumImpl.cpp 52168 2014-07-24 13:43:46Z 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.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#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::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::queryInfo call. */
152 const Guid uuidImage;
153 /** New parent UUID to be set on the next Medium::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::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 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 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 if (SUCCEEDED(rc))
1070 {
1071 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1072
1073 /* if the storage unit is not accessible, it's not acceptable for the
1074 * newly opened media so convert this into an error */
1075 if (m->state == MediumState_Inaccessible)
1076 {
1077 Assert(!m->strLastAccessError.isEmpty());
1078 rc = setError(E_FAIL, "%s", m->strLastAccessError.c_str());
1079 alock.release();
1080 autoCaller.release();
1081 uninit();
1082 }
1083 else
1084 {
1085 AssertStmt(!m->id.isZero(),
1086 alock.release(); autoCaller.release(); uninit(); return E_FAIL);
1087
1088 /* storage format must be detected by Medium::queryInfo if the
1089 * medium is accessible */
1090 AssertStmt(!m->strFormat.isEmpty(),
1091 alock.release(); autoCaller.release(); uninit(); return E_FAIL);
1092 }
1093 }
1094 else
1095 {
1096 /* opening this image failed, mark the object as dead */
1097 autoCaller.release();
1098 uninit();
1099 }
1100
1101 return rc;
1102}
1103
1104/**
1105 * Initializes the medium object by loading its data from the given settings
1106 * node. In this mode, the medium will always be opened read/write.
1107 *
1108 * In this case, since we're loading from a registry, uuidMachineRegistry is
1109 * always set: it's either the global registry UUID or a machine UUID when
1110 * loading from a per-machine registry.
1111 *
1112 * @param aVirtualBox VirtualBox object.
1113 * @param aParent Parent medium disk or NULL for a root (base) medium.
1114 * @param aDeviceType Device type of the medium.
1115 * @param uuidMachineRegistry The registry to which this medium should be
1116 * added (global registry UUID or machine UUID).
1117 * @param aNode Configuration settings.
1118 * @param strMachineFolder The machine folder with which to resolve relative paths;
1119 * if empty, then we use the VirtualBox home directory
1120 *
1121 * @note Locks the medium tree for writing.
1122 */
1123HRESULT Medium::init(VirtualBox *aVirtualBox,
1124 Medium *aParent,
1125 DeviceType_T aDeviceType,
1126 const Guid &uuidMachineRegistry,
1127 const settings::Medium &data,
1128 const Utf8Str &strMachineFolder)
1129{
1130 using namespace settings;
1131
1132 AssertReturn(aVirtualBox, E_INVALIDARG);
1133
1134 /* Enclose the state transition NotReady->InInit->Ready */
1135 AutoInitSpan autoInitSpan(this);
1136 AssertReturn(autoInitSpan.isOk(), E_FAIL);
1137
1138 HRESULT rc = S_OK;
1139
1140 unconst(m->pVirtualBox) = aVirtualBox;
1141
1142 if (uuidMachineRegistry.isValid() && !uuidMachineRegistry.isZero())
1143 m->llRegistryIDs.push_back(uuidMachineRegistry);
1144
1145 /* register with VirtualBox/parent early, since uninit() will
1146 * unconditionally unregister on failure */
1147 if (aParent)
1148 {
1149 // differencing medium: add to parent
1150 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
1151 m->pParent = aParent;
1152 aParent->m->llChildren.push_back(this);
1153 }
1154
1155 /* see below why we don't call Medium::queryInfo (and therefore treat
1156 * the medium as inaccessible for now */
1157 m->state = MediumState_Inaccessible;
1158 m->strLastAccessError = tr("Accessibility check was not yet performed");
1159
1160 /* required */
1161 unconst(m->id) = data.uuid;
1162
1163 /* assume not a host drive */
1164 m->hostDrive = false;
1165
1166 /* optional */
1167 m->strDescription = data.strDescription;
1168
1169 /* required */
1170 if (aDeviceType == DeviceType_HardDisk)
1171 {
1172 AssertReturn(!data.strFormat.isEmpty(), E_FAIL);
1173 rc = i_setFormat(data.strFormat);
1174 if (FAILED(rc)) return rc;
1175 }
1176 else
1177 {
1178 /// @todo handle host drive settings here as well?
1179 if (!data.strFormat.isEmpty())
1180 rc = i_setFormat(data.strFormat);
1181 else
1182 rc = i_setFormat("RAW");
1183 if (FAILED(rc)) return rc;
1184 }
1185
1186 /* optional, only for diffs, default is false; we can only auto-reset
1187 * diff media so they must have a parent */
1188 if (aParent != NULL)
1189 m->autoReset = data.fAutoReset;
1190 else
1191 m->autoReset = false;
1192
1193 /* properties (after setting the format as it populates the map). Note that
1194 * if some properties are not supported but present in the settings file,
1195 * they will still be read and accessible (for possible backward
1196 * compatibility; we can also clean them up from the XML upon next
1197 * XML format version change if we wish) */
1198 for (settings::StringsMap::const_iterator it = data.properties.begin();
1199 it != data.properties.end();
1200 ++it)
1201 {
1202 const Utf8Str &name = it->first;
1203 const Utf8Str &value = it->second;
1204 m->mapProperties[name] = value;
1205 }
1206
1207 /* try to decrypt an optional iSCSI initiator secret */
1208 settings::StringsMap::const_iterator itCph = data.properties.find("InitiatorSecretEncrypted");
1209 if ( itCph != data.properties.end()
1210 && !itCph->second.isEmpty())
1211 {
1212 Utf8Str strPlaintext;
1213 int vrc = m->pVirtualBox->i_decryptSetting(&strPlaintext, itCph->second);
1214 if (RT_SUCCESS(vrc))
1215 m->mapProperties["InitiatorSecret"] = strPlaintext;
1216 }
1217
1218 Utf8Str strFull;
1219 if (m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File)
1220 {
1221 // compose full path of the medium, if it's not fully qualified...
1222 // slightly convoluted logic here. If the caller has given us a
1223 // machine folder, then a relative path will be relative to that:
1224 if ( !strMachineFolder.isEmpty()
1225 && !RTPathStartsWithRoot(data.strLocation.c_str())
1226 )
1227 {
1228 strFull = strMachineFolder;
1229 strFull += RTPATH_SLASH;
1230 strFull += data.strLocation;
1231 }
1232 else
1233 {
1234 // Otherwise use the old VirtualBox "make absolute path" logic:
1235 rc = m->pVirtualBox->i_calculateFullPath(data.strLocation, strFull);
1236 if (FAILED(rc)) return rc;
1237 }
1238 }
1239 else
1240 strFull = data.strLocation;
1241
1242 rc = i_setLocation(strFull);
1243 if (FAILED(rc)) return rc;
1244
1245 if (aDeviceType == DeviceType_HardDisk)
1246 {
1247 /* type is only for base hard disks */
1248 if (m->pParent.isNull())
1249 m->type = data.hdType;
1250 }
1251 else if (aDeviceType == DeviceType_DVD)
1252 m->type = MediumType_Readonly;
1253 else
1254 m->type = MediumType_Writethrough;
1255
1256 /* remember device type for correct unregistering later */
1257 m->devType = aDeviceType;
1258
1259 LogFlowThisFunc(("m->strLocationFull='%s', m->strFormat=%s, m->id={%RTuuid}\n",
1260 m->strLocationFull.c_str(), m->strFormat.c_str(), m->id.raw()));
1261
1262 /* Don't call Medium::queryInfo for registered media to prevent the calling
1263 * thread (i.e. the VirtualBox server startup thread) from an unexpected
1264 * freeze but mark it as initially inaccessible instead. The vital UUID,
1265 * location and format properties are read from the registry file above; to
1266 * get the actual state and the rest of the data, the user will have to call
1267 * COMGETTER(State). */
1268
1269 /* load all children */
1270 for (settings::MediaList::const_iterator it = data.llChildren.begin();
1271 it != data.llChildren.end();
1272 ++it)
1273 {
1274 const settings::Medium &med = *it;
1275
1276 ComObjPtr<Medium> pHD;
1277 pHD.createObject();
1278 rc = pHD->init(aVirtualBox,
1279 this, // parent
1280 aDeviceType,
1281 uuidMachineRegistry,
1282 med, // child data
1283 strMachineFolder);
1284 if (FAILED(rc)) break;
1285
1286 AutoWriteLock treeLock(aVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
1287
1288 rc = m->pVirtualBox->i_registerMedium(pHD, &pHD, DeviceType_HardDisk, treeLock);
1289 if (FAILED(rc)) break;
1290 }
1291
1292 /* Confirm a successful initialization when it's the case */
1293 if (SUCCEEDED(rc))
1294 autoInitSpan.setSucceeded();
1295
1296 return rc;
1297}
1298
1299/**
1300 * Initializes the medium object by providing the host drive information.
1301 * Not used for anything but the host floppy/host DVD case.
1302 *
1303 * There is no registry for this case.
1304 *
1305 * @param aVirtualBox VirtualBox object.
1306 * @param aDeviceType Device type of the medium.
1307 * @param aLocation Location of the host drive.
1308 * @param aDescription Comment for this host drive.
1309 *
1310 * @note Locks VirtualBox lock for writing.
1311 */
1312HRESULT Medium::init(VirtualBox *aVirtualBox,
1313 DeviceType_T aDeviceType,
1314 const Utf8Str &aLocation,
1315 const Utf8Str &aDescription /* = Utf8Str::Empty */)
1316{
1317 ComAssertRet(aDeviceType == DeviceType_DVD || aDeviceType == DeviceType_Floppy, E_INVALIDARG);
1318 ComAssertRet(!aLocation.isEmpty(), E_INVALIDARG);
1319
1320 /* Enclose the state transition NotReady->InInit->Ready */
1321 AutoInitSpan autoInitSpan(this);
1322 AssertReturn(autoInitSpan.isOk(), E_FAIL);
1323
1324 unconst(m->pVirtualBox) = aVirtualBox;
1325
1326 // We do not store host drives in VirtualBox.xml or anywhere else, so if we want
1327 // host drives to be identifiable by UUID and not give the drive a different UUID
1328 // every time VirtualBox starts, we need to fake a reproducible UUID here:
1329 RTUUID uuid;
1330 RTUuidClear(&uuid);
1331 if (aDeviceType == DeviceType_DVD)
1332 memcpy(&uuid.au8[0], "DVD", 3);
1333 else
1334 memcpy(&uuid.au8[0], "FD", 2);
1335 /* use device name, adjusted to the end of uuid, shortened if necessary */
1336 size_t lenLocation = aLocation.length();
1337 if (lenLocation > 12)
1338 memcpy(&uuid.au8[4], aLocation.c_str() + (lenLocation - 12), 12);
1339 else
1340 memcpy(&uuid.au8[4 + 12 - lenLocation], aLocation.c_str(), lenLocation);
1341 unconst(m->id) = uuid;
1342
1343 if (aDeviceType == DeviceType_DVD)
1344 m->type = MediumType_Readonly;
1345 else
1346 m->type = MediumType_Writethrough;
1347 m->devType = aDeviceType;
1348 m->state = MediumState_Created;
1349 m->hostDrive = true;
1350 HRESULT rc = i_setFormat("RAW");
1351 if (FAILED(rc)) return rc;
1352 rc = i_setLocation(aLocation);
1353 if (FAILED(rc)) return rc;
1354 m->strDescription = aDescription;
1355
1356 autoInitSpan.setSucceeded();
1357 return S_OK;
1358}
1359
1360/**
1361 * Uninitializes the instance.
1362 *
1363 * Called either from FinalRelease() or by the parent when it gets destroyed.
1364 *
1365 * @note All children of this medium get uninitialized by calling their
1366 * uninit() methods.
1367 */
1368void Medium::uninit()
1369{
1370 /* It is possible that some previous/concurrent uninit has already cleared
1371 * the pVirtualBox reference, and in this case we don't need to continue.
1372 * Normally this would be handled through the AutoUninitSpan magic,
1373 * however this cannot be done at this point as the media tree must be
1374 * locked before reaching the AutoUninitSpan, otherwise deadlocks can
1375 * happen due to*/
1376 ComObjPtr<VirtualBox> pVirtualBox(m->pVirtualBox);
1377 if (!pVirtualBox)
1378 return;
1379
1380 /* Caller must not hold the object or media tree lock over uninit(). */
1381 Assert(!isWriteLockOnCurrentThread());
1382 Assert(!pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
1383
1384 AutoWriteLock treeLock(pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
1385
1386 /* Enclose the state transition Ready->InUninit->NotReady */
1387 AutoUninitSpan autoUninitSpan(this);
1388 if (autoUninitSpan.uninitDone())
1389 return;
1390
1391 if (!m->formatObj.isNull())
1392 m->formatObj.setNull();
1393
1394 if (m->state == MediumState_Deleting)
1395 {
1396 /* This medium has been already deleted (directly or as part of a
1397 * merge). Reparenting has already been done. */
1398 Assert(m->pParent.isNull());
1399 }
1400 else
1401 {
1402 MediaList llChildren(m->llChildren);
1403 m->llChildren.clear();
1404 autoUninitSpan.setSucceeded();
1405
1406 while (!llChildren.empty())
1407 {
1408 ComObjPtr<Medium> pChild = llChildren.front();
1409 llChildren.pop_front();
1410 pChild->m->pParent.setNull();
1411 treeLock.release();
1412 pChild->uninit();
1413 treeLock.acquire();
1414 }
1415
1416 if (m->pParent)
1417 {
1418 // this is a differencing disk: then remove it from the parent's children list
1419 i_deparent();
1420 }
1421 }
1422
1423 unconst(m->pVirtualBox) = NULL;
1424}
1425
1426/**
1427 * Internal helper that removes "this" from the list of children of its
1428 * parent. Used in uninit() and other places when reparenting is necessary.
1429 *
1430 * The caller must hold the medium tree lock!
1431 */
1432void Medium::i_deparent()
1433{
1434 MediaList &llParent = m->pParent->m->llChildren;
1435 for (MediaList::iterator it = llParent.begin();
1436 it != llParent.end();
1437 ++it)
1438 {
1439 Medium *pParentsChild = *it;
1440 if (this == pParentsChild)
1441 {
1442 llParent.erase(it);
1443 break;
1444 }
1445 }
1446 m->pParent.setNull();
1447}
1448
1449/**
1450 * Internal helper that removes "this" from the list of children of its
1451 * parent. Used in uninit() and other places when reparenting is necessary.
1452 *
1453 * The caller must hold the medium tree lock!
1454 */
1455void Medium::i_setParent(const ComObjPtr<Medium> &pParent)
1456{
1457 m->pParent = pParent;
1458 if (pParent)
1459 pParent->m->llChildren.push_back(this);
1460}
1461
1462
1463////////////////////////////////////////////////////////////////////////////////
1464//
1465// IMedium public methods
1466//
1467////////////////////////////////////////////////////////////////////////////////
1468
1469HRESULT Medium::getId(com::Guid &aId)
1470{
1471 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1472
1473 aId = m->id;
1474
1475 return S_OK;
1476}
1477
1478HRESULT Medium::getDescription(com::Utf8Str &aDescription)
1479{
1480 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1481
1482 aDescription = m->strDescription;
1483
1484 return S_OK;
1485}
1486
1487HRESULT Medium::setDescription(const com::Utf8Str &aDescription)
1488{
1489// AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1490
1491 /// @todo update m->description and save the global registry (and local
1492 /// registries of portable VMs referring to this medium), this will also
1493 /// require to add the mRegistered flag to data
1494 NOREF(aDescription);
1495 ReturnComNotImplemented();
1496}
1497
1498HRESULT Medium::getState(MediumState_T *aState)
1499{
1500 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1501 *aState = m->state;
1502
1503 return S_OK;
1504}
1505
1506HRESULT Medium::getVariant(std::vector<MediumVariant_T> &aVariant)
1507{
1508 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1509
1510 SafeArray<MediumVariant_T> variants(sizeof(MediumVariant_T)*8);
1511
1512 aVariant.resize(variants.size());
1513 for (ULONG i = 0; i < variants.size(); ++i)
1514 {
1515 ULONG temp = m->variant;
1516 temp &= 1<<i;
1517 aVariant[i] = (MediumVariant_T)temp;
1518 }
1519
1520 return S_OK;
1521}
1522
1523HRESULT Medium::getLocation(com::Utf8Str &aLocation)
1524{
1525 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1526
1527 aLocation = m->strLocationFull;
1528
1529 return S_OK;
1530}
1531
1532HRESULT Medium::getName(com::Utf8Str &aName)
1533{
1534 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1535
1536 aName = i_getName();
1537
1538 return S_OK;
1539}
1540
1541HRESULT Medium::getDeviceType(DeviceType_T *aDeviceType)
1542{
1543 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1544
1545 *aDeviceType = m->devType;
1546
1547 return S_OK;
1548}
1549
1550HRESULT Medium::getHostDrive(BOOL *aHostDrive)
1551{
1552 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1553
1554 *aHostDrive = m->hostDrive;
1555
1556 return S_OK;
1557}
1558
1559HRESULT Medium::getSize(LONG64 *aSize)
1560{
1561 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1562
1563 *aSize = m->size;
1564
1565 return S_OK;
1566}
1567
1568HRESULT Medium::getFormat(com::Utf8Str &aFormat)
1569{
1570 /* no need to lock, m->strFormat is const */
1571
1572 aFormat = m->strFormat;
1573 return S_OK;
1574}
1575
1576HRESULT Medium::getMediumFormat(ComPtr<IMediumFormat> &aMediumFormat)
1577{
1578 /* no need to lock, m->formatObj is const */
1579 m->formatObj.queryInterfaceTo(aMediumFormat.asOutParam());
1580
1581 return S_OK;
1582}
1583
1584HRESULT Medium::getType(MediumType_T *aType)
1585{
1586 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1587
1588 *aType = m->type;
1589
1590 return S_OK;
1591}
1592
1593HRESULT Medium::setType(MediumType_T aType)
1594{
1595 // we access mParent and members
1596 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
1597 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
1598
1599 switch (m->state)
1600 {
1601 case MediumState_Created:
1602 case MediumState_Inaccessible:
1603 break;
1604 default:
1605 return i_setStateError();
1606 }
1607
1608 if (m->type == aType)
1609 {
1610 /* Nothing to do */
1611 return S_OK;
1612 }
1613
1614 DeviceType_T devType = i_getDeviceType();
1615 // DVD media can only be readonly.
1616 if (devType == DeviceType_DVD && aType != MediumType_Readonly)
1617 return setError(VBOX_E_INVALID_OBJECT_STATE,
1618 tr("Cannot change the type of DVD medium '%s'"),
1619 m->strLocationFull.c_str());
1620 // Floppy media can only be writethrough or readonly.
1621 if ( devType == DeviceType_Floppy
1622 && aType != MediumType_Writethrough
1623 && aType != MediumType_Readonly)
1624 return setError(VBOX_E_INVALID_OBJECT_STATE,
1625 tr("Cannot change the type of floppy medium '%s'"),
1626 m->strLocationFull.c_str());
1627
1628 /* cannot change the type of a differencing medium */
1629 if (m->pParent)
1630 return setError(VBOX_E_INVALID_OBJECT_STATE,
1631 tr("Cannot change the type of medium '%s' because it is a differencing medium"),
1632 m->strLocationFull.c_str());
1633
1634 /* Cannot change the type of a medium being in use by more than one VM.
1635 * If the change is to Immutable or MultiAttach then it must not be
1636 * directly attached to any VM, otherwise the assumptions about indirect
1637 * attachment elsewhere are violated and the VM becomes inaccessible.
1638 * Attaching an immutable medium triggers the diff creation, and this is
1639 * vital for the correct operation. */
1640 if ( m->backRefs.size() > 1
1641 || ( ( aType == MediumType_Immutable
1642 || aType == MediumType_MultiAttach)
1643 && m->backRefs.size() > 0))
1644 return setError(VBOX_E_INVALID_OBJECT_STATE,
1645 tr("Cannot change the type of medium '%s' because it is attached to %d virtual machines"),
1646 m->strLocationFull.c_str(), m->backRefs.size());
1647
1648 switch (aType)
1649 {
1650 case MediumType_Normal:
1651 case MediumType_Immutable:
1652 case MediumType_MultiAttach:
1653 {
1654 /* normal can be easily converted to immutable and vice versa even
1655 * if they have children as long as they are not attached to any
1656 * machine themselves */
1657 break;
1658 }
1659 case MediumType_Writethrough:
1660 case MediumType_Shareable:
1661 case MediumType_Readonly:
1662 {
1663 /* cannot change to writethrough, shareable or readonly
1664 * if there are children */
1665 if (i_getChildren().size() != 0)
1666 return setError(VBOX_E_OBJECT_IN_USE,
1667 tr("Cannot change type for medium '%s' since it has %d child media"),
1668 m->strLocationFull.c_str(), i_getChildren().size());
1669 if (aType == MediumType_Shareable)
1670 {
1671 MediumVariant_T variant = i_getVariant();
1672 if (!(variant & MediumVariant_Fixed))
1673 return setError(VBOX_E_INVALID_OBJECT_STATE,
1674 tr("Cannot change type for medium '%s' to 'Shareable' since it is a dynamic medium storage unit"),
1675 m->strLocationFull.c_str());
1676 }
1677 else if (aType == MediumType_Readonly && devType == DeviceType_HardDisk)
1678 {
1679 // Readonly hard disks are not allowed, this medium type is reserved for
1680 // DVDs and floppy images at the moment. Later we might allow readonly hard
1681 // disks, but that's extremely unusual and many guest OSes will have trouble.
1682 return setError(VBOX_E_INVALID_OBJECT_STATE,
1683 tr("Cannot change type for medium '%s' to 'Readonly' since it is a hard disk"),
1684 m->strLocationFull.c_str());
1685 }
1686 break;
1687 }
1688 default:
1689 AssertFailedReturn(E_FAIL);
1690 }
1691
1692 if (aType == MediumType_MultiAttach)
1693 {
1694 // This type is new with VirtualBox 4.0 and therefore requires settings
1695 // version 1.11 in the settings backend. Unfortunately it is not enough to do
1696 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
1697 // two reasons: The medium type is a property of the media registry tree, which
1698 // can reside in the global config file (for pre-4.0 media); we would therefore
1699 // possibly need to bump the global config version. We don't want to do that though
1700 // because that might make downgrading to pre-4.0 impossible.
1701 // As a result, we can only use these two new types if the medium is NOT in the
1702 // global registry:
1703 const Guid &uuidGlobalRegistry = m->pVirtualBox->i_getGlobalRegistryId();
1704 if (i_isInRegistry(uuidGlobalRegistry))
1705 return setError(VBOX_E_INVALID_OBJECT_STATE,
1706 tr("Cannot change type for medium '%s': the media type 'MultiAttach' can only be used "
1707 "on media registered with a machine that was created with VirtualBox 4.0 or later"),
1708 m->strLocationFull.c_str());
1709 }
1710
1711 m->type = aType;
1712
1713 // save the settings
1714 mlock.release();
1715 treeLock.release();
1716 i_markRegistriesModified();
1717 m->pVirtualBox->i_saveModifiedRegistries();
1718
1719 return S_OK;
1720}
1721
1722HRESULT Medium::getAllowedTypes(std::vector<MediumType_T> &aAllowedTypes)
1723{
1724 NOREF(aAllowedTypes);
1725 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1726
1727 ReturnComNotImplemented();
1728}
1729
1730HRESULT Medium::getParent(AutoCaller &autoCaller, ComPtr<IMedium> &aParent)
1731{
1732 autoCaller.release();
1733
1734 /* It is possible that some previous/concurrent uninit has already cleared
1735 * the pVirtualBox reference, see #uninit(). */
1736 ComObjPtr<VirtualBox> pVirtualBox(m->pVirtualBox);
1737
1738 /* we access mParent */
1739 AutoReadLock treeLock(!pVirtualBox.isNull() ? &pVirtualBox->i_getMediaTreeLockHandle() : NULL COMMA_LOCKVAL_SRC_POS);
1740
1741 autoCaller.add();
1742 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1743
1744 m->pParent.queryInterfaceTo(aParent.asOutParam());
1745
1746 return S_OK;
1747}
1748
1749HRESULT Medium::getChildren(AutoCaller &autoCaller, std::vector<ComPtr<IMedium> > &aChildren)
1750{
1751 autoCaller.release();
1752
1753 /* It is possible that some previous/concurrent uninit has already cleared
1754 * the pVirtualBox reference, see #uninit(). */
1755 ComObjPtr<VirtualBox> pVirtualBox(m->pVirtualBox);
1756
1757 /* we access children */
1758 AutoReadLock treeLock(!pVirtualBox.isNull() ? &pVirtualBox->i_getMediaTreeLockHandle() : NULL COMMA_LOCKVAL_SRC_POS);
1759
1760 autoCaller.add();
1761 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1762
1763 MediaList children(this->i_getChildren());
1764 aChildren.resize(children.size());
1765 size_t i = 0;
1766 for (MediaList::const_iterator it = children.begin(); it != children.end(); ++it, ++i)
1767 (*it).queryInterfaceTo(aChildren[i].asOutParam());
1768 return S_OK;
1769}
1770
1771HRESULT Medium::getBase(AutoCaller &autoCaller, ComPtr<IMedium> &aBase)
1772{
1773 autoCaller.release();
1774
1775 /* i_getBase() will do callers/locking */
1776 i_getBase().queryInterfaceTo(aBase.asOutParam());
1777
1778 return S_OK;
1779}
1780
1781HRESULT Medium::getReadOnly(BOOL *aReadOnly)
1782{
1783 /* isReadOnly() will do locking */
1784 *aReadOnly = i_isReadOnly();
1785
1786 return S_OK;
1787}
1788
1789HRESULT Medium::getLogicalSize(LONG64 *aLogicalSize)
1790{
1791 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1792
1793 *aLogicalSize = m->logicalSize;
1794
1795 return S_OK;
1796}
1797
1798HRESULT Medium::getAutoReset(BOOL *aAutoReset)
1799{
1800 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1801
1802 if (m->pParent.isNull())
1803 *aAutoReset = FALSE;
1804 else
1805 *aAutoReset = m->autoReset;
1806
1807 return S_OK;
1808}
1809
1810HRESULT Medium::setAutoReset(BOOL aAutoReset)
1811{
1812 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
1813
1814 if (m->pParent.isNull())
1815 return setError(VBOX_E_NOT_SUPPORTED,
1816 tr("Medium '%s' is not differencing"),
1817 m->strLocationFull.c_str());
1818
1819 if (m->autoReset != !!aAutoReset)
1820 {
1821 m->autoReset = !!aAutoReset;
1822
1823 // save the settings
1824 mlock.release();
1825 i_markRegistriesModified();
1826 m->pVirtualBox->i_saveModifiedRegistries();
1827 }
1828
1829 return S_OK;
1830}
1831
1832HRESULT Medium::getLastAccessError(com::Utf8Str &aLastAccessError)
1833{
1834 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1835
1836 aLastAccessError = m->strLastAccessError;
1837
1838 return S_OK;
1839}
1840
1841HRESULT Medium::getMachineIds(std::vector<com::Guid> &aMachineIds)
1842{
1843 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1844
1845 if (m->backRefs.size() != 0)
1846 {
1847 BackRefList brlist(m->backRefs);
1848 aMachineIds.resize(brlist.size());
1849 size_t i = 0;
1850 for (BackRefList::const_iterator it = brlist.begin(); it != brlist.end(); ++it, ++i)
1851 aMachineIds[i] = it->machineId;
1852 }
1853
1854 return S_OK;
1855}
1856
1857HRESULT Medium::setIds(BOOL aSetImageId,
1858 const com::Guid &aImageId,
1859 BOOL aSetParentId,
1860 const com::Guid &aParentId)
1861{
1862 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1863
1864 switch (m->state)
1865 {
1866 case MediumState_Created:
1867 break;
1868 default:
1869 return i_setStateError();
1870 }
1871
1872 Guid imageId, parentId;
1873 if (aSetImageId)
1874 {
1875 if (aImageId.toUtf16().isEmpty())
1876 imageId.create();
1877 else
1878 {
1879 imageId = aImageId;
1880 if (!imageId.isValid())
1881 return setError(E_INVALIDARG, tr("Argument %s is invalid"), "aImageId");
1882 }
1883 }
1884 if (aSetParentId)
1885 {
1886 if (aParentId.toUtf16().isEmpty())
1887 parentId.create();
1888 else
1889 parentId = aParentId;
1890 }
1891
1892 unconst(m->uuidImage) = imageId;
1893 unconst(m->uuidParentImage) = parentId;
1894
1895 // must not hold any locks before calling Medium::queryInfo
1896 alock.release();
1897
1898 HRESULT rc = i_queryInfo(!!aSetImageId /* fSetImageId */,
1899 !!aSetParentId /* fSetParentId */);
1900
1901 return rc;
1902}
1903
1904HRESULT Medium::refreshState(MediumState_T *aState)
1905{
1906 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1907
1908 HRESULT rc = S_OK;
1909
1910 switch (m->state)
1911 {
1912 case MediumState_Created:
1913 case MediumState_Inaccessible:
1914 case MediumState_LockedRead:
1915 {
1916 // must not hold any locks before calling Medium::queryInfo
1917 alock.release();
1918
1919 rc = i_queryInfo(false /* fSetImageId */, false /* fSetParentId */);
1920
1921 alock.acquire();
1922 break;
1923 }
1924 default:
1925 break;
1926 }
1927
1928 *aState = m->state;
1929
1930 return rc;
1931}
1932
1933HRESULT Medium::getSnapshotIds(const com::Guid &aMachineId,
1934 std::vector<com::Guid> &aSnapshotIds)
1935{
1936 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1937
1938 for (BackRefList::const_iterator it = m->backRefs.begin();
1939 it != m->backRefs.end(); ++it)
1940 {
1941 if (it->machineId == aMachineId)
1942 {
1943 size_t size = it->llSnapshotIds.size();
1944
1945 /* if the medium is attached to the machine in the current state, we
1946 * return its ID as the first element of the array */
1947 if (it->fInCurState)
1948 ++size;
1949
1950 if (size > 0)
1951 {
1952 aSnapshotIds.resize(size);
1953
1954 size_t j = 0;
1955 if (it->fInCurState)
1956 aSnapshotIds[j++] = it->machineId.toUtf16();
1957
1958 for(GuidList::const_iterator jt = it->llSnapshotIds.begin(); jt != it->llSnapshotIds.end(); ++jt, ++j)
1959 aSnapshotIds[j] = (*jt);
1960 }
1961
1962 break;
1963 }
1964 }
1965
1966 return S_OK;
1967}
1968
1969HRESULT Medium::lockRead(ComPtr<IToken> &aToken)
1970{
1971 /* Must not hold the object lock, as we need control over it below. */
1972 Assert(!isWriteLockOnCurrentThread());
1973 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1974
1975 /* Wait for a concurrently running Medium::queryInfo to complete. */
1976 if (m->queryInfoRunning)
1977 {
1978 /* Must not hold the media tree lock, as Medium::queryInfo needs this
1979 * lock and thus we would run into a deadlock here. */
1980 Assert(!m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
1981 while (m->queryInfoRunning)
1982 {
1983 alock.release();
1984 {
1985 AutoReadLock qlock(m->queryInfoSem COMMA_LOCKVAL_SRC_POS);
1986 }
1987 alock.acquire();
1988 }
1989 }
1990
1991 HRESULT rc = S_OK;
1992
1993 switch (m->state)
1994 {
1995 case MediumState_Created:
1996 case MediumState_Inaccessible:
1997 case MediumState_LockedRead:
1998 {
1999 ++m->readers;
2000
2001 ComAssertMsgBreak(m->readers != 0, ("Counter overflow"), rc = E_FAIL);
2002
2003 /* Remember pre-lock state */
2004 if (m->state != MediumState_LockedRead)
2005 m->preLockState = m->state;
2006
2007 LogFlowThisFunc(("Okay - prev state=%d readers=%d\n", m->state, m->readers));
2008 m->state = MediumState_LockedRead;
2009
2010 ComObjPtr<MediumLockToken> pToken;
2011 rc = pToken.createObject();
2012 if (SUCCEEDED(rc))
2013 rc = pToken->init(this, false /* fWrite */);
2014 if (FAILED(rc))
2015 {
2016 --m->readers;
2017 if (m->readers == 0)
2018 m->state = m->preLockState;
2019 return rc;
2020 }
2021
2022 pToken.queryInterfaceTo(aToken.asOutParam());
2023 break;
2024 }
2025 default:
2026 {
2027 LogFlowThisFunc(("Failing - state=%d\n", m->state));
2028 rc = i_setStateError();
2029 break;
2030 }
2031 }
2032
2033 return rc;
2034}
2035
2036/**
2037 * @note @a aState may be NULL if the state value is not needed (only for
2038 * in-process calls).
2039 */
2040HRESULT Medium::i_unlockRead(MediumState_T *aState)
2041{
2042 AutoCaller autoCaller(this);
2043 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2044
2045 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2046
2047 HRESULT rc = S_OK;
2048
2049 switch (m->state)
2050 {
2051 case MediumState_LockedRead:
2052 {
2053 ComAssertMsgBreak(m->readers != 0, ("Counter underflow"), rc = E_FAIL);
2054 --m->readers;
2055
2056 /* Reset the state after the last reader */
2057 if (m->readers == 0)
2058 {
2059 m->state = m->preLockState;
2060 /* There are cases where we inject the deleting state into
2061 * a medium locked for reading. Make sure #unmarkForDeletion()
2062 * gets the right state afterwards. */
2063 if (m->preLockState == MediumState_Deleting)
2064 m->preLockState = MediumState_Created;
2065 }
2066
2067 LogFlowThisFunc(("new state=%d\n", m->state));
2068 break;
2069 }
2070 default:
2071 {
2072 LogFlowThisFunc(("Failing - state=%d\n", m->state));
2073 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
2074 tr("Medium '%s' is not locked for reading"),
2075 m->strLocationFull.c_str());
2076 break;
2077 }
2078 }
2079
2080 /* return the current state after */
2081 if (aState)
2082 *aState = m->state;
2083
2084 return rc;
2085}
2086HRESULT Medium::lockWrite(ComPtr<IToken> &aToken)
2087{
2088 /* Must not hold the object lock, as we need control over it below. */
2089 Assert(!isWriteLockOnCurrentThread());
2090 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2091
2092 /* Wait for a concurrently running Medium::queryInfo to complete. */
2093 if (m->queryInfoRunning)
2094 {
2095 /* Must not hold the media tree lock, as Medium::queryInfo needs this
2096 * lock and thus we would run into a deadlock here. */
2097 Assert(!m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
2098 while (m->queryInfoRunning)
2099 {
2100 alock.release();
2101 {
2102 AutoReadLock qlock(m->queryInfoSem COMMA_LOCKVAL_SRC_POS);
2103 }
2104 alock.acquire();
2105 }
2106 }
2107
2108 HRESULT rc = S_OK;
2109
2110 switch (m->state)
2111 {
2112 case MediumState_Created:
2113 case MediumState_Inaccessible:
2114 {
2115 m->preLockState = m->state;
2116
2117 LogFlowThisFunc(("Okay - prev state=%d locationFull=%s\n", m->state, i_getLocationFull().c_str()));
2118 m->state = MediumState_LockedWrite;
2119
2120 ComObjPtr<MediumLockToken> pToken;
2121 rc = pToken.createObject();
2122 if (SUCCEEDED(rc))
2123 rc = pToken->init(this, true /* fWrite */);
2124 if (FAILED(rc))
2125 {
2126 m->state = m->preLockState;
2127 return rc;
2128 }
2129
2130 pToken.queryInterfaceTo(aToken.asOutParam());
2131 break;
2132 }
2133 default:
2134 {
2135 LogFlowThisFunc(("Failing - state=%d locationFull=%s\n", m->state, i_getLocationFull().c_str()));
2136 rc = i_setStateError();
2137 break;
2138 }
2139 }
2140
2141 return rc;
2142}
2143
2144/**
2145 * @note @a aState may be NULL if the state value is not needed (only for
2146 * in-process calls).
2147 */
2148HRESULT Medium::i_unlockWrite(MediumState_T *aState)
2149{
2150 AutoCaller autoCaller(this);
2151 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2152
2153 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2154
2155 HRESULT rc = S_OK;
2156
2157 switch (m->state)
2158 {
2159 case MediumState_LockedWrite:
2160 {
2161 m->state = m->preLockState;
2162 /* There are cases where we inject the deleting state into
2163 * a medium locked for writing. Make sure #unmarkForDeletion()
2164 * gets the right state afterwards. */
2165 if (m->preLockState == MediumState_Deleting)
2166 m->preLockState = MediumState_Created;
2167 LogFlowThisFunc(("new state=%d locationFull=%s\n", m->state, i_getLocationFull().c_str()));
2168 break;
2169 }
2170 default:
2171 {
2172 LogFlowThisFunc(("Failing - state=%d locationFull=%s\n", m->state, i_getLocationFull().c_str()));
2173 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
2174 tr("Medium '%s' is not locked for writing"),
2175 m->strLocationFull.c_str());
2176 break;
2177 }
2178 }
2179
2180 /* return the current state after */
2181 if (aState)
2182 *aState = m->state;
2183
2184 return rc;
2185}
2186
2187HRESULT Medium::close(AutoCaller &aAutoCaller)
2188{
2189 // make a copy of VirtualBox pointer which gets nulled by uninit()
2190 ComObjPtr<VirtualBox> pVirtualBox(m->pVirtualBox);
2191
2192 MultiResult mrc = i_close(aAutoCaller);
2193
2194 pVirtualBox->i_saveModifiedRegistries();
2195
2196 return mrc;
2197}
2198
2199HRESULT Medium::getProperty(const com::Utf8Str &aName,
2200 com::Utf8Str &aValue)
2201{
2202 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2203
2204 settings::StringsMap::const_iterator it = m->mapProperties.find(aName);
2205 if (it == m->mapProperties.end())
2206 {
2207 if (!aName.startsWith("Special/"))
2208 return setError(VBOX_E_OBJECT_NOT_FOUND,
2209 tr("Property '%s' does not exist"), aName.c_str());
2210 else
2211 /* be more silent here */
2212 return VBOX_E_OBJECT_NOT_FOUND;
2213 }
2214
2215 aValue = it->second;
2216
2217 return S_OK;
2218}
2219
2220HRESULT Medium::setProperty(const com::Utf8Str &aName,
2221 const com::Utf8Str &aValue)
2222{
2223 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
2224
2225 switch (m->state)
2226 {
2227 case MediumState_Created:
2228 case MediumState_Inaccessible:
2229 break;
2230 default:
2231 return i_setStateError();
2232 }
2233
2234 settings::StringsMap::iterator it = m->mapProperties.find(aName);
2235 if ( !aName.startsWith("Special/")
2236 && !i_isPropertyForFilter(aName))
2237 {
2238 if (it == m->mapProperties.end())
2239 return setError(VBOX_E_OBJECT_NOT_FOUND,
2240 tr("Property '%s' does not exist"),
2241 aName.c_str());
2242 it->second = aValue;
2243 }
2244 else
2245 {
2246 if (it == m->mapProperties.end())
2247 {
2248 if (!aValue.isEmpty())
2249 m->mapProperties[aName] = aValue;
2250 }
2251 else
2252 {
2253 if (!aValue.isEmpty())
2254 it->second = aValue;
2255 else
2256 m->mapProperties.erase(it);
2257 }
2258 }
2259
2260 // save the settings
2261 mlock.release();
2262 i_markRegistriesModified();
2263 m->pVirtualBox->i_saveModifiedRegistries();
2264
2265 return S_OK;
2266}
2267
2268HRESULT Medium::getProperties(const com::Utf8Str &aNames,
2269 std::vector<com::Utf8Str> &aReturnNames,
2270 std::vector<com::Utf8Str> &aReturnValues)
2271{
2272 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2273
2274 /// @todo make use of aNames according to the documentation
2275 NOREF(aNames);
2276
2277 aReturnNames.resize(m->mapProperties.size());
2278 aReturnValues.resize(m->mapProperties.size());
2279 size_t i = 0;
2280 for (settings::StringsMap::const_iterator it = m->mapProperties.begin();
2281 it != m->mapProperties.end();
2282 ++it, ++i)
2283 {
2284 aReturnNames[i] = it->first;
2285 aReturnValues[i] = it->second;
2286 }
2287 return S_OK;
2288}
2289
2290HRESULT Medium::setProperties(const std::vector<com::Utf8Str> &aNames,
2291 const std::vector<com::Utf8Str> &aValues)
2292{
2293 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
2294
2295 /* first pass: validate names */
2296 for (size_t i = 0;
2297 i < aNames.size();
2298 ++i)
2299 {
2300 Utf8Str strName(aNames[i]);
2301 if ( !strName.startsWith("Special/")
2302 && !i_isPropertyForFilter(strName)
2303 && m->mapProperties.find(strName) == m->mapProperties.end())
2304 return setError(VBOX_E_OBJECT_NOT_FOUND,
2305 tr("Property '%s' does not exist"), strName.c_str());
2306 }
2307
2308 /* second pass: assign */
2309 for (size_t i = 0;
2310 i < aNames.size();
2311 ++i)
2312 {
2313 Utf8Str strName(aNames[i]);
2314 Utf8Str strValue(aValues[i]);
2315 settings::StringsMap::iterator it = m->mapProperties.find(strName);
2316 if ( !strName.startsWith("Special/")
2317 && !i_isPropertyForFilter(strName))
2318 {
2319 AssertReturn(it != m->mapProperties.end(), E_FAIL);
2320 it->second = strValue;
2321 }
2322 else
2323 {
2324 if (it == m->mapProperties.end())
2325 {
2326 if (!strValue.isEmpty())
2327 m->mapProperties[strName] = strValue;
2328 }
2329 else
2330 {
2331 if (!strValue.isEmpty())
2332 it->second = strValue;
2333 else
2334 m->mapProperties.erase(it);
2335 }
2336 }
2337 }
2338
2339 // save the settings
2340 mlock.release();
2341 i_markRegistriesModified();
2342 m->pVirtualBox->i_saveModifiedRegistries();
2343
2344 return S_OK;
2345}
2346HRESULT Medium::createBaseStorage(LONG64 aLogicalSize,
2347 const std::vector<MediumVariant_T> &aVariant,
2348 ComPtr<IProgress> &aProgress)
2349{
2350 if (aLogicalSize < 0)
2351 return setError(E_INVALIDARG, tr("The medium size argument (%lld) is negative"), aLogicalSize);
2352
2353 HRESULT rc = S_OK;
2354 ComObjPtr<Progress> pProgress;
2355 Medium::Task *pTask = NULL;
2356
2357 try
2358 {
2359 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2360
2361 ULONG mediumVariantFlags = 0;
2362
2363 if (aVariant.size())
2364 {
2365 com::SafeArray<MediumVariant_T> variants(aVariant);
2366 for (size_t i = 0; i < variants.size(); i++)
2367 mediumVariantFlags |= variants[i];
2368 }
2369
2370 mediumVariantFlags &= ((unsigned)~MediumVariant_Diff);
2371
2372 if ( !(mediumVariantFlags & MediumVariant_Fixed)
2373 && !(m->formatObj->i_getCapabilities() & MediumFormatCapabilities_CreateDynamic))
2374 throw setError(VBOX_E_NOT_SUPPORTED,
2375 tr("Medium format '%s' does not support dynamic storage creation"),
2376 m->strFormat.c_str());
2377
2378 if ( (mediumVariantFlags & MediumVariant_Fixed)
2379 && !(m->formatObj->i_getCapabilities() & MediumFormatCapabilities_CreateDynamic))
2380 throw setError(VBOX_E_NOT_SUPPORTED,
2381 tr("Medium format '%s' does not support fixed storage creation"),
2382 m->strFormat.c_str());
2383
2384 if (m->state != MediumState_NotCreated)
2385 throw i_setStateError();
2386
2387 pProgress.createObject();
2388 rc = pProgress->init(m->pVirtualBox,
2389 static_cast<IMedium*>(this),
2390 (mediumVariantFlags & MediumVariant_Fixed)
2391 ? BstrFmt(tr("Creating fixed medium storage unit '%s'"), m->strLocationFull.c_str()).raw()
2392 : BstrFmt(tr("Creating dynamic medium storage unit '%s'"), m->strLocationFull.c_str()).raw(),
2393 TRUE /* aCancelable */);
2394 if (FAILED(rc))
2395 throw rc;
2396
2397 /* setup task object to carry out the operation asynchronously */
2398 pTask = new Medium::CreateBaseTask(this, pProgress, aLogicalSize,
2399 (MediumVariant_T)mediumVariantFlags);
2400 //(MediumVariant_T)aVariant);
2401 rc = pTask->rc();
2402 AssertComRC(rc);
2403 if (FAILED(rc))
2404 throw rc;
2405
2406 m->state = MediumState_Creating;
2407 }
2408 catch (HRESULT aRC) { rc = aRC; }
2409
2410 if (SUCCEEDED(rc))
2411 {
2412 rc = i_startThread(pTask);
2413
2414 if (SUCCEEDED(rc))
2415 pProgress.queryInterfaceTo(aProgress.asOutParam());
2416 }
2417 else if (pTask != NULL)
2418 delete pTask;
2419
2420 return rc;
2421}
2422
2423HRESULT Medium::deleteStorage(ComPtr<IProgress> &aProgress)
2424{
2425 ComObjPtr<Progress> pProgress;
2426
2427 MultiResult mrc = i_deleteStorage(&pProgress,
2428 false /* aWait */);
2429 /* Must save the registries in any case, since an entry was removed. */
2430 m->pVirtualBox->i_saveModifiedRegistries();
2431
2432 if (SUCCEEDED(mrc))
2433 pProgress.queryInterfaceTo(aProgress.asOutParam());
2434
2435 return mrc;
2436}
2437
2438HRESULT Medium::createDiffStorage(const ComPtr<IMedium> &aTarget,
2439 const std::vector<MediumVariant_T> &aVariant,
2440 ComPtr<IProgress> &aProgress)
2441{
2442 IMedium *aT = aTarget;
2443 ComObjPtr<Medium> diff = static_cast<Medium*>(aT);
2444
2445 // locking: we need the tree lock first because we access parent pointers
2446 AutoMultiWriteLock3 alock(&m->pVirtualBox->i_getMediaTreeLockHandle(),
2447 this->lockHandle(), diff->lockHandle() COMMA_LOCKVAL_SRC_POS);
2448
2449 if (m->type == MediumType_Writethrough)
2450 return setError(VBOX_E_INVALID_OBJECT_STATE,
2451 tr("Medium type of '%s' is Writethrough"),
2452 m->strLocationFull.c_str());
2453 else if (m->type == MediumType_Shareable)
2454 return setError(VBOX_E_INVALID_OBJECT_STATE,
2455 tr("Medium type of '%s' is Shareable"),
2456 m->strLocationFull.c_str());
2457 else if (m->type == MediumType_Readonly)
2458 return setError(VBOX_E_INVALID_OBJECT_STATE,
2459 tr("Medium type of '%s' is Readonly"),
2460 m->strLocationFull.c_str());
2461
2462 /* Apply the normal locking logic to the entire chain. */
2463 MediumLockList *pMediumLockList(new MediumLockList());
2464 alock.release();
2465 HRESULT rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
2466 true /* fMediumLockWrite */,
2467 this,
2468 *pMediumLockList);
2469 alock.acquire();
2470 if (FAILED(rc))
2471 {
2472 delete pMediumLockList;
2473 return rc;
2474 }
2475
2476 alock.release();
2477 rc = pMediumLockList->Lock();
2478 alock.acquire();
2479 if (FAILED(rc))
2480 {
2481 delete pMediumLockList;
2482
2483 return setError(rc, tr("Could not lock medium when creating diff '%s'"),
2484 diff->i_getLocationFull().c_str());
2485 }
2486
2487 Guid parentMachineRegistry;
2488 if (i_getFirstRegistryMachineId(parentMachineRegistry))
2489 {
2490 /* since this medium has been just created it isn't associated yet */
2491 diff->m->llRegistryIDs.push_back(parentMachineRegistry);
2492 alock.release();
2493 diff->i_markRegistriesModified();
2494 alock.acquire();
2495 }
2496
2497 alock.release();
2498
2499 ComObjPtr<Progress> pProgress;
2500
2501 ULONG mediumVariantFlags = 0;
2502
2503 if (aVariant.size())
2504 {
2505 com::SafeArray<MediumVariant_T> variants(aVariant);
2506 for (size_t i = 0; i < variants.size(); i++)
2507 mediumVariantFlags |= variants[i];
2508 }
2509
2510 rc = i_createDiffStorage(diff, (MediumVariant_T)mediumVariantFlags, pMediumLockList,
2511 &pProgress, false /* aWait */);
2512 if (FAILED(rc))
2513 delete pMediumLockList;
2514 else
2515 pProgress.queryInterfaceTo(aProgress.asOutParam());
2516
2517 return rc;
2518}
2519
2520HRESULT Medium::mergeTo(const ComPtr<IMedium> &aTarget,
2521 ComPtr<IProgress> &aProgress)
2522{
2523
2524 IMedium *aT = aTarget;
2525
2526 ComAssertRet(aT != this, E_INVALIDARG);
2527
2528 ComObjPtr<Medium> pTarget = static_cast<Medium*>(aT);
2529
2530 bool fMergeForward = false;
2531 ComObjPtr<Medium> pParentForTarget;
2532 MediumLockList *pChildrenToReparent = NULL;
2533 MediumLockList *pMediumLockList = NULL;
2534
2535 HRESULT rc = S_OK;
2536
2537 rc = i_prepareMergeTo(pTarget, NULL, NULL, true, fMergeForward,
2538 pParentForTarget, pChildrenToReparent, pMediumLockList);
2539 if (FAILED(rc)) return rc;
2540
2541 ComObjPtr<Progress> pProgress;
2542
2543 rc = i_mergeTo(pTarget, fMergeForward, pParentForTarget, pChildrenToReparent,
2544 pMediumLockList, &pProgress, false /* aWait */);
2545 if (FAILED(rc))
2546 i_cancelMergeTo(pChildrenToReparent, pMediumLockList);
2547 else
2548 pProgress.queryInterfaceTo(aProgress.asOutParam());
2549
2550 return rc;
2551}
2552
2553HRESULT Medium::cloneToBase(const ComPtr<IMedium> &aTarget,
2554 const std::vector<MediumVariant_T> &aVariant,
2555 ComPtr<IProgress> &aProgress)
2556{
2557 int rc = S_OK;
2558
2559 rc = cloneTo(aTarget, aVariant, NULL, aProgress);
2560 return rc;
2561}
2562
2563HRESULT Medium::cloneTo(const ComPtr<IMedium> &aTarget,
2564 const std::vector<MediumVariant_T> &aVariant,
2565 const ComPtr<IMedium> &aParent,
2566 ComPtr<IProgress> &aProgress)
2567{
2568 ComAssertRet(aTarget != this, E_INVALIDARG);
2569
2570 IMedium *aT = aTarget;
2571 ComObjPtr<Medium> pTarget = static_cast<Medium*>(aT);
2572 ComObjPtr<Medium> pParent;
2573 if (aParent)
2574 {
2575 IMedium *aP = aParent;
2576 pParent = static_cast<Medium*>(aP);
2577 }
2578
2579 HRESULT rc = S_OK;
2580 ComObjPtr<Progress> pProgress;
2581 Medium::Task *pTask = NULL;
2582
2583 try
2584 {
2585 // locking: we need the tree lock first because we access parent pointers
2586 // and we need to write-lock the media involved
2587 uint32_t cHandles = 3;
2588 LockHandle* pHandles[4] = { &m->pVirtualBox->i_getMediaTreeLockHandle(),
2589 this->lockHandle(),
2590 pTarget->lockHandle() };
2591 /* Only add parent to the lock if it is not null */
2592 if (!pParent.isNull())
2593 pHandles[cHandles++] = pParent->lockHandle();
2594 AutoWriteLock alock(cHandles,
2595 pHandles
2596 COMMA_LOCKVAL_SRC_POS);
2597
2598 if ( pTarget->m->state != MediumState_NotCreated
2599 && pTarget->m->state != MediumState_Created)
2600 throw pTarget->i_setStateError();
2601
2602 /* Build the source lock list. */
2603 MediumLockList *pSourceMediumLockList(new MediumLockList());
2604 alock.release();
2605 rc = i_createMediumLockList(true /* fFailIfInaccessible */,
2606 false /* fMediumLockWrite */,
2607 NULL,
2608 *pSourceMediumLockList);
2609 alock.acquire();
2610 if (FAILED(rc))
2611 {
2612 delete pSourceMediumLockList;
2613 throw rc;
2614 }
2615
2616 /* Build the target lock list (including the to-be parent chain). */
2617 MediumLockList *pTargetMediumLockList(new MediumLockList());
2618 alock.release();
2619 rc = pTarget->i_createMediumLockList(true /* fFailIfInaccessible */,
2620 true /* fMediumLockWrite */,
2621 pParent,
2622 *pTargetMediumLockList);
2623 alock.acquire();
2624 if (FAILED(rc))
2625 {
2626 delete pSourceMediumLockList;
2627 delete pTargetMediumLockList;
2628 throw rc;
2629 }
2630
2631 alock.release();
2632 rc = pSourceMediumLockList->Lock();
2633 alock.acquire();
2634 if (FAILED(rc))
2635 {
2636 delete pSourceMediumLockList;
2637 delete pTargetMediumLockList;
2638 throw setError(rc,
2639 tr("Failed to lock source media '%s'"),
2640 i_getLocationFull().c_str());
2641 }
2642 alock.release();
2643 rc = pTargetMediumLockList->Lock();
2644 alock.acquire();
2645 if (FAILED(rc))
2646 {
2647 delete pSourceMediumLockList;
2648 delete pTargetMediumLockList;
2649 throw setError(rc,
2650 tr("Failed to lock target media '%s'"),
2651 pTarget->i_getLocationFull().c_str());
2652 }
2653
2654 pProgress.createObject();
2655 rc = pProgress->init(m->pVirtualBox,
2656 static_cast <IMedium *>(this),
2657 BstrFmt(tr("Creating clone medium '%s'"), pTarget->m->strLocationFull.c_str()).raw(),
2658 TRUE /* aCancelable */);
2659 if (FAILED(rc))
2660 {
2661 delete pSourceMediumLockList;
2662 delete pTargetMediumLockList;
2663 throw rc;
2664 }
2665
2666 ULONG mediumVariantFlags = 0;
2667
2668 if (aVariant.size())
2669 {
2670 com::SafeArray<MediumVariant_T> variants(aVariant);
2671 for (size_t i = 0; i < variants.size(); i++)
2672 mediumVariantFlags |= variants[i];
2673 }
2674
2675 /* setup task object to carry out the operation asynchronously */
2676 pTask = new Medium::CloneTask(this, pProgress, pTarget,
2677 (MediumVariant_T)mediumVariantFlags,
2678 pParent, UINT32_MAX, UINT32_MAX,
2679 pSourceMediumLockList, pTargetMediumLockList);
2680 rc = pTask->rc();
2681 AssertComRC(rc);
2682 if (FAILED(rc))
2683 throw rc;
2684
2685 if (pTarget->m->state == MediumState_NotCreated)
2686 pTarget->m->state = MediumState_Creating;
2687 }
2688 catch (HRESULT aRC) { rc = aRC; }
2689
2690 if (SUCCEEDED(rc))
2691 {
2692 rc = i_startThread(pTask);
2693
2694 if (SUCCEEDED(rc))
2695 pProgress.queryInterfaceTo(aProgress.asOutParam());
2696 }
2697 else if (pTask != NULL)
2698 delete pTask;
2699
2700 return rc;
2701}
2702
2703HRESULT Medium::setLocation(const com::Utf8Str &aLocation, ComPtr<IProgress> &aProgress)
2704{
2705 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2706 NOREF(aLocation);
2707 NOREF(aProgress);
2708
2709 /// @todo NEWMEDIA for file names, add the default extension if no extension
2710 /// is present (using the information from the VD backend which also implies
2711 /// that one more parameter should be passed to setLocation() requesting
2712 /// that functionality since it is only allowed when called from this method
2713
2714 /// @todo NEWMEDIA rename the file and set m->location on success, then save
2715 /// the global registry (and local registries of portable VMs referring to
2716 /// this medium), this will also require to add the mRegistered flag to data
2717
2718 ReturnComNotImplemented();
2719}
2720
2721HRESULT Medium::compact(ComPtr<IProgress> &aProgress)
2722{
2723 HRESULT rc = S_OK;
2724 ComObjPtr<Progress> pProgress;
2725 Medium::Task *pTask = NULL;
2726
2727 try
2728 {
2729 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2730
2731 /* Build the medium lock list. */
2732 MediumLockList *pMediumLockList(new MediumLockList());
2733 alock.release();
2734 rc = i_createMediumLockList(true /* fFailIfInaccessible */ ,
2735 true /* fMediumLockWrite */,
2736 NULL,
2737 *pMediumLockList);
2738 alock.acquire();
2739 if (FAILED(rc))
2740 {
2741 delete pMediumLockList;
2742 throw rc;
2743 }
2744
2745 alock.release();
2746 rc = pMediumLockList->Lock();
2747 alock.acquire();
2748 if (FAILED(rc))
2749 {
2750 delete pMediumLockList;
2751 throw setError(rc,
2752 tr("Failed to lock media when compacting '%s'"),
2753 i_getLocationFull().c_str());
2754 }
2755
2756 pProgress.createObject();
2757 rc = pProgress->init(m->pVirtualBox,
2758 static_cast <IMedium *>(this),
2759 BstrFmt(tr("Compacting medium '%s'"), m->strLocationFull.c_str()).raw(),
2760 TRUE /* aCancelable */);
2761 if (FAILED(rc))
2762 {
2763 delete pMediumLockList;
2764 throw rc;
2765 }
2766
2767 /* setup task object to carry out the operation asynchronously */
2768 pTask = new Medium::CompactTask(this, pProgress, pMediumLockList);
2769 rc = pTask->rc();
2770 AssertComRC(rc);
2771 if (FAILED(rc))
2772 throw rc;
2773 }
2774 catch (HRESULT aRC) { rc = aRC; }
2775
2776 if (SUCCEEDED(rc))
2777 {
2778 rc = i_startThread(pTask);
2779
2780 if (SUCCEEDED(rc))
2781 pProgress.queryInterfaceTo(aProgress.asOutParam());
2782 }
2783 else if (pTask != NULL)
2784 delete pTask;
2785
2786 return rc;
2787}
2788
2789HRESULT Medium::resize(LONG64 aLogicalSize,
2790 ComPtr<IProgress> &aProgress)
2791{
2792 HRESULT rc = S_OK;
2793 ComObjPtr<Progress> pProgress;
2794 Medium::Task *pTask = NULL;
2795
2796 try
2797 {
2798 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2799
2800 /* Build the medium lock list. */
2801 MediumLockList *pMediumLockList(new MediumLockList());
2802 alock.release();
2803 rc = i_createMediumLockList(true /* fFailIfInaccessible */ ,
2804 true /* fMediumLockWrite */,
2805 NULL,
2806 *pMediumLockList);
2807 alock.acquire();
2808 if (FAILED(rc))
2809 {
2810 delete pMediumLockList;
2811 throw rc;
2812 }
2813
2814 alock.release();
2815 rc = pMediumLockList->Lock();
2816 alock.acquire();
2817 if (FAILED(rc))
2818 {
2819 delete pMediumLockList;
2820 throw setError(rc,
2821 tr("Failed to lock media when compacting '%s'"),
2822 i_getLocationFull().c_str());
2823 }
2824
2825 pProgress.createObject();
2826 rc = pProgress->init(m->pVirtualBox,
2827 static_cast <IMedium *>(this),
2828 BstrFmt(tr("Compacting medium '%s'"), m->strLocationFull.c_str()).raw(),
2829 TRUE /* aCancelable */);
2830 if (FAILED(rc))
2831 {
2832 delete pMediumLockList;
2833 throw rc;
2834 }
2835
2836 /* setup task object to carry out the operation asynchronously */
2837 pTask = new Medium::ResizeTask(this, aLogicalSize, pProgress, pMediumLockList);
2838 rc = pTask->rc();
2839 AssertComRC(rc);
2840 if (FAILED(rc))
2841 throw rc;
2842 }
2843 catch (HRESULT aRC) { rc = aRC; }
2844
2845 if (SUCCEEDED(rc))
2846 {
2847 rc = i_startThread(pTask);
2848
2849 if (SUCCEEDED(rc))
2850 pProgress.queryInterfaceTo(aProgress.asOutParam());
2851 }
2852 else if (pTask != NULL)
2853 delete pTask;
2854
2855 return rc;
2856}
2857
2858HRESULT Medium::reset(ComPtr<IProgress> &aProgress)
2859{
2860 HRESULT rc = S_OK;
2861 ComObjPtr<Progress> pProgress;
2862 Medium::Task *pTask = NULL;
2863
2864 try
2865 {
2866 /* canClose() needs the tree lock */
2867 AutoMultiWriteLock2 multilock(&m->pVirtualBox->i_getMediaTreeLockHandle(),
2868 this->lockHandle()
2869 COMMA_LOCKVAL_SRC_POS);
2870
2871 LogFlowThisFunc(("ENTER for medium %s\n", m->strLocationFull.c_str()));
2872
2873 if (m->pParent.isNull())
2874 throw setError(VBOX_E_NOT_SUPPORTED,
2875 tr("Medium type of '%s' is not differencing"),
2876 m->strLocationFull.c_str());
2877
2878 rc = i_canClose();
2879 if (FAILED(rc))
2880 throw rc;
2881
2882 /* Build the medium lock list. */
2883 MediumLockList *pMediumLockList(new MediumLockList());
2884 multilock.release();
2885 rc = i_createMediumLockList(true /* fFailIfInaccessible */,
2886 true /* fMediumLockWrite */,
2887 NULL,
2888 *pMediumLockList);
2889 multilock.acquire();
2890 if (FAILED(rc))
2891 {
2892 delete pMediumLockList;
2893 throw rc;
2894 }
2895
2896 multilock.release();
2897 rc = pMediumLockList->Lock();
2898 multilock.acquire();
2899 if (FAILED(rc))
2900 {
2901 delete pMediumLockList;
2902 throw setError(rc,
2903 tr("Failed to lock media when resetting '%s'"),
2904 i_getLocationFull().c_str());
2905 }
2906
2907 pProgress.createObject();
2908 rc = pProgress->init(m->pVirtualBox,
2909 static_cast<IMedium*>(this),
2910 BstrFmt(tr("Resetting differencing medium '%s'"), m->strLocationFull.c_str()).raw(),
2911 FALSE /* aCancelable */);
2912 if (FAILED(rc))
2913 throw rc;
2914
2915 /* setup task object to carry out the operation asynchronously */
2916 pTask = new Medium::ResetTask(this, pProgress, pMediumLockList);
2917 rc = pTask->rc();
2918 AssertComRC(rc);
2919 if (FAILED(rc))
2920 throw rc;
2921 }
2922 catch (HRESULT aRC) { rc = aRC; }
2923
2924 if (SUCCEEDED(rc))
2925 {
2926 rc = i_startThread(pTask);
2927
2928 if (SUCCEEDED(rc))
2929 pProgress.queryInterfaceTo(aProgress.asOutParam());
2930 }
2931 else if (pTask != NULL)
2932 delete pTask;
2933
2934 LogFlowThisFunc(("LEAVE, rc=%Rhrc\n", rc));
2935
2936 return rc;
2937}
2938
2939////////////////////////////////////////////////////////////////////////////////
2940//
2941// Medium public internal methods
2942//
2943////////////////////////////////////////////////////////////////////////////////
2944
2945/**
2946 * Internal method to return the medium's parent medium. Must have caller + locking!
2947 * @return
2948 */
2949const ComObjPtr<Medium>& Medium::i_getParent() const
2950{
2951 return m->pParent;
2952}
2953
2954/**
2955 * Internal method to return the medium's list of child media. Must have caller + locking!
2956 * @return
2957 */
2958const MediaList& Medium::i_getChildren() const
2959{
2960 return m->llChildren;
2961}
2962
2963/**
2964 * Internal method to return the medium's GUID. Must have caller + locking!
2965 * @return
2966 */
2967const Guid& Medium::i_getId() const
2968{
2969 return m->id;
2970}
2971
2972/**
2973 * Internal method to return the medium's state. Must have caller + locking!
2974 * @return
2975 */
2976MediumState_T Medium::i_getState() const
2977{
2978 return m->state;
2979}
2980
2981/**
2982 * Internal method to return the medium's variant. Must have caller + locking!
2983 * @return
2984 */
2985MediumVariant_T Medium::i_getVariant() const
2986{
2987 return m->variant;
2988}
2989
2990/**
2991 * Internal method which returns true if this medium represents a host drive.
2992 * @return
2993 */
2994bool Medium::i_isHostDrive() const
2995{
2996 return m->hostDrive;
2997}
2998
2999/**
3000 * Internal method to return the medium's full location. Must have caller + locking!
3001 * @return
3002 */
3003const Utf8Str& Medium::i_getLocationFull() const
3004{
3005 return m->strLocationFull;
3006}
3007
3008/**
3009 * Internal method to return the medium's format string. Must have caller + locking!
3010 * @return
3011 */
3012const Utf8Str& Medium::i_getFormat() const
3013{
3014 return m->strFormat;
3015}
3016
3017/**
3018 * Internal method to return the medium's format object. Must have caller + locking!
3019 * @return
3020 */
3021const ComObjPtr<MediumFormat>& Medium::i_getMediumFormat() const
3022{
3023 return m->formatObj;
3024}
3025
3026/**
3027 * Internal method that returns true if the medium is represented by a file on the host disk
3028 * (and not iSCSI or something).
3029 * @return
3030 */
3031bool Medium::i_isMediumFormatFile() const
3032{
3033 if ( m->formatObj
3034 && (m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File)
3035 )
3036 return true;
3037 return false;
3038}
3039
3040/**
3041 * Internal method to return the medium's size. Must have caller + locking!
3042 * @return
3043 */
3044uint64_t Medium::i_getSize() const
3045{
3046 return m->size;
3047}
3048
3049/**
3050 * Returns the medium device type. Must have caller + locking!
3051 * @return
3052 */
3053DeviceType_T Medium::i_getDeviceType() const
3054{
3055 return m->devType;
3056}
3057
3058/**
3059 * Returns the medium type. Must have caller + locking!
3060 * @return
3061 */
3062MediumType_T Medium::i_getType() const
3063{
3064 return m->type;
3065}
3066
3067/**
3068 * Returns a short version of the location attribute.
3069 *
3070 * @note Must be called from under this object's read or write lock.
3071 */
3072Utf8Str Medium::i_getName()
3073{
3074 Utf8Str name = RTPathFilename(m->strLocationFull.c_str());
3075 return name;
3076}
3077
3078/**
3079 * This adds the given UUID to the list of media registries in which this
3080 * medium should be registered. The UUID can either be a machine UUID,
3081 * to add a machine registry, or the global registry UUID as returned by
3082 * VirtualBox::getGlobalRegistryId().
3083 *
3084 * Note that for hard disks, this method does nothing if the medium is
3085 * already in another registry to avoid having hard disks in more than
3086 * one registry, which causes trouble with keeping diff images in sync.
3087 * See getFirstRegistryMachineId() for details.
3088 *
3089 * If fRecurse == true, then the media tree lock must be held for reading.
3090 *
3091 * @param id
3092 * @param fRecurse If true, recurses into child media to make sure the whole tree has registries in sync.
3093 * @return true if the registry was added; false if the given id was already on the list.
3094 */
3095bool Medium::i_addRegistry(const Guid& id, bool fRecurse)
3096{
3097 AutoCaller autoCaller(this);
3098 if (FAILED(autoCaller.rc()))
3099 return false;
3100 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3101
3102 bool fAdd = true;
3103
3104 // hard disks cannot be in more than one registry
3105 if ( m->devType == DeviceType_HardDisk
3106 && m->llRegistryIDs.size() > 0)
3107 fAdd = false;
3108
3109 // no need to add the UUID twice
3110 if (fAdd)
3111 {
3112 for (GuidList::const_iterator it = m->llRegistryIDs.begin();
3113 it != m->llRegistryIDs.end();
3114 ++it)
3115 {
3116 if ((*it) == id)
3117 {
3118 fAdd = false;
3119 break;
3120 }
3121 }
3122 }
3123
3124 if (fAdd)
3125 m->llRegistryIDs.push_back(id);
3126
3127 if (fRecurse)
3128 {
3129 // Get private list of children and release medium lock straight away.
3130 MediaList llChildren(m->llChildren);
3131 alock.release();
3132
3133 for (MediaList::iterator it = llChildren.begin();
3134 it != llChildren.end();
3135 ++it)
3136 {
3137 Medium *pChild = *it;
3138 fAdd |= pChild->i_addRegistry(id, true);
3139 }
3140 }
3141
3142 return fAdd;
3143}
3144
3145/**
3146 * Removes the given UUID from the list of media registry UUIDs. Returns true
3147 * if found or false if not.
3148 *
3149 * If fRecurse == true, then the media tree lock must be held for reading.
3150 *
3151 * @param id
3152 * @param fRecurse If true, recurses into child media to make sure the whole tree has registries in sync.
3153 * @return
3154 */
3155bool Medium::i_removeRegistry(const Guid& id, bool fRecurse)
3156{
3157 AutoCaller autoCaller(this);
3158 if (FAILED(autoCaller.rc()))
3159 return false;
3160 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3161
3162 bool fRemove = false;
3163
3164 for (GuidList::iterator it = m->llRegistryIDs.begin();
3165 it != m->llRegistryIDs.end();
3166 ++it)
3167 {
3168 if ((*it) == id)
3169 {
3170 m->llRegistryIDs.erase(it);
3171 fRemove = true;
3172 break;
3173 }
3174 }
3175
3176 if (fRecurse)
3177 {
3178 // Get private list of children and release medium lock straight away.
3179 MediaList llChildren(m->llChildren);
3180 alock.release();
3181
3182 for (MediaList::iterator it = llChildren.begin();
3183 it != llChildren.end();
3184 ++it)
3185 {
3186 Medium *pChild = *it;
3187 fRemove |= pChild->i_removeRegistry(id, true);
3188 }
3189 }
3190
3191 return fRemove;
3192}
3193
3194/**
3195 * Returns true if id is in the list of media registries for this medium.
3196 *
3197 * Must have caller + read locking!
3198 *
3199 * @param id
3200 * @return
3201 */
3202bool Medium::i_isInRegistry(const Guid& id)
3203{
3204 for (GuidList::const_iterator it = m->llRegistryIDs.begin();
3205 it != m->llRegistryIDs.end();
3206 ++it)
3207 {
3208 if (*it == id)
3209 return true;
3210 }
3211
3212 return false;
3213}
3214
3215/**
3216 * Internal method to return the medium's first registry machine (i.e. the machine in whose
3217 * machine XML this medium is listed).
3218 *
3219 * Every attached medium must now (4.0) reside in at least one media registry, which is identified
3220 * by a UUID. This is either a machine UUID if the machine is from 4.0 or newer, in which case
3221 * machines have their own media registries, or it is the pseudo-UUID of the VirtualBox
3222 * object if the machine is old and still needs the global registry in VirtualBox.xml.
3223 *
3224 * By definition, hard disks may only be in one media registry, in which all its children
3225 * will be stored as well. Otherwise we run into problems with having keep multiple registries
3226 * in sync. (This is the "cloned VM" case in which VM1 may link to the disks of VM2; in this
3227 * case, only VM2's registry is used for the disk in question.)
3228 *
3229 * If there is no medium registry, particularly if the medium has not been attached yet, this
3230 * does not modify uuid and returns false.
3231 *
3232 * ISOs and RAWs, by contrast, can be in more than one repository to make things easier for
3233 * the user.
3234 *
3235 * Must have caller + locking!
3236 *
3237 * @param uuid Receives first registry machine UUID, if available.
3238 * @return true if uuid was set.
3239 */
3240bool Medium::i_getFirstRegistryMachineId(Guid &uuid) const
3241{
3242 if (m->llRegistryIDs.size())
3243 {
3244 uuid = m->llRegistryIDs.front();
3245 return true;
3246 }
3247 return false;
3248}
3249
3250/**
3251 * Marks all the registries in which this medium is registered as modified.
3252 */
3253void Medium::i_markRegistriesModified()
3254{
3255 AutoCaller autoCaller(this);
3256 if (FAILED(autoCaller.rc())) return;
3257
3258 // Get local copy, as keeping the lock over VirtualBox::markRegistryModified
3259 // causes trouble with the lock order
3260 GuidList llRegistryIDs;
3261 {
3262 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3263 llRegistryIDs = m->llRegistryIDs;
3264 }
3265
3266 autoCaller.release();
3267
3268 /* Save the error information now, the implicit restore when this goes
3269 * out of scope will throw away spurious additional errors created below. */
3270 ErrorInfoKeeper eik;
3271 for (GuidList::const_iterator it = llRegistryIDs.begin();
3272 it != llRegistryIDs.end();
3273 ++it)
3274 {
3275 m->pVirtualBox->i_markRegistryModified(*it);
3276 }
3277}
3278
3279/**
3280 * Adds the given machine and optionally the snapshot to the list of the objects
3281 * this medium is attached to.
3282 *
3283 * @param aMachineId Machine ID.
3284 * @param aSnapshotId Snapshot ID; when non-empty, adds a snapshot attachment.
3285 */
3286HRESULT Medium::i_addBackReference(const Guid &aMachineId,
3287 const Guid &aSnapshotId /*= Guid::Empty*/)
3288{
3289 AssertReturn(aMachineId.isValid(), E_FAIL);
3290
3291 LogFlowThisFunc(("ENTER, aMachineId: {%RTuuid}, aSnapshotId: {%RTuuid}\n", aMachineId.raw(), aSnapshotId.raw()));
3292
3293 AutoCaller autoCaller(this);
3294 AssertComRCReturnRC(autoCaller.rc());
3295
3296 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3297
3298 switch (m->state)
3299 {
3300 case MediumState_Created:
3301 case MediumState_Inaccessible:
3302 case MediumState_LockedRead:
3303 case MediumState_LockedWrite:
3304 break;
3305
3306 default:
3307 return i_setStateError();
3308 }
3309
3310 if (m->numCreateDiffTasks > 0)
3311 return setError(VBOX_E_OBJECT_IN_USE,
3312 tr("Cannot attach medium '%s' {%RTuuid}: %u differencing child media are being created"),
3313 m->strLocationFull.c_str(),
3314 m->id.raw(),
3315 m->numCreateDiffTasks);
3316
3317 BackRefList::iterator it = std::find_if(m->backRefs.begin(),
3318 m->backRefs.end(),
3319 BackRef::EqualsTo(aMachineId));
3320 if (it == m->backRefs.end())
3321 {
3322 BackRef ref(aMachineId, aSnapshotId);
3323 m->backRefs.push_back(ref);
3324
3325 return S_OK;
3326 }
3327
3328 // if the caller has not supplied a snapshot ID, then we're attaching
3329 // to a machine a medium which represents the machine's current state,
3330 // so set the flag
3331
3332 if (aSnapshotId.isZero())
3333 {
3334 /* sanity: no duplicate attachments */
3335 if (it->fInCurState)
3336 return setError(VBOX_E_OBJECT_IN_USE,
3337 tr("Cannot attach medium '%s' {%RTuuid}: medium is already associated with the current state of machine uuid {%RTuuid}!"),
3338 m->strLocationFull.c_str(),
3339 m->id.raw(),
3340 aMachineId.raw());
3341 it->fInCurState = true;
3342
3343 return S_OK;
3344 }
3345
3346 // otherwise: a snapshot medium is being attached
3347
3348 /* sanity: no duplicate attachments */
3349 for (GuidList::const_iterator jt = it->llSnapshotIds.begin();
3350 jt != it->llSnapshotIds.end();
3351 ++jt)
3352 {
3353 const Guid &idOldSnapshot = *jt;
3354
3355 if (idOldSnapshot == aSnapshotId)
3356 {
3357#ifdef DEBUG
3358 i_dumpBackRefs();
3359#endif
3360 return setError(VBOX_E_OBJECT_IN_USE,
3361 tr("Cannot attach medium '%s' {%RTuuid} from snapshot '%RTuuid': medium is already in use by this snapshot!"),
3362 m->strLocationFull.c_str(),
3363 m->id.raw(),
3364 aSnapshotId.raw());
3365 }
3366 }
3367
3368 it->llSnapshotIds.push_back(aSnapshotId);
3369 // Do not touch fInCurState, as the image may be attached to the current
3370 // state *and* a snapshot, otherwise we lose the current state association!
3371
3372 LogFlowThisFuncLeave();
3373
3374 return S_OK;
3375}
3376
3377/**
3378 * Removes the given machine and optionally the snapshot from the list of the
3379 * objects this medium is attached to.
3380 *
3381 * @param aMachineId Machine ID.
3382 * @param aSnapshotId Snapshot ID; when non-empty, removes the snapshot
3383 * attachment.
3384 */
3385HRESULT Medium::i_removeBackReference(const Guid &aMachineId,
3386 const Guid &aSnapshotId /*= Guid::Empty*/)
3387{
3388 AssertReturn(aMachineId.isValid(), E_FAIL);
3389
3390 AutoCaller autoCaller(this);
3391 AssertComRCReturnRC(autoCaller.rc());
3392
3393 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3394
3395 BackRefList::iterator it =
3396 std::find_if(m->backRefs.begin(), m->backRefs.end(),
3397 BackRef::EqualsTo(aMachineId));
3398 AssertReturn(it != m->backRefs.end(), E_FAIL);
3399
3400 if (aSnapshotId.isZero())
3401 {
3402 /* remove the current state attachment */
3403 it->fInCurState = false;
3404 }
3405 else
3406 {
3407 /* remove the snapshot attachment */
3408 GuidList::iterator jt = std::find(it->llSnapshotIds.begin(),
3409 it->llSnapshotIds.end(),
3410 aSnapshotId);
3411
3412 AssertReturn(jt != it->llSnapshotIds.end(), E_FAIL);
3413 it->llSnapshotIds.erase(jt);
3414 }
3415
3416 /* if the backref becomes empty, remove it */
3417 if (it->fInCurState == false && it->llSnapshotIds.size() == 0)
3418 m->backRefs.erase(it);
3419
3420 return S_OK;
3421}
3422
3423/**
3424 * Internal method to return the medium's list of backrefs. Must have caller + locking!
3425 * @return
3426 */
3427const Guid* Medium::i_getFirstMachineBackrefId() const
3428{
3429 if (!m->backRefs.size())
3430 return NULL;
3431
3432 return &m->backRefs.front().machineId;
3433}
3434
3435/**
3436 * Internal method which returns a machine that either this medium or one of its children
3437 * is attached to. This is used for finding a replacement media registry when an existing
3438 * media registry is about to be deleted in VirtualBox::unregisterMachine().
3439 *
3440 * Must have caller + locking, *and* caller must hold the media tree lock!
3441 * @return
3442 */
3443const Guid* Medium::i_getAnyMachineBackref() const
3444{
3445 if (m->backRefs.size())
3446 return &m->backRefs.front().machineId;
3447
3448 for (MediaList::iterator it = m->llChildren.begin();
3449 it != m->llChildren.end();
3450 ++it)
3451 {
3452 Medium *pChild = *it;
3453 // recurse for this child
3454 const Guid* puuid;
3455 if ((puuid = pChild->i_getAnyMachineBackref()))
3456 return puuid;
3457 }
3458
3459 return NULL;
3460}
3461
3462const Guid* Medium::i_getFirstMachineBackrefSnapshotId() const
3463{
3464 if (!m->backRefs.size())
3465 return NULL;
3466
3467 const BackRef &ref = m->backRefs.front();
3468 if (!ref.llSnapshotIds.size())
3469 return NULL;
3470
3471 return &ref.llSnapshotIds.front();
3472}
3473
3474size_t Medium::i_getMachineBackRefCount() const
3475{
3476 return m->backRefs.size();
3477}
3478
3479#ifdef DEBUG
3480/**
3481 * Debugging helper that gets called after VirtualBox initialization that writes all
3482 * machine backreferences to the debug log.
3483 */
3484void Medium::i_dumpBackRefs()
3485{
3486 AutoCaller autoCaller(this);
3487 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3488
3489 LogFlowThisFunc(("Dumping backrefs for medium '%s':\n", m->strLocationFull.c_str()));
3490
3491 for (BackRefList::iterator it2 = m->backRefs.begin();
3492 it2 != m->backRefs.end();
3493 ++it2)
3494 {
3495 const BackRef &ref = *it2;
3496 LogFlowThisFunc((" Backref from machine {%RTuuid} (fInCurState: %d)\n", ref.machineId.raw(), ref.fInCurState));
3497
3498 for (GuidList::const_iterator jt2 = it2->llSnapshotIds.begin();
3499 jt2 != it2->llSnapshotIds.end();
3500 ++jt2)
3501 {
3502 const Guid &id = *jt2;
3503 LogFlowThisFunc((" Backref from snapshot {%RTuuid}\n", id.raw()));
3504 }
3505 }
3506}
3507#endif
3508
3509/**
3510 * Checks if the given change of \a aOldPath to \a aNewPath affects the location
3511 * of this media and updates it if necessary to reflect the new location.
3512 *
3513 * @param aOldPath Old path (full).
3514 * @param aNewPath New path (full).
3515 *
3516 * @note Locks this object for writing.
3517 */
3518HRESULT Medium::i_updatePath(const Utf8Str &strOldPath, const Utf8Str &strNewPath)
3519{
3520 AssertReturn(!strOldPath.isEmpty(), E_FAIL);
3521 AssertReturn(!strNewPath.isEmpty(), E_FAIL);
3522
3523 AutoCaller autoCaller(this);
3524 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3525
3526 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3527
3528 LogFlowThisFunc(("locationFull.before='%s'\n", m->strLocationFull.c_str()));
3529
3530 const char *pcszMediumPath = m->strLocationFull.c_str();
3531
3532 if (RTPathStartsWith(pcszMediumPath, strOldPath.c_str()))
3533 {
3534 Utf8Str newPath(strNewPath);
3535 newPath.append(pcszMediumPath + strOldPath.length());
3536 unconst(m->strLocationFull) = newPath;
3537
3538 LogFlowThisFunc(("locationFull.after='%s'\n", m->strLocationFull.c_str()));
3539 // we changed something
3540 return S_OK;
3541 }
3542
3543 // no change was necessary, signal error which the caller needs to interpret
3544 return VBOX_E_FILE_ERROR;
3545}
3546
3547/**
3548 * Returns the base medium of the media chain this medium is part of.
3549 *
3550 * The base medium is found by walking up the parent-child relationship axis.
3551 * If the medium doesn't have a parent (i.e. it's a base medium), it
3552 * returns itself in response to this method.
3553 *
3554 * @param aLevel Where to store the number of ancestors of this medium
3555 * (zero for the base), may be @c NULL.
3556 *
3557 * @note Locks medium tree for reading.
3558 */
3559ComObjPtr<Medium> Medium::i_getBase(uint32_t *aLevel /*= NULL*/)
3560{
3561 ComObjPtr<Medium> pBase;
3562
3563 /* it is possible that some previous/concurrent uninit has already cleared
3564 * the pVirtualBox reference, and in this case we don't need to continue */
3565 ComObjPtr<VirtualBox> pVirtualBox(m->pVirtualBox);
3566 if (!pVirtualBox)
3567 return pBase;
3568
3569 /* we access mParent */
3570 AutoReadLock treeLock(pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3571
3572 AutoCaller autoCaller(this);
3573 AssertReturn(autoCaller.isOk(), pBase);
3574
3575 pBase = this;
3576 uint32_t level = 0;
3577
3578 if (m->pParent)
3579 {
3580 for (;;)
3581 {
3582 AutoCaller baseCaller(pBase);
3583 AssertReturn(baseCaller.isOk(), pBase);
3584
3585 if (pBase->m->pParent.isNull())
3586 break;
3587
3588 pBase = pBase->m->pParent;
3589 ++level;
3590 }
3591 }
3592
3593 if (aLevel != NULL)
3594 *aLevel = level;
3595
3596 return pBase;
3597}
3598
3599/**
3600 * Returns @c true if this medium cannot be modified because it has
3601 * dependents (children) or is part of the snapshot. Related to the medium
3602 * type and posterity, not to the current media state.
3603 *
3604 * @note Locks this object and medium tree for reading.
3605 */
3606bool Medium::i_isReadOnly()
3607{
3608 AutoCaller autoCaller(this);
3609 AssertComRCReturn(autoCaller.rc(), false);
3610
3611 /* we access children */
3612 AutoReadLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3613
3614 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3615
3616 switch (m->type)
3617 {
3618 case MediumType_Normal:
3619 {
3620 if (i_getChildren().size() != 0)
3621 return true;
3622
3623 for (BackRefList::const_iterator it = m->backRefs.begin();
3624 it != m->backRefs.end(); ++it)
3625 if (it->llSnapshotIds.size() != 0)
3626 return true;
3627
3628 if (m->variant & MediumVariant_VmdkStreamOptimized)
3629 return true;
3630
3631 return false;
3632 }
3633 case MediumType_Immutable:
3634 case MediumType_MultiAttach:
3635 return true;
3636 case MediumType_Writethrough:
3637 case MediumType_Shareable:
3638 case MediumType_Readonly: /* explicit readonly media has no diffs */
3639 return false;
3640 default:
3641 break;
3642 }
3643
3644 AssertFailedReturn(false);
3645}
3646
3647/**
3648 * Internal method to return the medium's size. Must have caller + locking!
3649 * @return
3650 */
3651void Medium::i_updateId(const Guid &id)
3652{
3653 unconst(m->id) = id;
3654}
3655
3656/**
3657 * Saves medium data by appending a new child node to the given
3658 * parent XML settings node.
3659 *
3660 * @param data Settings struct to be updated.
3661 * @param strHardDiskFolder Folder for which paths should be relative.
3662 *
3663 * @note Locks this object, medium tree and children for reading.
3664 */
3665HRESULT Medium::i_saveSettings(settings::Medium &data,
3666 const Utf8Str &strHardDiskFolder)
3667{
3668 AutoCaller autoCaller(this);
3669 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3670
3671 /* we access mParent */
3672 AutoReadLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3673
3674 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3675
3676 data.uuid = m->id;
3677
3678 // make path relative if needed
3679 if ( !strHardDiskFolder.isEmpty()
3680 && RTPathStartsWith(m->strLocationFull.c_str(), strHardDiskFolder.c_str())
3681 )
3682 data.strLocation = m->strLocationFull.substr(strHardDiskFolder.length() + 1);
3683 else
3684 data.strLocation = m->strLocationFull;
3685 data.strFormat = m->strFormat;
3686
3687 /* optional, only for diffs, default is false */
3688 if (m->pParent)
3689 data.fAutoReset = m->autoReset;
3690 else
3691 data.fAutoReset = false;
3692
3693 /* optional */
3694 data.strDescription = m->strDescription;
3695
3696 /* optional properties */
3697 data.properties.clear();
3698
3699 /* handle iSCSI initiator secrets transparently */
3700 bool fHaveInitiatorSecretEncrypted = false;
3701 Utf8Str strCiphertext;
3702 settings::StringsMap::const_iterator itPln = m->mapProperties.find("InitiatorSecret");
3703 if ( itPln != m->mapProperties.end()
3704 && !itPln->second.isEmpty())
3705 {
3706 /* Encrypt the plain secret. If that does not work (i.e. no or wrong settings key
3707 * specified), just use the encrypted secret (if there is any). */
3708 int rc = m->pVirtualBox->i_encryptSetting(itPln->second, &strCiphertext);
3709 if (RT_SUCCESS(rc))
3710 fHaveInitiatorSecretEncrypted = true;
3711 }
3712 for (settings::StringsMap::const_iterator it = m->mapProperties.begin();
3713 it != m->mapProperties.end();
3714 ++it)
3715 {
3716 /* only save properties that have non-default values */
3717 if (!it->second.isEmpty())
3718 {
3719 const Utf8Str &name = it->first;
3720 const Utf8Str &value = it->second;
3721 /* do NOT store the plain InitiatorSecret */
3722 if ( !fHaveInitiatorSecretEncrypted
3723 || !name.equals("InitiatorSecret"))
3724 data.properties[name] = value;
3725 }
3726 }
3727 if (fHaveInitiatorSecretEncrypted)
3728 data.properties["InitiatorSecretEncrypted"] = strCiphertext;
3729
3730 /* only for base media */
3731 if (m->pParent.isNull())
3732 data.hdType = m->type;
3733
3734 /* save all children */
3735 for (MediaList::const_iterator it = i_getChildren().begin();
3736 it != i_getChildren().end();
3737 ++it)
3738 {
3739 settings::Medium med;
3740 HRESULT rc = (*it)->i_saveSettings(med, strHardDiskFolder);
3741 AssertComRCReturnRC(rc);
3742 data.llChildren.push_back(med);
3743 }
3744
3745 return S_OK;
3746}
3747
3748/**
3749 * Constructs a medium lock list for this medium. The lock is not taken.
3750 *
3751 * @note Caller MUST NOT hold the media tree or medium lock.
3752 *
3753 * @param fFailIfInaccessible If true, this fails with an error if a medium is inaccessible. If false,
3754 * inaccessible media are silently skipped and not locked (i.e. their state remains "Inaccessible");
3755 * this is necessary for a VM's removable media VM startup for which we do not want to fail.
3756 * @param fMediumLockWrite Whether to associate a write lock with this medium.
3757 * @param pToBeParent Medium which will become the parent of this medium.
3758 * @param mediumLockList Where to store the resulting list.
3759 */
3760HRESULT Medium::i_createMediumLockList(bool fFailIfInaccessible,
3761 bool fMediumLockWrite,
3762 Medium *pToBeParent,
3763 MediumLockList &mediumLockList)
3764{
3765 Assert(!m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
3766 Assert(!isWriteLockOnCurrentThread());
3767
3768 AutoCaller autoCaller(this);
3769 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3770
3771 HRESULT rc = S_OK;
3772
3773 /* paranoid sanity checking if the medium has a to-be parent medium */
3774 if (pToBeParent)
3775 {
3776 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3777 ComAssertRet(i_getParent().isNull(), E_FAIL);
3778 ComAssertRet(i_getChildren().size() == 0, E_FAIL);
3779 }
3780
3781 ErrorInfoKeeper eik;
3782 MultiResult mrc(S_OK);
3783
3784 ComObjPtr<Medium> pMedium = this;
3785 while (!pMedium.isNull())
3786 {
3787 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
3788
3789 /* Accessibility check must be first, otherwise locking interferes
3790 * with getting the medium state. Lock lists are not created for
3791 * fun, and thus getting the medium status is no luxury. */
3792 MediumState_T mediumState = pMedium->i_getState();
3793 if (mediumState == MediumState_Inaccessible)
3794 {
3795 alock.release();
3796 rc = pMedium->i_queryInfo(false /* fSetImageId */, false /* fSetParentId */);
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)
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 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 {
5438 AutoReadLock qlock(m->queryInfoSem COMMA_LOCKVAL_SRC_POS);
5439 }
5440 alock.acquire();
5441 }
5442
5443 return S_OK;
5444 }
5445
5446 bool success = false;
5447 Utf8Str lastAccessError;
5448
5449 /* are we dealing with a new medium constructed using the existing
5450 * location? */
5451 bool isImport = m->id.isZero();
5452 unsigned uOpenFlags = VD_OPEN_FLAGS_INFO;
5453
5454 /* Note that we don't use VD_OPEN_FLAGS_READONLY when opening new
5455 * media because that would prevent necessary modifications
5456 * when opening media of some third-party formats for the first
5457 * time in VirtualBox (such as VMDK for which VDOpen() needs to
5458 * generate an UUID if it is missing) */
5459 if ( m->hddOpenMode == OpenReadOnly
5460 || m->type == MediumType_Readonly
5461 || (!isImport && !fSetImageId && !fSetParentId)
5462 )
5463 uOpenFlags |= VD_OPEN_FLAGS_READONLY;
5464
5465 /* Open shareable medium with the appropriate flags */
5466 if (m->type == MediumType_Shareable)
5467 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
5468
5469 /* Lock the medium, which makes the behavior much more consistent */
5470 alock.release();
5471 ComPtr<IToken> pToken;
5472 if (uOpenFlags & (VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SHAREABLE))
5473 rc = LockRead(pToken.asOutParam());
5474 else
5475 rc = LockWrite(pToken.asOutParam());
5476 if (FAILED(rc)) return rc;
5477 alock.acquire();
5478
5479 /* Copies of the input state fields which are not read-only,
5480 * as we're dropping the lock. CAUTION: be extremely careful what
5481 * you do with the contents of this medium object, as you will
5482 * create races if there are concurrent changes. */
5483 Utf8Str format(m->strFormat);
5484 Utf8Str location(m->strLocationFull);
5485 ComObjPtr<MediumFormat> formatObj = m->formatObj;
5486
5487 /* "Output" values which can't be set because the lock isn't held
5488 * at the time the values are determined. */
5489 Guid mediumId = m->id;
5490 uint64_t mediumSize = 0;
5491 uint64_t mediumLogicalSize = 0;
5492
5493 /* Flag whether a base image has a non-zero parent UUID and thus
5494 * need repairing after it was closed again. */
5495 bool fRepairImageZeroParentUuid = false;
5496
5497 /* release the object lock before a lengthy operation, and take the
5498 * opportunity to have a media tree lock, too, which isn't held initially */
5499 m->queryInfoRunning = true;
5500 alock.release();
5501 Assert(!isWriteLockOnCurrentThread());
5502 Assert(!m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
5503 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
5504 treeLock.release();
5505
5506 /* Note that taking the queryInfoSem after leaving the object lock above
5507 * can lead to short spinning of the loops waiting for queryInfo() to
5508 * complete. This is unavoidable since the other order causes a lock order
5509 * violation: here it would be requesting the object lock (at the beginning
5510 * of the method), then queryInfoSem, and below the other way round. */
5511 AutoWriteLock qlock(m->queryInfoSem COMMA_LOCKVAL_SRC_POS);
5512
5513 try
5514 {
5515 /* skip accessibility checks for host drives */
5516 if (m->hostDrive)
5517 {
5518 success = true;
5519 throw S_OK;
5520 }
5521
5522 PVBOXHDD hdd;
5523 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
5524 ComAssertRCThrow(vrc, E_FAIL);
5525
5526 try
5527 {
5528 /** @todo This kind of opening of media is assuming that diff
5529 * media can be opened as base media. Should be documented that
5530 * it must work for all medium format backends. */
5531 vrc = VDOpen(hdd,
5532 format.c_str(),
5533 location.c_str(),
5534 uOpenFlags | m->uOpenFlagsDef,
5535 m->vdImageIfaces);
5536 if (RT_FAILURE(vrc))
5537 {
5538 lastAccessError = Utf8StrFmt(tr("Could not open the medium '%s'%s"),
5539 location.c_str(), i_vdError(vrc).c_str());
5540 throw S_OK;
5541 }
5542
5543 if (formatObj->i_getCapabilities() & MediumFormatCapabilities_Uuid)
5544 {
5545 /* Modify the UUIDs if necessary. The associated fields are
5546 * not modified by other code, so no need to copy. */
5547 if (fSetImageId)
5548 {
5549 alock.acquire();
5550 vrc = VDSetUuid(hdd, 0, m->uuidImage.raw());
5551 alock.release();
5552 if (RT_FAILURE(vrc))
5553 {
5554 lastAccessError = Utf8StrFmt(tr("Could not update the UUID of medium '%s'%s"),
5555 location.c_str(), i_vdError(vrc).c_str());
5556 throw S_OK;
5557 }
5558 mediumId = m->uuidImage;
5559 }
5560 if (fSetParentId)
5561 {
5562 alock.acquire();
5563 vrc = VDSetParentUuid(hdd, 0, m->uuidParentImage.raw());
5564 alock.release();
5565 if (RT_FAILURE(vrc))
5566 {
5567 lastAccessError = Utf8StrFmt(tr("Could not update the parent UUID of medium '%s'%s"),
5568 location.c_str(), i_vdError(vrc).c_str());
5569 throw S_OK;
5570 }
5571 }
5572 /* zap the information, these are no long-term members */
5573 alock.acquire();
5574 unconst(m->uuidImage).clear();
5575 unconst(m->uuidParentImage).clear();
5576 alock.release();
5577
5578 /* check the UUID */
5579 RTUUID uuid;
5580 vrc = VDGetUuid(hdd, 0, &uuid);
5581 ComAssertRCThrow(vrc, E_FAIL);
5582
5583 if (isImport)
5584 {
5585 mediumId = uuid;
5586
5587 if (mediumId.isZero() && (m->hddOpenMode == OpenReadOnly))
5588 // only when importing a VDMK that has no UUID, create one in memory
5589 mediumId.create();
5590 }
5591 else
5592 {
5593 Assert(!mediumId.isZero());
5594
5595 if (mediumId != uuid)
5596 {
5597 /** @todo r=klaus this always refers to VirtualBox.xml as the medium registry, even for new VMs */
5598 lastAccessError = Utf8StrFmt(
5599 tr("UUID {%RTuuid} of the medium '%s' does not match the value {%RTuuid} stored in the media registry ('%s')"),
5600 &uuid,
5601 location.c_str(),
5602 mediumId.raw(),
5603 m->pVirtualBox->i_settingsFilePath().c_str());
5604 throw S_OK;
5605 }
5606 }
5607 }
5608 else
5609 {
5610 /* the backend does not support storing UUIDs within the
5611 * underlying storage so use what we store in XML */
5612
5613 if (fSetImageId)
5614 {
5615 /* set the UUID if an API client wants to change it */
5616 alock.acquire();
5617 mediumId = m->uuidImage;
5618 alock.release();
5619 }
5620 else if (isImport)
5621 {
5622 /* generate an UUID for an imported UUID-less medium */
5623 mediumId.create();
5624 }
5625 }
5626
5627 /* set the image uuid before the below parent uuid handling code
5628 * might place it somewhere in the media tree, so that the medium
5629 * UUID is valid at this point */
5630 alock.acquire();
5631 if (isImport || fSetImageId)
5632 unconst(m->id) = mediumId;
5633 alock.release();
5634
5635 /* get the medium variant */
5636 unsigned uImageFlags;
5637 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
5638 ComAssertRCThrow(vrc, E_FAIL);
5639 alock.acquire();
5640 m->variant = (MediumVariant_T)uImageFlags;
5641 alock.release();
5642
5643 /* check/get the parent uuid and update corresponding state */
5644 if (uImageFlags & VD_IMAGE_FLAGS_DIFF)
5645 {
5646 RTUUID parentId;
5647 vrc = VDGetParentUuid(hdd, 0, &parentId);
5648 ComAssertRCThrow(vrc, E_FAIL);
5649
5650 /* streamOptimized VMDK images are only accepted as base
5651 * images, as this allows automatic repair of OVF appliances.
5652 * Since such images don't support random writes they will not
5653 * be created for diff images. Only an overly smart user might
5654 * manually create this case. Too bad for him. */
5655 if ( (isImport || fSetParentId)
5656 && !(uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED))
5657 {
5658 /* the parent must be known to us. Note that we freely
5659 * call locking methods of mVirtualBox and parent, as all
5660 * relevant locks must be already held. There may be no
5661 * concurrent access to the just opened medium on other
5662 * threads yet (and init() will fail if this method reports
5663 * MediumState_Inaccessible) */
5664
5665 ComObjPtr<Medium> pParent;
5666 if (RTUuidIsNull(&parentId))
5667 rc = VBOX_E_OBJECT_NOT_FOUND;
5668 else
5669 rc = m->pVirtualBox->i_findHardDiskById(Guid(parentId), false /* aSetError */, &pParent);
5670 if (FAILED(rc))
5671 {
5672 if (fSetImageId && !fSetParentId)
5673 {
5674 /* If the image UUID gets changed for an existing
5675 * image then the parent UUID can be stale. In such
5676 * cases clear the parent information. The parent
5677 * information may/will be re-set later if the
5678 * API client wants to adjust a complete medium
5679 * hierarchy one by one. */
5680 rc = S_OK;
5681 alock.acquire();
5682 RTUuidClear(&parentId);
5683 vrc = VDSetParentUuid(hdd, 0, &parentId);
5684 alock.release();
5685 ComAssertRCThrow(vrc, E_FAIL);
5686 }
5687 else
5688 {
5689 lastAccessError = Utf8StrFmt(tr("Parent medium with UUID {%RTuuid} of the medium '%s' is not found in the media registry ('%s')"),
5690 &parentId, location.c_str(),
5691 m->pVirtualBox->i_settingsFilePath().c_str());
5692 throw S_OK;
5693 }
5694 }
5695
5696 /* we set mParent & children() */
5697 treeLock.acquire();
5698
5699 if (m->pParent)
5700 i_deparent();
5701 i_setParent(pParent);
5702
5703 treeLock.release();
5704 }
5705 else
5706 {
5707 /* we access mParent */
5708 treeLock.acquire();
5709
5710 /* check that parent UUIDs match. Note that there's no need
5711 * for the parent's AutoCaller (our lifetime is bound to
5712 * it) */
5713
5714 if (m->pParent.isNull())
5715 {
5716 /* Due to a bug in VDCopy() in VirtualBox 3.0.0-3.0.14
5717 * and 3.1.0-3.1.8 there are base images out there
5718 * which have a non-zero parent UUID. No point in
5719 * complaining about them, instead automatically
5720 * repair the problem. Later we can bring back the
5721 * error message, but we should wait until really
5722 * most users have repaired their images, either with
5723 * VBoxFixHdd or this way. */
5724#if 1
5725 fRepairImageZeroParentUuid = true;
5726#else /* 0 */
5727 lastAccessError = Utf8StrFmt(
5728 tr("Medium type of '%s' is differencing but it is not associated with any parent medium in the media registry ('%s')"),
5729 location.c_str(),
5730 m->pVirtualBox->settingsFilePath().c_str());
5731 treeLock.release();
5732 throw S_OK;
5733#endif /* 0 */
5734 }
5735
5736 {
5737 AutoReadLock parentLock(m->pParent COMMA_LOCKVAL_SRC_POS);
5738 if ( !fRepairImageZeroParentUuid
5739 && m->pParent->i_getState() != MediumState_Inaccessible
5740 && m->pParent->i_getId() != parentId)
5741 {
5742 /** @todo r=klaus this always refers to VirtualBox.xml as the medium registry, even for new VMs */
5743 lastAccessError = Utf8StrFmt(
5744 tr("Parent UUID {%RTuuid} of the medium '%s' does not match UUID {%RTuuid} of its parent medium stored in the media registry ('%s')"),
5745 &parentId, location.c_str(),
5746 m->pParent->i_getId().raw(),
5747 m->pVirtualBox->i_settingsFilePath().c_str());
5748 parentLock.release();
5749 treeLock.release();
5750 throw S_OK;
5751 }
5752 }
5753
5754 /// @todo NEWMEDIA what to do if the parent is not
5755 /// accessible while the diff is? Probably nothing. The
5756 /// real code will detect the mismatch anyway.
5757
5758 treeLock.release();
5759 }
5760 }
5761
5762 mediumSize = VDGetFileSize(hdd, 0);
5763 mediumLogicalSize = VDGetSize(hdd, 0);
5764
5765 success = true;
5766 }
5767 catch (HRESULT aRC)
5768 {
5769 rc = aRC;
5770 }
5771
5772 vrc = VDDestroy(hdd);
5773 if (RT_FAILURE(vrc))
5774 {
5775 lastAccessError = Utf8StrFmt(tr("Could not update and close the medium '%s'%s"),
5776 location.c_str(), i_vdError(vrc).c_str());
5777 success = false;
5778 throw S_OK;
5779 }
5780 }
5781 catch (HRESULT aRC)
5782 {
5783 rc = aRC;
5784 }
5785
5786 treeLock.acquire();
5787 alock.acquire();
5788
5789 if (success)
5790 {
5791 m->size = mediumSize;
5792 m->logicalSize = mediumLogicalSize;
5793 m->strLastAccessError.setNull();
5794 }
5795 else
5796 {
5797 m->strLastAccessError = lastAccessError;
5798 LogWarningFunc(("'%s' is not accessible (error='%s', rc=%Rhrc, vrc=%Rrc)\n",
5799 location.c_str(), m->strLastAccessError.c_str(),
5800 rc, vrc));
5801 }
5802
5803 /* unblock anyone waiting for the queryInfo results */
5804 qlock.release();
5805 m->queryInfoRunning = false;
5806
5807 /* Set the proper state according to the result of the check */
5808 if (success)
5809 m->preLockState = MediumState_Created;
5810 else
5811 m->preLockState = MediumState_Inaccessible;
5812
5813 pToken->Abandon();
5814 pToken.setNull();
5815
5816 if (FAILED(rc)) return rc;
5817
5818 /* If this is a base image which incorrectly has a parent UUID set,
5819 * repair the image now by zeroing the parent UUID. This is only done
5820 * when we have structural information from a config file, on import
5821 * this is not possible. If someone would accidentally call openMedium
5822 * with a diff image before the base is registered this would destroy
5823 * the diff. Not acceptable. */
5824 if (fRepairImageZeroParentUuid)
5825 {
5826 rc = LockWrite(pToken.asOutParam());
5827 if (FAILED(rc)) return rc;
5828
5829 alock.release();
5830
5831 try
5832 {
5833 PVBOXHDD hdd;
5834 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
5835 ComAssertRCThrow(vrc, E_FAIL);
5836
5837 try
5838 {
5839 vrc = VDOpen(hdd,
5840 format.c_str(),
5841 location.c_str(),
5842 (uOpenFlags & ~VD_OPEN_FLAGS_READONLY) | m->uOpenFlagsDef,
5843 m->vdImageIfaces);
5844 if (RT_FAILURE(vrc))
5845 throw S_OK;
5846
5847 RTUUID zeroParentUuid;
5848 RTUuidClear(&zeroParentUuid);
5849 vrc = VDSetParentUuid(hdd, 0, &zeroParentUuid);
5850 ComAssertRCThrow(vrc, E_FAIL);
5851 }
5852 catch (HRESULT aRC)
5853 {
5854 rc = aRC;
5855 }
5856
5857 VDDestroy(hdd);
5858 }
5859 catch (HRESULT aRC)
5860 {
5861 rc = aRC;
5862 }
5863
5864 pToken->Abandon();
5865 pToken.setNull();
5866 if (FAILED(rc)) return rc;
5867 }
5868
5869 return rc;
5870}
5871
5872/**
5873 * Performs extra checks if the medium can be closed and returns S_OK in
5874 * this case. Otherwise, returns a respective error message. Called by
5875 * Close() under the medium tree lock and the medium lock.
5876 *
5877 * @note Also reused by Medium::Reset().
5878 *
5879 * @note Caller must hold the media tree write lock!
5880 */
5881HRESULT Medium::i_canClose()
5882{
5883 Assert(m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
5884
5885 if (i_getChildren().size() != 0)
5886 return setError(VBOX_E_OBJECT_IN_USE,
5887 tr("Cannot close medium '%s' because it has %d child media"),
5888 m->strLocationFull.c_str(), i_getChildren().size());
5889
5890 return S_OK;
5891}
5892
5893/**
5894 * Unregisters this medium with mVirtualBox. Called by close() under the medium tree lock.
5895 *
5896 * @note Caller must have locked the media tree lock for writing!
5897 */
5898HRESULT Medium::i_unregisterWithVirtualBox()
5899{
5900 /* Note that we need to de-associate ourselves from the parent to let
5901 * unregisterMedium() properly save the registry */
5902
5903 /* we modify mParent and access children */
5904 Assert(m->pVirtualBox->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
5905
5906 Medium *pParentBackup = m->pParent;
5907 AssertReturn(i_getChildren().size() == 0, E_FAIL);
5908 if (m->pParent)
5909 i_deparent();
5910
5911 HRESULT rc = m->pVirtualBox->i_unregisterMedium(this);
5912 if (FAILED(rc))
5913 {
5914 if (pParentBackup)
5915 {
5916 // re-associate with the parent as we are still relatives in the registry
5917 m->pParent = pParentBackup;
5918 m->pParent->m->llChildren.push_back(this);
5919 }
5920 }
5921
5922 return rc;
5923}
5924
5925/**
5926 * Like SetProperty but do not trigger a settings store. Only for internal use!
5927 */
5928HRESULT Medium::i_setPropertyDirect(const Utf8Str &aName, const Utf8Str &aValue)
5929{
5930 AutoCaller autoCaller(this);
5931 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5932
5933 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5934
5935 switch (m->state)
5936 {
5937 case MediumState_Created:
5938 case MediumState_Inaccessible:
5939 break;
5940 default:
5941 return i_setStateError();
5942 }
5943
5944 m->mapProperties[aName] = aValue;
5945
5946 return S_OK;
5947}
5948
5949/**
5950 * Sets the extended error info according to the current media state.
5951 *
5952 * @note Must be called from under this object's write or read lock.
5953 */
5954HRESULT Medium::i_setStateError()
5955{
5956 HRESULT rc = E_FAIL;
5957
5958 switch (m->state)
5959 {
5960 case MediumState_NotCreated:
5961 {
5962 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
5963 tr("Storage for the medium '%s' is not created"),
5964 m->strLocationFull.c_str());
5965 break;
5966 }
5967 case MediumState_Created:
5968 {
5969 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
5970 tr("Storage for the medium '%s' is already created"),
5971 m->strLocationFull.c_str());
5972 break;
5973 }
5974 case MediumState_LockedRead:
5975 {
5976 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
5977 tr("Medium '%s' is locked for reading by another task"),
5978 m->strLocationFull.c_str());
5979 break;
5980 }
5981 case MediumState_LockedWrite:
5982 {
5983 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
5984 tr("Medium '%s' is locked for writing by another task"),
5985 m->strLocationFull.c_str());
5986 break;
5987 }
5988 case MediumState_Inaccessible:
5989 {
5990 /* be in sync with Console::powerUpThread() */
5991 if (!m->strLastAccessError.isEmpty())
5992 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
5993 tr("Medium '%s' is not accessible. %s"),
5994 m->strLocationFull.c_str(), m->strLastAccessError.c_str());
5995 else
5996 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
5997 tr("Medium '%s' is not accessible"),
5998 m->strLocationFull.c_str());
5999 break;
6000 }
6001 case MediumState_Creating:
6002 {
6003 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
6004 tr("Storage for the medium '%s' is being created"),
6005 m->strLocationFull.c_str());
6006 break;
6007 }
6008 case MediumState_Deleting:
6009 {
6010 rc = setError(VBOX_E_INVALID_OBJECT_STATE,
6011 tr("Storage for the medium '%s' is being deleted"),
6012 m->strLocationFull.c_str());
6013 break;
6014 }
6015 default:
6016 {
6017 AssertFailed();
6018 break;
6019 }
6020 }
6021
6022 return rc;
6023}
6024
6025/**
6026 * Sets the value of m->strLocationFull. The given location must be a fully
6027 * qualified path; relative paths are not supported here.
6028 *
6029 * As a special exception, if the specified location is a file path that ends with '/'
6030 * then the file name part will be generated by this method automatically in the format
6031 * '{<uuid>}.<ext>' where <uuid> is a fresh UUID that this method will generate
6032 * and assign to this medium, and <ext> is the default extension for this
6033 * medium's storage format. Note that this procedure requires the media state to
6034 * be NotCreated and will return a failure otherwise.
6035 *
6036 * @param aLocation Location of the storage unit. If the location is a FS-path,
6037 * then it can be relative to the VirtualBox home directory.
6038 * @param aFormat Optional fallback format if it is an import and the format
6039 * cannot be determined.
6040 *
6041 * @note Must be called from under this object's write lock.
6042 */
6043HRESULT Medium::i_setLocation(const Utf8Str &aLocation,
6044 const Utf8Str &aFormat /* = Utf8Str::Empty */)
6045{
6046 AssertReturn(!aLocation.isEmpty(), E_FAIL);
6047
6048 AutoCaller autoCaller(this);
6049 AssertComRCReturnRC(autoCaller.rc());
6050
6051 /* formatObj may be null only when initializing from an existing path and
6052 * no format is known yet */
6053 AssertReturn( (!m->strFormat.isEmpty() && !m->formatObj.isNull())
6054 || ( getObjectState().getState() == ObjectState::InInit
6055 && m->state != MediumState_NotCreated
6056 && m->id.isZero()
6057 && m->strFormat.isEmpty()
6058 && m->formatObj.isNull()),
6059 E_FAIL);
6060
6061 /* are we dealing with a new medium constructed using the existing
6062 * location? */
6063 bool isImport = m->strFormat.isEmpty();
6064
6065 if ( isImport
6066 || ( (m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File)
6067 && !m->hostDrive))
6068 {
6069 Guid id;
6070
6071 Utf8Str locationFull(aLocation);
6072
6073 if (m->state == MediumState_NotCreated)
6074 {
6075 /* must be a file (formatObj must be already known) */
6076 Assert(m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File);
6077
6078 if (RTPathFilename(aLocation.c_str()) == NULL)
6079 {
6080 /* no file name is given (either an empty string or ends with a
6081 * slash), generate a new UUID + file name if the state allows
6082 * this */
6083
6084 ComAssertMsgRet(!m->formatObj->i_getFileExtensions().empty(),
6085 ("Must be at least one extension if it is MediumFormatCapabilities_File\n"),
6086 E_FAIL);
6087
6088 Utf8Str strExt = m->formatObj->i_getFileExtensions().front();
6089 ComAssertMsgRet(!strExt.isEmpty(),
6090 ("Default extension must not be empty\n"),
6091 E_FAIL);
6092
6093 id.create();
6094
6095 locationFull = Utf8StrFmt("%s{%RTuuid}.%s",
6096 aLocation.c_str(), id.raw(), strExt.c_str());
6097 }
6098 }
6099
6100 // we must always have full paths now (if it refers to a file)
6101 if ( ( m->formatObj.isNull()
6102 || m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File)
6103 && !RTPathStartsWithRoot(locationFull.c_str()))
6104 return setError(VBOX_E_FILE_ERROR,
6105 tr("The given path '%s' is not fully qualified"),
6106 locationFull.c_str());
6107
6108 /* detect the backend from the storage unit if importing */
6109 if (isImport)
6110 {
6111 VDTYPE enmType = VDTYPE_INVALID;
6112 char *backendName = NULL;
6113
6114 int vrc = VINF_SUCCESS;
6115
6116 /* is it a file? */
6117 {
6118 RTFILE file;
6119 vrc = RTFileOpen(&file, locationFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
6120 if (RT_SUCCESS(vrc))
6121 RTFileClose(file);
6122 }
6123 if (RT_SUCCESS(vrc))
6124 {
6125 vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
6126 locationFull.c_str(), &backendName, &enmType);
6127 }
6128 else if ( vrc != VERR_FILE_NOT_FOUND
6129 && vrc != VERR_PATH_NOT_FOUND
6130 && vrc != VERR_ACCESS_DENIED
6131 && locationFull != aLocation)
6132 {
6133 /* assume it's not a file, restore the original location */
6134 locationFull = aLocation;
6135 vrc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */,
6136 locationFull.c_str(), &backendName, &enmType);
6137 }
6138
6139 if (RT_FAILURE(vrc))
6140 {
6141 if (vrc == VERR_ACCESS_DENIED)
6142 return setError(VBOX_E_FILE_ERROR,
6143 tr("Permission problem accessing the file for the medium '%s' (%Rrc)"),
6144 locationFull.c_str(), vrc);
6145 else if (vrc == VERR_FILE_NOT_FOUND || vrc == VERR_PATH_NOT_FOUND)
6146 return setError(VBOX_E_FILE_ERROR,
6147 tr("Could not find file for the medium '%s' (%Rrc)"),
6148 locationFull.c_str(), vrc);
6149 else if (aFormat.isEmpty())
6150 return setError(VBOX_E_IPRT_ERROR,
6151 tr("Could not get the storage format of the medium '%s' (%Rrc)"),
6152 locationFull.c_str(), vrc);
6153 else
6154 {
6155 HRESULT rc = i_setFormat(aFormat);
6156 /* setFormat() must not fail since we've just used the backend so
6157 * the format object must be there */
6158 AssertComRCReturnRC(rc);
6159 }
6160 }
6161 else if ( enmType == VDTYPE_INVALID
6162 || m->devType != i_convertToDeviceType(enmType))
6163 {
6164 /*
6165 * The user tried to use a image as a device which is not supported
6166 * by the backend.
6167 */
6168 return setError(E_FAIL,
6169 tr("The medium '%s' can't be used as the requested device type"),
6170 locationFull.c_str());
6171 }
6172 else
6173 {
6174 ComAssertRet(backendName != NULL && *backendName != '\0', E_FAIL);
6175
6176 HRESULT rc = i_setFormat(backendName);
6177 RTStrFree(backendName);
6178
6179 /* setFormat() must not fail since we've just used the backend so
6180 * the format object must be there */
6181 AssertComRCReturnRC(rc);
6182 }
6183 }
6184
6185 m->strLocationFull = locationFull;
6186
6187 /* is it still a file? */
6188 if ( (m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File)
6189 && (m->state == MediumState_NotCreated)
6190 )
6191 /* assign a new UUID (this UUID will be used when calling
6192 * VDCreateBase/VDCreateDiff as a wanted UUID). Note that we
6193 * also do that if we didn't generate it to make sure it is
6194 * either generated by us or reset to null */
6195 unconst(m->id) = id;
6196 }
6197 else
6198 m->strLocationFull = aLocation;
6199
6200 return S_OK;
6201}
6202
6203/**
6204 * Checks that the format ID is valid and sets it on success.
6205 *
6206 * Note that this method will caller-reference the format object on success!
6207 * This reference must be released somewhere to let the MediumFormat object be
6208 * uninitialized.
6209 *
6210 * @note Must be called from under this object's write lock.
6211 */
6212HRESULT Medium::i_setFormat(const Utf8Str &aFormat)
6213{
6214 /* get the format object first */
6215 {
6216 SystemProperties *pSysProps = m->pVirtualBox->i_getSystemProperties();
6217 AutoReadLock propsLock(pSysProps COMMA_LOCKVAL_SRC_POS);
6218
6219 unconst(m->formatObj) = pSysProps->i_mediumFormat(aFormat);
6220 if (m->formatObj.isNull())
6221 return setError(E_INVALIDARG,
6222 tr("Invalid medium storage format '%s'"),
6223 aFormat.c_str());
6224
6225 /* get properties (preinsert them as keys in the map). Note that the
6226 * map doesn't grow over the object life time since the set of
6227 * properties is meant to be constant. */
6228
6229 Assert(m->mapProperties.empty());
6230
6231 for (MediumFormat::PropertyArray::const_iterator it = m->formatObj->i_getProperties().begin();
6232 it != m->formatObj->i_getProperties().end();
6233 ++it)
6234 {
6235 m->mapProperties.insert(std::make_pair(it->strName, Utf8Str::Empty));
6236 }
6237 }
6238
6239 unconst(m->strFormat) = aFormat;
6240
6241 return S_OK;
6242}
6243
6244/**
6245 * Converts the Medium device type to the VD type.
6246 */
6247VDTYPE Medium::i_convertDeviceType()
6248{
6249 VDTYPE enmType;
6250
6251 switch (m->devType)
6252 {
6253 case DeviceType_HardDisk:
6254 enmType = VDTYPE_HDD;
6255 break;
6256 case DeviceType_DVD:
6257 enmType = VDTYPE_DVD;
6258 break;
6259 case DeviceType_Floppy:
6260 enmType = VDTYPE_FLOPPY;
6261 break;
6262 default:
6263 ComAssertFailedRet(VDTYPE_INVALID);
6264 }
6265
6266 return enmType;
6267}
6268
6269/**
6270 * Converts from the VD type to the medium type.
6271 */
6272DeviceType_T Medium::i_convertToDeviceType(VDTYPE enmType)
6273{
6274 DeviceType_T devType;
6275
6276 switch (enmType)
6277 {
6278 case VDTYPE_HDD:
6279 devType = DeviceType_HardDisk;
6280 break;
6281 case VDTYPE_DVD:
6282 devType = DeviceType_DVD;
6283 break;
6284 case VDTYPE_FLOPPY:
6285 devType = DeviceType_Floppy;
6286 break;
6287 default:
6288 ComAssertFailedRet(DeviceType_Null);
6289 }
6290
6291 return devType;
6292}
6293
6294/**
6295 * Internal method which checks whether a property name is for a filter plugin.
6296 */
6297bool Medium::i_isPropertyForFilter(const com::Utf8Str &aName)
6298{
6299 /* If the name contains "/" use the part before as a filter name and lookup the filter. */
6300 size_t offSlash;
6301 if ((offSlash = aName.find("/", 0)) != aName.npos)
6302 {
6303 com::Utf8Str strFilter;
6304 com::Utf8Str strKey;
6305
6306 HRESULT rc = strFilter.assignEx(aName, 0, offSlash);
6307 if (FAILED(rc))
6308 return false;
6309
6310 rc = strKey.assignEx(aName, offSlash + 1, aName.length() - offSlash - 1); /* Skip slash */
6311 if (FAILED(rc))
6312 return false;
6313
6314 VDFILTERINFO FilterInfo;
6315 int vrc = VDFilterInfoOne(strFilter.c_str(), &FilterInfo);
6316 if (RT_SUCCESS(vrc))
6317 {
6318 /* Check that the property exists. */
6319 PCVDCONFIGINFO paConfig = FilterInfo.paConfigInfo;
6320 while (paConfig->pszKey)
6321 {
6322 if (strKey.equals(paConfig->pszKey))
6323 return true;
6324 paConfig++;
6325 }
6326 }
6327 }
6328
6329 return false;
6330}
6331
6332/**
6333 * Returns the last error message collected by the i_vdErrorCall callback and
6334 * resets it.
6335 *
6336 * The error message is returned prepended with a dot and a space, like this:
6337 * <code>
6338 * ". <error_text> (%Rrc)"
6339 * </code>
6340 * to make it easily appendable to a more general error message. The @c %Rrc
6341 * format string is given @a aVRC as an argument.
6342 *
6343 * If there is no last error message collected by i_vdErrorCall or if it is a
6344 * null or empty string, then this function returns the following text:
6345 * <code>
6346 * " (%Rrc)"
6347 * </code>
6348 *
6349 * @note Doesn't do any object locking; it is assumed that the caller makes sure
6350 * the callback isn't called by more than one thread at a time.
6351 *
6352 * @param aVRC VBox error code to use when no error message is provided.
6353 */
6354Utf8Str Medium::i_vdError(int aVRC)
6355{
6356 Utf8Str error;
6357
6358 if (m->vdError.isEmpty())
6359 error = Utf8StrFmt(" (%Rrc)", aVRC);
6360 else
6361 error = Utf8StrFmt(".\n%s", m->vdError.c_str());
6362
6363 m->vdError.setNull();
6364
6365 return error;
6366}
6367
6368/**
6369 * Error message callback.
6370 *
6371 * Puts the reported error message to the m->vdError field.
6372 *
6373 * @note Doesn't do any object locking; it is assumed that the caller makes sure
6374 * the callback isn't called by more than one thread at a time.
6375 *
6376 * @param pvUser The opaque data passed on container creation.
6377 * @param rc The VBox error code.
6378 * @param RT_SRC_POS_DECL Use RT_SRC_POS.
6379 * @param pszFormat Error message format string.
6380 * @param va Error message arguments.
6381 */
6382/*static*/
6383DECLCALLBACK(void) Medium::i_vdErrorCall(void *pvUser, int rc, RT_SRC_POS_DECL,
6384 const char *pszFormat, va_list va)
6385{
6386 NOREF(pszFile); NOREF(iLine); NOREF(pszFunction); /* RT_SRC_POS_DECL */
6387
6388 Medium *that = static_cast<Medium*>(pvUser);
6389 AssertReturnVoid(that != NULL);
6390
6391 if (that->m->vdError.isEmpty())
6392 that->m->vdError =
6393 Utf8StrFmt("%s (%Rrc)", Utf8Str(pszFormat, va).c_str(), rc);
6394 else
6395 that->m->vdError =
6396 Utf8StrFmt("%s.\n%s (%Rrc)", that->m->vdError.c_str(),
6397 Utf8Str(pszFormat, va).c_str(), rc);
6398}
6399
6400/* static */
6401DECLCALLBACK(bool) Medium::i_vdConfigAreKeysValid(void *pvUser,
6402 const char * /* pszzValid */)
6403{
6404 Medium *that = static_cast<Medium*>(pvUser);
6405 AssertReturn(that != NULL, false);
6406
6407 /* we always return true since the only keys we have are those found in
6408 * VDBACKENDINFO */
6409 return true;
6410}
6411
6412/* static */
6413DECLCALLBACK(int) Medium::i_vdConfigQuerySize(void *pvUser,
6414 const char *pszName,
6415 size_t *pcbValue)
6416{
6417 AssertReturn(VALID_PTR(pcbValue), VERR_INVALID_POINTER);
6418
6419 Medium *that = static_cast<Medium*>(pvUser);
6420 AssertReturn(that != NULL, VERR_GENERAL_FAILURE);
6421
6422 settings::StringsMap::const_iterator it = that->m->mapProperties.find(Utf8Str(pszName));
6423 if (it == that->m->mapProperties.end())
6424 return VERR_CFGM_VALUE_NOT_FOUND;
6425
6426 /* we interpret null values as "no value" in Medium */
6427 if (it->second.isEmpty())
6428 return VERR_CFGM_VALUE_NOT_FOUND;
6429
6430 *pcbValue = it->second.length() + 1 /* include terminator */;
6431
6432 return VINF_SUCCESS;
6433}
6434
6435/* static */
6436DECLCALLBACK(int) Medium::i_vdConfigQuery(void *pvUser,
6437 const char *pszName,
6438 char *pszValue,
6439 size_t cchValue)
6440{
6441 AssertReturn(VALID_PTR(pszValue), VERR_INVALID_POINTER);
6442
6443 Medium *that = static_cast<Medium*>(pvUser);
6444 AssertReturn(that != NULL, VERR_GENERAL_FAILURE);
6445
6446 settings::StringsMap::const_iterator it = that->m->mapProperties.find(Utf8Str(pszName));
6447 if (it == that->m->mapProperties.end())
6448 return VERR_CFGM_VALUE_NOT_FOUND;
6449
6450 /* we interpret null values as "no value" in Medium */
6451 if (it->second.isEmpty())
6452 return VERR_CFGM_VALUE_NOT_FOUND;
6453
6454 const Utf8Str &value = it->second;
6455 if (value.length() >= cchValue)
6456 return VERR_CFGM_NOT_ENOUGH_SPACE;
6457
6458 memcpy(pszValue, value.c_str(), value.length() + 1);
6459
6460 return VINF_SUCCESS;
6461}
6462
6463DECLCALLBACK(int) Medium::i_vdTcpSocketCreate(uint32_t fFlags, PVDSOCKET pSock)
6464{
6465 PVDSOCKETINT pSocketInt = NULL;
6466
6467 if ((fFlags & VD_INTERFACETCPNET_CONNECT_EXTENDED_SELECT) != 0)
6468 return VERR_NOT_SUPPORTED;
6469
6470 pSocketInt = (PVDSOCKETINT)RTMemAllocZ(sizeof(VDSOCKETINT));
6471 if (!pSocketInt)
6472 return VERR_NO_MEMORY;
6473
6474 pSocketInt->hSocket = NIL_RTSOCKET;
6475 *pSock = pSocketInt;
6476 return VINF_SUCCESS;
6477}
6478
6479DECLCALLBACK(int) Medium::i_vdTcpSocketDestroy(VDSOCKET Sock)
6480{
6481 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
6482
6483 if (pSocketInt->hSocket != NIL_RTSOCKET)
6484 RTTcpClientCloseEx(pSocketInt->hSocket, false /*fGracefulShutdown*/);
6485
6486 RTMemFree(pSocketInt);
6487
6488 return VINF_SUCCESS;
6489}
6490
6491DECLCALLBACK(int) Medium::i_vdTcpClientConnect(VDSOCKET Sock, const char *pszAddress, uint32_t uPort)
6492{
6493 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
6494
6495 return RTTcpClientConnect(pszAddress, uPort, &pSocketInt->hSocket);
6496}
6497
6498DECLCALLBACK(int) Medium::i_vdTcpClientClose(VDSOCKET Sock)
6499{
6500 int rc = VINF_SUCCESS;
6501 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
6502
6503 rc = RTTcpClientCloseEx(pSocketInt->hSocket, false /*fGracefulShutdown*/);
6504 pSocketInt->hSocket = NIL_RTSOCKET;
6505 return rc;
6506}
6507
6508DECLCALLBACK(bool) Medium::i_vdTcpIsClientConnected(VDSOCKET Sock)
6509{
6510 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
6511 return pSocketInt->hSocket != NIL_RTSOCKET;
6512}
6513
6514DECLCALLBACK(int) Medium::i_vdTcpSelectOne(VDSOCKET Sock, RTMSINTERVAL cMillies)
6515{
6516 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
6517 return RTTcpSelectOne(pSocketInt->hSocket, cMillies);
6518}
6519
6520DECLCALLBACK(int) Medium::i_vdTcpRead(VDSOCKET Sock, void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
6521{
6522 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
6523 return RTTcpRead(pSocketInt->hSocket, pvBuffer, cbBuffer, pcbRead);
6524}
6525
6526DECLCALLBACK(int) Medium::i_vdTcpWrite(VDSOCKET Sock, const void *pvBuffer, size_t cbBuffer)
6527{
6528 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
6529 return RTTcpWrite(pSocketInt->hSocket, pvBuffer, cbBuffer);
6530}
6531
6532DECLCALLBACK(int) Medium::i_vdTcpSgWrite(VDSOCKET Sock, PCRTSGBUF pSgBuf)
6533{
6534 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
6535 return RTTcpSgWrite(pSocketInt->hSocket, pSgBuf);
6536}
6537
6538DECLCALLBACK(int) Medium::i_vdTcpFlush(VDSOCKET Sock)
6539{
6540 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
6541 return RTTcpFlush(pSocketInt->hSocket);
6542}
6543
6544DECLCALLBACK(int) Medium::i_vdTcpSetSendCoalescing(VDSOCKET Sock, bool fEnable)
6545{
6546 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
6547 return RTTcpSetSendCoalescing(pSocketInt->hSocket, fEnable);
6548}
6549
6550DECLCALLBACK(int) Medium::i_vdTcpGetLocalAddress(VDSOCKET Sock, PRTNETADDR pAddr)
6551{
6552 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
6553 return RTTcpGetLocalAddress(pSocketInt->hSocket, pAddr);
6554}
6555
6556DECLCALLBACK(int) Medium::i_vdTcpGetPeerAddress(VDSOCKET Sock, PRTNETADDR pAddr)
6557{
6558 PVDSOCKETINT pSocketInt = (PVDSOCKETINT)Sock;
6559 return RTTcpGetPeerAddress(pSocketInt->hSocket, pAddr);
6560}
6561
6562/**
6563 * Starts a new thread driven by the appropriate Medium::Task::handler() method.
6564 *
6565 * @note When the task is executed by this method, IProgress::notifyComplete()
6566 * is automatically called for the progress object associated with this
6567 * task when the task is finished to signal the operation completion for
6568 * other threads asynchronously waiting for it.
6569 */
6570HRESULT Medium::i_startThread(Medium::Task *pTask)
6571{
6572#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
6573 /* Extreme paranoia: The calling thread should not hold the medium
6574 * tree lock or any medium lock. Since there is no separate lock class
6575 * for medium objects be even more strict: no other object locks. */
6576 Assert(!AutoLockHoldsLocksInClass(LOCKCLASS_LISTOFMEDIA));
6577 Assert(!AutoLockHoldsLocksInClass(getLockingClass()));
6578#endif
6579
6580 /// @todo use a more descriptive task name
6581 int vrc = RTThreadCreate(NULL, Medium::Task::fntMediumTask, pTask,
6582 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
6583 "Medium::Task");
6584 if (RT_FAILURE(vrc))
6585 {
6586 delete pTask;
6587 return setError(E_FAIL, "Could not create Medium::Task thread (%Rrc)\n", vrc);
6588 }
6589
6590 return S_OK;
6591}
6592
6593/**
6594 * Runs Medium::Task::handler() on the current thread instead of creating
6595 * a new one.
6596 *
6597 * This call implies that it is made on another temporary thread created for
6598 * some asynchronous task. Avoid calling it from a normal thread since the task
6599 * operations are potentially lengthy and will block the calling thread in this
6600 * case.
6601 *
6602 * @note When the task is executed by this method, IProgress::notifyComplete()
6603 * is not called for the progress object associated with this task when
6604 * the task is finished. Instead, the result of the operation is returned
6605 * by this method directly and it's the caller's responsibility to
6606 * complete the progress object in this case.
6607 */
6608HRESULT Medium::i_runNow(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 /* NIL_RTTHREAD indicates synchronous call. */
6619 return (HRESULT)Medium::Task::fntMediumTask(NIL_RTTHREAD, pTask);
6620}
6621
6622/**
6623 * Implementation code for the "create base" task.
6624 *
6625 * This only gets started from Medium::CreateBaseStorage() and always runs
6626 * asynchronously. As a result, we always save the VirtualBox.xml file when
6627 * we're done here.
6628 *
6629 * @param task
6630 * @return
6631 */
6632HRESULT Medium::i_taskCreateBaseHandler(Medium::CreateBaseTask &task)
6633{
6634 HRESULT rc = S_OK;
6635
6636 /* these parameters we need after creation */
6637 uint64_t size = 0, logicalSize = 0;
6638 MediumVariant_T variant = MediumVariant_Standard;
6639 bool fGenerateUuid = false;
6640
6641 try
6642 {
6643 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
6644
6645 /* The object may request a specific UUID (through a special form of
6646 * the setLocation() argument). Otherwise we have to generate it */
6647 Guid id = m->id;
6648
6649 fGenerateUuid = id.isZero();
6650 if (fGenerateUuid)
6651 {
6652 id.create();
6653 /* VirtualBox::registerMedium() will need UUID */
6654 unconst(m->id) = id;
6655 }
6656
6657 Utf8Str format(m->strFormat);
6658 Utf8Str location(m->strLocationFull);
6659 uint64_t capabilities = m->formatObj->i_getCapabilities();
6660 ComAssertThrow(capabilities & ( MediumFormatCapabilities_CreateFixed
6661 | MediumFormatCapabilities_CreateDynamic), E_FAIL);
6662 Assert(m->state == MediumState_Creating);
6663
6664 PVBOXHDD hdd;
6665 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
6666 ComAssertRCThrow(vrc, E_FAIL);
6667
6668 /* unlock before the potentially lengthy operation */
6669 thisLock.release();
6670
6671 try
6672 {
6673 /* ensure the directory exists */
6674 if (capabilities & MediumFormatCapabilities_File)
6675 {
6676 rc = VirtualBox::i_ensureFilePathExists(location, !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
6677 if (FAILED(rc))
6678 throw rc;
6679 }
6680
6681 VDGEOMETRY geo = { 0, 0, 0 }; /* auto-detect */
6682
6683 vrc = VDCreateBase(hdd,
6684 format.c_str(),
6685 location.c_str(),
6686 task.mSize,
6687 task.mVariant & ~MediumVariant_NoCreateDir,
6688 NULL,
6689 &geo,
6690 &geo,
6691 id.raw(),
6692 VD_OPEN_FLAGS_NORMAL | m->uOpenFlagsDef,
6693 m->vdImageIfaces,
6694 task.mVDOperationIfaces);
6695 if (RT_FAILURE(vrc))
6696 throw setError(VBOX_E_FILE_ERROR,
6697 tr("Could not create the medium storage unit '%s'%s"),
6698 location.c_str(), i_vdError(vrc).c_str());
6699
6700 size = VDGetFileSize(hdd, 0);
6701 logicalSize = VDGetSize(hdd, 0);
6702 unsigned uImageFlags;
6703 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
6704 if (RT_SUCCESS(vrc))
6705 variant = (MediumVariant_T)uImageFlags;
6706 }
6707 catch (HRESULT aRC) { rc = aRC; }
6708
6709 VDDestroy(hdd);
6710 }
6711 catch (HRESULT aRC) { rc = aRC; }
6712
6713 if (SUCCEEDED(rc))
6714 {
6715 /* register with mVirtualBox as the last step and move to
6716 * Created state only on success (leaving an orphan file is
6717 * better than breaking media registry consistency) */
6718 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
6719 ComObjPtr<Medium> pMedium;
6720 rc = m->pVirtualBox->i_registerMedium(this, &pMedium, DeviceType_HardDisk, treeLock);
6721 Assert(this == pMedium);
6722 }
6723
6724 // re-acquire the lock before changing state
6725 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
6726
6727 if (SUCCEEDED(rc))
6728 {
6729 m->state = MediumState_Created;
6730
6731 m->size = size;
6732 m->logicalSize = logicalSize;
6733 m->variant = variant;
6734
6735 thisLock.release();
6736 i_markRegistriesModified();
6737 if (task.isAsync())
6738 {
6739 // in asynchronous mode, save settings now
6740 m->pVirtualBox->i_saveModifiedRegistries();
6741 }
6742 }
6743 else
6744 {
6745 /* back to NotCreated on failure */
6746 m->state = MediumState_NotCreated;
6747
6748 /* reset UUID to prevent it from being reused next time */
6749 if (fGenerateUuid)
6750 unconst(m->id).clear();
6751 }
6752
6753 return rc;
6754}
6755
6756/**
6757 * Implementation code for the "create diff" task.
6758 *
6759 * This task always gets started from Medium::createDiffStorage() and can run
6760 * synchronously or asynchronously depending on the "wait" parameter passed to
6761 * that function. If we run synchronously, the caller expects the medium
6762 * registry modification to be set before returning; otherwise (in asynchronous
6763 * mode), we save the settings ourselves.
6764 *
6765 * @param task
6766 * @return
6767 */
6768HRESULT Medium::i_taskCreateDiffHandler(Medium::CreateDiffTask &task)
6769{
6770 HRESULT rcTmp = S_OK;
6771
6772 const ComObjPtr<Medium> &pTarget = task.mTarget;
6773
6774 uint64_t size = 0, logicalSize = 0;
6775 MediumVariant_T variant = MediumVariant_Standard;
6776 bool fGenerateUuid = false;
6777
6778 try
6779 {
6780 /* Lock both in {parent,child} order. */
6781 AutoMultiWriteLock2 mediaLock(this, pTarget COMMA_LOCKVAL_SRC_POS);
6782
6783 /* The object may request a specific UUID (through a special form of
6784 * the setLocation() argument). Otherwise we have to generate it */
6785 Guid targetId = pTarget->m->id;
6786
6787 fGenerateUuid = targetId.isZero();
6788 if (fGenerateUuid)
6789 {
6790 targetId.create();
6791 /* VirtualBox::registerMedium() will need UUID */
6792 unconst(pTarget->m->id) = targetId;
6793 }
6794
6795 Guid id = m->id;
6796
6797 Utf8Str targetFormat(pTarget->m->strFormat);
6798 Utf8Str targetLocation(pTarget->m->strLocationFull);
6799 uint64_t capabilities = pTarget->m->formatObj->i_getCapabilities();
6800 ComAssertThrow(capabilities & MediumFormatCapabilities_CreateDynamic, E_FAIL);
6801
6802 Assert(pTarget->m->state == MediumState_Creating);
6803 Assert(m->state == MediumState_LockedRead);
6804
6805 PVBOXHDD hdd;
6806 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
6807 ComAssertRCThrow(vrc, E_FAIL);
6808
6809 /* the two media are now protected by their non-default states;
6810 * unlock the media before the potentially lengthy operation */
6811 mediaLock.release();
6812
6813 try
6814 {
6815 /* Open all media in the target chain but the last. */
6816 MediumLockList::Base::const_iterator targetListBegin =
6817 task.mpMediumLockList->GetBegin();
6818 MediumLockList::Base::const_iterator targetListEnd =
6819 task.mpMediumLockList->GetEnd();
6820 for (MediumLockList::Base::const_iterator it = targetListBegin;
6821 it != targetListEnd;
6822 ++it)
6823 {
6824 const MediumLock &mediumLock = *it;
6825 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
6826
6827 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
6828
6829 /* Skip over the target diff medium */
6830 if (pMedium->m->state == MediumState_Creating)
6831 continue;
6832
6833 /* sanity check */
6834 Assert(pMedium->m->state == MediumState_LockedRead);
6835
6836 /* Open all media in appropriate mode. */
6837 vrc = VDOpen(hdd,
6838 pMedium->m->strFormat.c_str(),
6839 pMedium->m->strLocationFull.c_str(),
6840 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
6841 pMedium->m->vdImageIfaces);
6842 if (RT_FAILURE(vrc))
6843 throw setError(VBOX_E_FILE_ERROR,
6844 tr("Could not open the medium storage unit '%s'%s"),
6845 pMedium->m->strLocationFull.c_str(),
6846 i_vdError(vrc).c_str());
6847 }
6848
6849 /* ensure the target directory exists */
6850 if (capabilities & MediumFormatCapabilities_File)
6851 {
6852 HRESULT rc = VirtualBox::i_ensureFilePathExists(targetLocation,
6853 !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
6854 if (FAILED(rc))
6855 throw rc;
6856 }
6857
6858 vrc = VDCreateDiff(hdd,
6859 targetFormat.c_str(),
6860 targetLocation.c_str(),
6861 (task.mVariant & ~MediumVariant_NoCreateDir) | VD_IMAGE_FLAGS_DIFF,
6862 NULL,
6863 targetId.raw(),
6864 id.raw(),
6865 VD_OPEN_FLAGS_NORMAL | m->uOpenFlagsDef,
6866 pTarget->m->vdImageIfaces,
6867 task.mVDOperationIfaces);
6868 if (RT_FAILURE(vrc))
6869 throw setError(VBOX_E_FILE_ERROR,
6870 tr("Could not create the differencing medium storage unit '%s'%s"),
6871 targetLocation.c_str(), i_vdError(vrc).c_str());
6872
6873 size = VDGetFileSize(hdd, VD_LAST_IMAGE);
6874 logicalSize = VDGetSize(hdd, VD_LAST_IMAGE);
6875 unsigned uImageFlags;
6876 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
6877 if (RT_SUCCESS(vrc))
6878 variant = (MediumVariant_T)uImageFlags;
6879 }
6880 catch (HRESULT aRC) { rcTmp = aRC; }
6881
6882 VDDestroy(hdd);
6883 }
6884 catch (HRESULT aRC) { rcTmp = aRC; }
6885
6886 MultiResult mrc(rcTmp);
6887
6888 if (SUCCEEDED(mrc))
6889 {
6890 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
6891
6892 Assert(pTarget->m->pParent.isNull());
6893
6894 /* associate the child with the parent */
6895 pTarget->m->pParent = this;
6896 m->llChildren.push_back(pTarget);
6897
6898 /** @todo r=klaus neither target nor base() are locked,
6899 * potential race! */
6900 /* diffs for immutable media are auto-reset by default */
6901 pTarget->m->autoReset = (i_getBase()->m->type == MediumType_Immutable);
6902
6903 /* register with mVirtualBox as the last step and move to
6904 * Created state only on success (leaving an orphan file is
6905 * better than breaking media registry consistency) */
6906 ComObjPtr<Medium> pMedium;
6907 mrc = m->pVirtualBox->i_registerMedium(pTarget, &pMedium, DeviceType_HardDisk, treeLock);
6908 Assert(pTarget == pMedium);
6909
6910 if (FAILED(mrc))
6911 /* break the parent association on failure to register */
6912 i_deparent();
6913 }
6914
6915 AutoMultiWriteLock2 mediaLock(this, pTarget COMMA_LOCKVAL_SRC_POS);
6916
6917 if (SUCCEEDED(mrc))
6918 {
6919 pTarget->m->state = MediumState_Created;
6920
6921 pTarget->m->size = size;
6922 pTarget->m->logicalSize = logicalSize;
6923 pTarget->m->variant = variant;
6924 }
6925 else
6926 {
6927 /* back to NotCreated on failure */
6928 pTarget->m->state = MediumState_NotCreated;
6929
6930 pTarget->m->autoReset = false;
6931
6932 /* reset UUID to prevent it from being reused next time */
6933 if (fGenerateUuid)
6934 unconst(pTarget->m->id).clear();
6935 }
6936
6937 // deregister the task registered in createDiffStorage()
6938 Assert(m->numCreateDiffTasks != 0);
6939 --m->numCreateDiffTasks;
6940
6941 mediaLock.release();
6942 i_markRegistriesModified();
6943 if (task.isAsync())
6944 {
6945 // in asynchronous mode, save settings now
6946 m->pVirtualBox->i_saveModifiedRegistries();
6947 }
6948
6949 /* Note that in sync mode, it's the caller's responsibility to
6950 * unlock the medium. */
6951
6952 return mrc;
6953}
6954
6955/**
6956 * Implementation code for the "merge" task.
6957 *
6958 * This task always gets started from Medium::mergeTo() and can run
6959 * synchronously or asynchronously depending on the "wait" parameter passed to
6960 * that function. If we run synchronously, the caller expects the medium
6961 * registry modification to be set before returning; otherwise (in asynchronous
6962 * mode), we save the settings ourselves.
6963 *
6964 * @param task
6965 * @return
6966 */
6967HRESULT Medium::i_taskMergeHandler(Medium::MergeTask &task)
6968{
6969 HRESULT rcTmp = S_OK;
6970
6971 const ComObjPtr<Medium> &pTarget = task.mTarget;
6972
6973 try
6974 {
6975 PVBOXHDD hdd;
6976 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
6977 ComAssertRCThrow(vrc, E_FAIL);
6978
6979 try
6980 {
6981 // Similar code appears in SessionMachine::onlineMergeMedium, so
6982 // if you make any changes below check whether they are applicable
6983 // in that context as well.
6984
6985 unsigned uTargetIdx = VD_LAST_IMAGE;
6986 unsigned uSourceIdx = VD_LAST_IMAGE;
6987 /* Open all media in the chain. */
6988 MediumLockList::Base::iterator lockListBegin =
6989 task.mpMediumLockList->GetBegin();
6990 MediumLockList::Base::iterator lockListEnd =
6991 task.mpMediumLockList->GetEnd();
6992 unsigned i = 0;
6993 for (MediumLockList::Base::iterator it = lockListBegin;
6994 it != lockListEnd;
6995 ++it)
6996 {
6997 MediumLock &mediumLock = *it;
6998 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
6999
7000 if (pMedium == this)
7001 uSourceIdx = i;
7002 else if (pMedium == pTarget)
7003 uTargetIdx = i;
7004
7005 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
7006
7007 /*
7008 * complex sanity (sane complexity)
7009 *
7010 * The current medium must be in the Deleting (medium is merged)
7011 * or LockedRead (parent medium) state if it is not the target.
7012 * If it is the target it must be in the LockedWrite state.
7013 */
7014 Assert( ( pMedium != pTarget
7015 && ( pMedium->m->state == MediumState_Deleting
7016 || pMedium->m->state == MediumState_LockedRead))
7017 || ( pMedium == pTarget
7018 && pMedium->m->state == MediumState_LockedWrite));
7019
7020 /*
7021 * Medium must be the target, in the LockedRead state
7022 * or Deleting state where it is not allowed to be attached
7023 * to a virtual machine.
7024 */
7025 Assert( pMedium == pTarget
7026 || pMedium->m->state == MediumState_LockedRead
7027 || ( pMedium->m->backRefs.size() == 0
7028 && pMedium->m->state == MediumState_Deleting));
7029 /* The source medium must be in Deleting state. */
7030 Assert( pMedium != this
7031 || pMedium->m->state == MediumState_Deleting);
7032
7033 unsigned uOpenFlags = VD_OPEN_FLAGS_NORMAL;
7034
7035 if ( pMedium->m->state == MediumState_LockedRead
7036 || pMedium->m->state == MediumState_Deleting)
7037 uOpenFlags = VD_OPEN_FLAGS_READONLY;
7038 if (pMedium->m->type == MediumType_Shareable)
7039 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
7040
7041 /* Open the medium */
7042 vrc = VDOpen(hdd,
7043 pMedium->m->strFormat.c_str(),
7044 pMedium->m->strLocationFull.c_str(),
7045 uOpenFlags | m->uOpenFlagsDef,
7046 pMedium->m->vdImageIfaces);
7047 if (RT_FAILURE(vrc))
7048 throw vrc;
7049
7050 i++;
7051 }
7052
7053 ComAssertThrow( uSourceIdx != VD_LAST_IMAGE
7054 && uTargetIdx != VD_LAST_IMAGE, E_FAIL);
7055
7056 vrc = VDMerge(hdd, uSourceIdx, uTargetIdx,
7057 task.mVDOperationIfaces);
7058 if (RT_FAILURE(vrc))
7059 throw vrc;
7060
7061 /* update parent UUIDs */
7062 if (!task.mfMergeForward)
7063 {
7064 /* we need to update UUIDs of all source's children
7065 * which cannot be part of the container at once so
7066 * add each one in there individually */
7067 if (task.mpChildrenToReparent)
7068 {
7069 MediumLockList::Base::iterator childrenBegin = task.mpChildrenToReparent->GetBegin();
7070 MediumLockList::Base::iterator childrenEnd = task.mpChildrenToReparent->GetEnd();
7071 for (MediumLockList::Base::iterator it = childrenBegin;
7072 it != childrenEnd;
7073 ++it)
7074 {
7075 Medium *pMedium = it->GetMedium();
7076 /* VD_OPEN_FLAGS_INFO since UUID is wrong yet */
7077 vrc = VDOpen(hdd,
7078 pMedium->m->strFormat.c_str(),
7079 pMedium->m->strLocationFull.c_str(),
7080 VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
7081 pMedium->m->vdImageIfaces);
7082 if (RT_FAILURE(vrc))
7083 throw vrc;
7084
7085 vrc = VDSetParentUuid(hdd, VD_LAST_IMAGE,
7086 pTarget->m->id.raw());
7087 if (RT_FAILURE(vrc))
7088 throw vrc;
7089
7090 vrc = VDClose(hdd, false /* fDelete */);
7091 if (RT_FAILURE(vrc))
7092 throw vrc;
7093 }
7094 }
7095 }
7096 }
7097 catch (HRESULT aRC) { rcTmp = aRC; }
7098 catch (int aVRC)
7099 {
7100 rcTmp = setError(VBOX_E_FILE_ERROR,
7101 tr("Could not merge the medium '%s' to '%s'%s"),
7102 m->strLocationFull.c_str(),
7103 pTarget->m->strLocationFull.c_str(),
7104 i_vdError(aVRC).c_str());
7105 }
7106
7107 VDDestroy(hdd);
7108 }
7109 catch (HRESULT aRC) { rcTmp = aRC; }
7110
7111 ErrorInfoKeeper eik;
7112 MultiResult mrc(rcTmp);
7113 HRESULT rc2;
7114
7115 if (SUCCEEDED(mrc))
7116 {
7117 /* all media but the target were successfully deleted by
7118 * VDMerge; reparent the last one and uninitialize deleted media. */
7119
7120 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
7121
7122 if (task.mfMergeForward)
7123 {
7124 /* first, unregister the target since it may become a base
7125 * medium which needs re-registration */
7126 rc2 = m->pVirtualBox->i_unregisterMedium(pTarget);
7127 AssertComRC(rc2);
7128
7129 /* then, reparent it and disconnect the deleted branch at
7130 * both ends (chain->parent() is source's parent) */
7131 pTarget->i_deparent();
7132 pTarget->m->pParent = task.mParentForTarget;
7133 if (pTarget->m->pParent)
7134 {
7135 pTarget->m->pParent->m->llChildren.push_back(pTarget);
7136 i_deparent();
7137 }
7138
7139 /* then, register again */
7140 ComObjPtr<Medium> pMedium;
7141 rc2 = m->pVirtualBox->i_registerMedium(pTarget, &pMedium,
7142 DeviceType_HardDisk,
7143 treeLock);
7144 AssertComRC(rc2);
7145 }
7146 else
7147 {
7148 Assert(pTarget->i_getChildren().size() == 1);
7149 Medium *targetChild = pTarget->i_getChildren().front();
7150
7151 /* disconnect the deleted branch at the elder end */
7152 targetChild->i_deparent();
7153
7154 /* reparent source's children and disconnect the deleted
7155 * branch at the younger end */
7156 if (task.mpChildrenToReparent)
7157 {
7158 /* obey {parent,child} lock order */
7159 AutoWriteLock sourceLock(this COMMA_LOCKVAL_SRC_POS);
7160
7161 MediumLockList::Base::iterator childrenBegin = task.mpChildrenToReparent->GetBegin();
7162 MediumLockList::Base::iterator childrenEnd = task.mpChildrenToReparent->GetEnd();
7163 for (MediumLockList::Base::iterator it = childrenBegin;
7164 it != childrenEnd;
7165 ++it)
7166 {
7167 Medium *pMedium = it->GetMedium();
7168 AutoWriteLock childLock(pMedium COMMA_LOCKVAL_SRC_POS);
7169
7170 pMedium->i_deparent(); // removes pMedium from source
7171 pMedium->i_setParent(pTarget);
7172 }
7173 }
7174 }
7175
7176 /* unregister and uninitialize all media removed by the merge */
7177 MediumLockList::Base::iterator lockListBegin =
7178 task.mpMediumLockList->GetBegin();
7179 MediumLockList::Base::iterator lockListEnd =
7180 task.mpMediumLockList->GetEnd();
7181 for (MediumLockList::Base::iterator it = lockListBegin;
7182 it != lockListEnd;
7183 )
7184 {
7185 MediumLock &mediumLock = *it;
7186 /* Create a real copy of the medium pointer, as the medium
7187 * lock deletion below would invalidate the referenced object. */
7188 const ComObjPtr<Medium> pMedium = mediumLock.GetMedium();
7189
7190 /* The target and all media not merged (readonly) are skipped */
7191 if ( pMedium == pTarget
7192 || pMedium->m->state == MediumState_LockedRead)
7193 {
7194 ++it;
7195 continue;
7196 }
7197
7198 rc2 = pMedium->m->pVirtualBox->i_unregisterMedium(pMedium);
7199 AssertComRC(rc2);
7200
7201 /* now, uninitialize the deleted medium (note that
7202 * due to the Deleting state, uninit() will not touch
7203 * the parent-child relationship so we need to
7204 * uninitialize each disk individually) */
7205
7206 /* note that the operation initiator medium (which is
7207 * normally also the source medium) is a special case
7208 * -- there is one more caller added by Task to it which
7209 * we must release. Also, if we are in sync mode, the
7210 * caller may still hold an AutoCaller instance for it
7211 * and therefore we cannot uninit() it (it's therefore
7212 * the caller's responsibility) */
7213 if (pMedium == this)
7214 {
7215 Assert(i_getChildren().size() == 0);
7216 Assert(m->backRefs.size() == 0);
7217 task.mMediumCaller.release();
7218 }
7219
7220 /* Delete the medium lock list entry, which also releases the
7221 * caller added by MergeChain before uninit() and updates the
7222 * iterator to point to the right place. */
7223 rc2 = task.mpMediumLockList->RemoveByIterator(it);
7224 AssertComRC(rc2);
7225
7226 if (task.isAsync() || pMedium != this)
7227 {
7228 treeLock.release();
7229 pMedium->uninit();
7230 treeLock.acquire();
7231 }
7232 }
7233 }
7234
7235 i_markRegistriesModified();
7236 if (task.isAsync())
7237 {
7238 // in asynchronous mode, save settings now
7239 eik.restore();
7240 m->pVirtualBox->i_saveModifiedRegistries();
7241 eik.fetch();
7242 }
7243
7244 if (FAILED(mrc))
7245 {
7246 /* Here we come if either VDMerge() failed (in which case we
7247 * assume that it tried to do everything to make a further
7248 * retry possible -- e.g. not deleted intermediate media
7249 * and so on) or VirtualBox::saveRegistries() failed (where we
7250 * should have the original tree but with intermediate storage
7251 * units deleted by VDMerge()). We have to only restore states
7252 * (through the MergeChain dtor) unless we are run synchronously
7253 * in which case it's the responsibility of the caller as stated
7254 * in the mergeTo() docs. The latter also implies that we
7255 * don't own the merge chain, so release it in this case. */
7256 if (task.isAsync())
7257 i_cancelMergeTo(task.mpChildrenToReparent, task.mpMediumLockList);
7258 }
7259
7260 return mrc;
7261}
7262
7263/**
7264 * Implementation code for the "clone" task.
7265 *
7266 * This only gets started from Medium::CloneTo() and always runs asynchronously.
7267 * As a result, we always save the VirtualBox.xml file when we're done here.
7268 *
7269 * @param task
7270 * @return
7271 */
7272HRESULT Medium::i_taskCloneHandler(Medium::CloneTask &task)
7273{
7274 HRESULT rcTmp = S_OK;
7275
7276 const ComObjPtr<Medium> &pTarget = task.mTarget;
7277 const ComObjPtr<Medium> &pParent = task.mParent;
7278
7279 bool fCreatingTarget = false;
7280
7281 uint64_t size = 0, logicalSize = 0;
7282 MediumVariant_T variant = MediumVariant_Standard;
7283 bool fGenerateUuid = false;
7284
7285 try
7286 {
7287 /* Lock all in {parent,child} order. The lock is also used as a
7288 * signal from the task initiator (which releases it only after
7289 * RTThreadCreate()) that we can start the job. */
7290 AutoMultiWriteLock3 thisLock(this, pTarget, pParent COMMA_LOCKVAL_SRC_POS);
7291
7292 fCreatingTarget = pTarget->m->state == MediumState_Creating;
7293
7294 /* The object may request a specific UUID (through a special form of
7295 * the setLocation() argument). Otherwise we have to generate it */
7296 Guid targetId = pTarget->m->id;
7297
7298 fGenerateUuid = targetId.isZero();
7299 if (fGenerateUuid)
7300 {
7301 targetId.create();
7302 /* VirtualBox::registerMedium() will need UUID */
7303 unconst(pTarget->m->id) = targetId;
7304 }
7305
7306 PVBOXHDD hdd;
7307 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
7308 ComAssertRCThrow(vrc, E_FAIL);
7309
7310 try
7311 {
7312 /* Open all media in the source chain. */
7313 MediumLockList::Base::const_iterator sourceListBegin =
7314 task.mpSourceMediumLockList->GetBegin();
7315 MediumLockList::Base::const_iterator sourceListEnd =
7316 task.mpSourceMediumLockList->GetEnd();
7317 for (MediumLockList::Base::const_iterator it = sourceListBegin;
7318 it != sourceListEnd;
7319 ++it)
7320 {
7321 const MediumLock &mediumLock = *it;
7322 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
7323 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
7324
7325 /* sanity check */
7326 Assert(pMedium->m->state == MediumState_LockedRead);
7327
7328 /** Open all media in read-only mode. */
7329 vrc = VDOpen(hdd,
7330 pMedium->m->strFormat.c_str(),
7331 pMedium->m->strLocationFull.c_str(),
7332 VD_OPEN_FLAGS_READONLY | m->uOpenFlagsDef,
7333 pMedium->m->vdImageIfaces);
7334 if (RT_FAILURE(vrc))
7335 throw setError(VBOX_E_FILE_ERROR,
7336 tr("Could not open the medium storage unit '%s'%s"),
7337 pMedium->m->strLocationFull.c_str(),
7338 i_vdError(vrc).c_str());
7339 }
7340
7341 Utf8Str targetFormat(pTarget->m->strFormat);
7342 Utf8Str targetLocation(pTarget->m->strLocationFull);
7343 uint64_t capabilities = pTarget->m->formatObj->i_getCapabilities();
7344
7345 Assert( pTarget->m->state == MediumState_Creating
7346 || pTarget->m->state == MediumState_LockedWrite);
7347 Assert(m->state == MediumState_LockedRead);
7348 Assert( pParent.isNull()
7349 || pParent->m->state == MediumState_LockedRead);
7350
7351 /* unlock before the potentially lengthy operation */
7352 thisLock.release();
7353
7354 /* ensure the target directory exists */
7355 if (capabilities & MediumFormatCapabilities_File)
7356 {
7357 HRESULT rc = VirtualBox::i_ensureFilePathExists(targetLocation,
7358 !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
7359 if (FAILED(rc))
7360 throw rc;
7361 }
7362
7363 PVBOXHDD targetHdd;
7364 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &targetHdd);
7365 ComAssertRCThrow(vrc, E_FAIL);
7366
7367 try
7368 {
7369 /* Open all media in the target chain. */
7370 MediumLockList::Base::const_iterator targetListBegin =
7371 task.mpTargetMediumLockList->GetBegin();
7372 MediumLockList::Base::const_iterator targetListEnd =
7373 task.mpTargetMediumLockList->GetEnd();
7374 for (MediumLockList::Base::const_iterator it = targetListBegin;
7375 it != targetListEnd;
7376 ++it)
7377 {
7378 const MediumLock &mediumLock = *it;
7379 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
7380
7381 /* If the target medium is not created yet there's no
7382 * reason to open it. */
7383 if (pMedium == pTarget && fCreatingTarget)
7384 continue;
7385
7386 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
7387
7388 /* sanity check */
7389 Assert( pMedium->m->state == MediumState_LockedRead
7390 || pMedium->m->state == MediumState_LockedWrite);
7391
7392 unsigned uOpenFlags = VD_OPEN_FLAGS_NORMAL;
7393 if (pMedium->m->state != MediumState_LockedWrite)
7394 uOpenFlags = VD_OPEN_FLAGS_READONLY;
7395 if (pMedium->m->type == MediumType_Shareable)
7396 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
7397
7398 /* Open all media in appropriate mode. */
7399 vrc = VDOpen(targetHdd,
7400 pMedium->m->strFormat.c_str(),
7401 pMedium->m->strLocationFull.c_str(),
7402 uOpenFlags | m->uOpenFlagsDef,
7403 pMedium->m->vdImageIfaces);
7404 if (RT_FAILURE(vrc))
7405 throw setError(VBOX_E_FILE_ERROR,
7406 tr("Could not open the medium storage unit '%s'%s"),
7407 pMedium->m->strLocationFull.c_str(),
7408 i_vdError(vrc).c_str());
7409 }
7410
7411 /** @todo r=klaus target isn't locked, race getting the state */
7412 if (task.midxSrcImageSame == UINT32_MAX)
7413 {
7414 vrc = VDCopy(hdd,
7415 VD_LAST_IMAGE,
7416 targetHdd,
7417 targetFormat.c_str(),
7418 (fCreatingTarget) ? targetLocation.c_str() : (char *)NULL,
7419 false /* fMoveByRename */,
7420 0 /* cbSize */,
7421 task.mVariant & ~MediumVariant_NoCreateDir,
7422 targetId.raw(),
7423 VD_OPEN_FLAGS_NORMAL | m->uOpenFlagsDef,
7424 NULL /* pVDIfsOperation */,
7425 pTarget->m->vdImageIfaces,
7426 task.mVDOperationIfaces);
7427 }
7428 else
7429 {
7430 vrc = VDCopyEx(hdd,
7431 VD_LAST_IMAGE,
7432 targetHdd,
7433 targetFormat.c_str(),
7434 (fCreatingTarget) ? targetLocation.c_str() : (char *)NULL,
7435 false /* fMoveByRename */,
7436 0 /* cbSize */,
7437 task.midxSrcImageSame,
7438 task.midxDstImageSame,
7439 task.mVariant & ~MediumVariant_NoCreateDir,
7440 targetId.raw(),
7441 VD_OPEN_FLAGS_NORMAL | m->uOpenFlagsDef,
7442 NULL /* pVDIfsOperation */,
7443 pTarget->m->vdImageIfaces,
7444 task.mVDOperationIfaces);
7445 }
7446 if (RT_FAILURE(vrc))
7447 throw setError(VBOX_E_FILE_ERROR,
7448 tr("Could not create the clone medium '%s'%s"),
7449 targetLocation.c_str(), i_vdError(vrc).c_str());
7450
7451 size = VDGetFileSize(targetHdd, VD_LAST_IMAGE);
7452 logicalSize = VDGetSize(targetHdd, VD_LAST_IMAGE);
7453 unsigned uImageFlags;
7454 vrc = VDGetImageFlags(targetHdd, 0, &uImageFlags);
7455 if (RT_SUCCESS(vrc))
7456 variant = (MediumVariant_T)uImageFlags;
7457 }
7458 catch (HRESULT aRC) { rcTmp = aRC; }
7459
7460 VDDestroy(targetHdd);
7461 }
7462 catch (HRESULT aRC) { rcTmp = aRC; }
7463
7464 VDDestroy(hdd);
7465 }
7466 catch (HRESULT aRC) { rcTmp = aRC; }
7467
7468 ErrorInfoKeeper eik;
7469 MultiResult mrc(rcTmp);
7470
7471 /* Only do the parent changes for newly created media. */
7472 if (SUCCEEDED(mrc) && fCreatingTarget)
7473 {
7474 /* we set mParent & children() */
7475 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
7476
7477 Assert(pTarget->m->pParent.isNull());
7478
7479 if (pParent)
7480 {
7481 /* associate the clone with the parent and deassociate
7482 * from VirtualBox */
7483 pTarget->m->pParent = pParent;
7484 pParent->m->llChildren.push_back(pTarget);
7485
7486 /* register with mVirtualBox as the last step and move to
7487 * Created state only on success (leaving an orphan file is
7488 * better than breaking media registry consistency) */
7489 eik.restore();
7490 ComObjPtr<Medium> pMedium;
7491 mrc = pParent->m->pVirtualBox->i_registerMedium(pTarget, &pMedium,
7492 DeviceType_HardDisk,
7493 treeLock);
7494 Assert( FAILED(mrc)
7495 || pTarget == pMedium);
7496 eik.fetch();
7497
7498 if (FAILED(mrc))
7499 /* break parent association on failure to register */
7500 pTarget->i_deparent(); // removes target from parent
7501 }
7502 else
7503 {
7504 /* just register */
7505 eik.restore();
7506 ComObjPtr<Medium> pMedium;
7507 mrc = m->pVirtualBox->i_registerMedium(pTarget, &pMedium,
7508 DeviceType_HardDisk,
7509 treeLock);
7510 Assert( FAILED(mrc)
7511 || pTarget == pMedium);
7512 eik.fetch();
7513 }
7514 }
7515
7516 if (fCreatingTarget)
7517 {
7518 AutoWriteLock mLock(pTarget COMMA_LOCKVAL_SRC_POS);
7519
7520 if (SUCCEEDED(mrc))
7521 {
7522 pTarget->m->state = MediumState_Created;
7523
7524 pTarget->m->size = size;
7525 pTarget->m->logicalSize = logicalSize;
7526 pTarget->m->variant = variant;
7527 }
7528 else
7529 {
7530 /* back to NotCreated on failure */
7531 pTarget->m->state = MediumState_NotCreated;
7532
7533 /* reset UUID to prevent it from being reused next time */
7534 if (fGenerateUuid)
7535 unconst(pTarget->m->id).clear();
7536 }
7537 }
7538
7539 // now, at the end of this task (always asynchronous), save the settings
7540 if (SUCCEEDED(mrc))
7541 {
7542 // save the settings
7543 i_markRegistriesModified();
7544 /* collect multiple errors */
7545 eik.restore();
7546 m->pVirtualBox->i_saveModifiedRegistries();
7547 eik.fetch();
7548 }
7549
7550 /* Everything is explicitly unlocked when the task exits,
7551 * as the task destruction also destroys the source chain. */
7552
7553 /* Make sure the source chain is released early. It could happen
7554 * that we get a deadlock in Appliance::Import when Medium::Close
7555 * is called & the source chain is released at the same time. */
7556 task.mpSourceMediumLockList->Clear();
7557
7558 return mrc;
7559}
7560
7561/**
7562 * Implementation code for the "delete" task.
7563 *
7564 * This task always gets started from Medium::deleteStorage() and can run
7565 * synchronously or asynchronously depending on the "wait" parameter passed to
7566 * that function.
7567 *
7568 * @param task
7569 * @return
7570 */
7571HRESULT Medium::i_taskDeleteHandler(Medium::DeleteTask &task)
7572{
7573 NOREF(task);
7574 HRESULT rc = S_OK;
7575
7576 try
7577 {
7578 /* The lock is also used as a signal from the task initiator (which
7579 * releases it only after RTThreadCreate()) that we can start the job */
7580 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
7581
7582 PVBOXHDD hdd;
7583 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
7584 ComAssertRCThrow(vrc, E_FAIL);
7585
7586 Utf8Str format(m->strFormat);
7587 Utf8Str location(m->strLocationFull);
7588
7589 /* unlock before the potentially lengthy operation */
7590 Assert(m->state == MediumState_Deleting);
7591 thisLock.release();
7592
7593 try
7594 {
7595 vrc = VDOpen(hdd,
7596 format.c_str(),
7597 location.c_str(),
7598 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
7599 m->vdImageIfaces);
7600 if (RT_SUCCESS(vrc))
7601 vrc = VDClose(hdd, true /* fDelete */);
7602
7603 if (RT_FAILURE(vrc))
7604 throw setError(VBOX_E_FILE_ERROR,
7605 tr("Could not delete the medium storage unit '%s'%s"),
7606 location.c_str(), i_vdError(vrc).c_str());
7607
7608 }
7609 catch (HRESULT aRC) { rc = aRC; }
7610
7611 VDDestroy(hdd);
7612 }
7613 catch (HRESULT aRC) { rc = aRC; }
7614
7615 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
7616
7617 /* go to the NotCreated state even on failure since the storage
7618 * may have been already partially deleted and cannot be used any
7619 * more. One will be able to manually re-open the storage if really
7620 * needed to re-register it. */
7621 m->state = MediumState_NotCreated;
7622
7623 /* Reset UUID to prevent Create* from reusing it again */
7624 unconst(m->id).clear();
7625
7626 return rc;
7627}
7628
7629/**
7630 * Implementation code for the "reset" task.
7631 *
7632 * This always gets started asynchronously from Medium::Reset().
7633 *
7634 * @param task
7635 * @return
7636 */
7637HRESULT Medium::i_taskResetHandler(Medium::ResetTask &task)
7638{
7639 HRESULT rc = S_OK;
7640
7641 uint64_t size = 0, logicalSize = 0;
7642 MediumVariant_T variant = MediumVariant_Standard;
7643
7644 try
7645 {
7646 /* The lock is also used as a signal from the task initiator (which
7647 * releases it only after RTThreadCreate()) that we can start the job */
7648 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
7649
7650 /// @todo Below we use a pair of delete/create operations to reset
7651 /// the diff contents but the most efficient way will of course be
7652 /// to add a VDResetDiff() API call
7653
7654 PVBOXHDD hdd;
7655 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
7656 ComAssertRCThrow(vrc, E_FAIL);
7657
7658 Guid id = m->id;
7659 Utf8Str format(m->strFormat);
7660 Utf8Str location(m->strLocationFull);
7661
7662 Medium *pParent = m->pParent;
7663 Guid parentId = pParent->m->id;
7664 Utf8Str parentFormat(pParent->m->strFormat);
7665 Utf8Str parentLocation(pParent->m->strLocationFull);
7666
7667 Assert(m->state == MediumState_LockedWrite);
7668
7669 /* unlock before the potentially lengthy operation */
7670 thisLock.release();
7671
7672 try
7673 {
7674 /* Open all media in the target chain but the last. */
7675 MediumLockList::Base::const_iterator targetListBegin =
7676 task.mpMediumLockList->GetBegin();
7677 MediumLockList::Base::const_iterator targetListEnd =
7678 task.mpMediumLockList->GetEnd();
7679 for (MediumLockList::Base::const_iterator it = targetListBegin;
7680 it != targetListEnd;
7681 ++it)
7682 {
7683 const MediumLock &mediumLock = *it;
7684 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
7685
7686 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
7687
7688 /* sanity check, "this" is checked above */
7689 Assert( pMedium == this
7690 || pMedium->m->state == MediumState_LockedRead);
7691
7692 /* Open all media in appropriate mode. */
7693 vrc = VDOpen(hdd,
7694 pMedium->m->strFormat.c_str(),
7695 pMedium->m->strLocationFull.c_str(),
7696 VD_OPEN_FLAGS_READONLY | m->uOpenFlagsDef,
7697 pMedium->m->vdImageIfaces);
7698 if (RT_FAILURE(vrc))
7699 throw setError(VBOX_E_FILE_ERROR,
7700 tr("Could not open the medium storage unit '%s'%s"),
7701 pMedium->m->strLocationFull.c_str(),
7702 i_vdError(vrc).c_str());
7703
7704 /* Done when we hit the media which should be reset */
7705 if (pMedium == this)
7706 break;
7707 }
7708
7709 /* first, delete the storage unit */
7710 vrc = VDClose(hdd, true /* fDelete */);
7711 if (RT_FAILURE(vrc))
7712 throw setError(VBOX_E_FILE_ERROR,
7713 tr("Could not delete the medium storage unit '%s'%s"),
7714 location.c_str(), i_vdError(vrc).c_str());
7715
7716 /* next, create it again */
7717 vrc = VDOpen(hdd,
7718 parentFormat.c_str(),
7719 parentLocation.c_str(),
7720 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef,
7721 m->vdImageIfaces);
7722 if (RT_FAILURE(vrc))
7723 throw setError(VBOX_E_FILE_ERROR,
7724 tr("Could not open the medium storage unit '%s'%s"),
7725 parentLocation.c_str(), i_vdError(vrc).c_str());
7726
7727 vrc = VDCreateDiff(hdd,
7728 format.c_str(),
7729 location.c_str(),
7730 /// @todo use the same medium variant as before
7731 VD_IMAGE_FLAGS_NONE,
7732 NULL,
7733 id.raw(),
7734 parentId.raw(),
7735 VD_OPEN_FLAGS_NORMAL,
7736 m->vdImageIfaces,
7737 task.mVDOperationIfaces);
7738 if (RT_FAILURE(vrc))
7739 throw setError(VBOX_E_FILE_ERROR,
7740 tr("Could not create the differencing medium storage unit '%s'%s"),
7741 location.c_str(), i_vdError(vrc).c_str());
7742
7743 size = VDGetFileSize(hdd, VD_LAST_IMAGE);
7744 logicalSize = VDGetSize(hdd, VD_LAST_IMAGE);
7745 unsigned uImageFlags;
7746 vrc = VDGetImageFlags(hdd, 0, &uImageFlags);
7747 if (RT_SUCCESS(vrc))
7748 variant = (MediumVariant_T)uImageFlags;
7749 }
7750 catch (HRESULT aRC) { rc = aRC; }
7751
7752 VDDestroy(hdd);
7753 }
7754 catch (HRESULT aRC) { rc = aRC; }
7755
7756 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
7757
7758 m->size = size;
7759 m->logicalSize = logicalSize;
7760 m->variant = variant;
7761
7762 /* Everything is explicitly unlocked when the task exits,
7763 * as the task destruction also destroys the media chain. */
7764
7765 return rc;
7766}
7767
7768/**
7769 * Implementation code for the "compact" task.
7770 *
7771 * @param task
7772 * @return
7773 */
7774HRESULT Medium::i_taskCompactHandler(Medium::CompactTask &task)
7775{
7776 HRESULT rc = S_OK;
7777
7778 /* Lock all in {parent,child} order. The lock is also used as a
7779 * signal from the task initiator (which releases it only after
7780 * RTThreadCreate()) that we can start the job. */
7781 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
7782
7783 try
7784 {
7785 PVBOXHDD hdd;
7786 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
7787 ComAssertRCThrow(vrc, E_FAIL);
7788
7789 try
7790 {
7791 /* Open all media in the chain. */
7792 MediumLockList::Base::const_iterator mediumListBegin =
7793 task.mpMediumLockList->GetBegin();
7794 MediumLockList::Base::const_iterator mediumListEnd =
7795 task.mpMediumLockList->GetEnd();
7796 MediumLockList::Base::const_iterator mediumListLast =
7797 mediumListEnd;
7798 mediumListLast--;
7799 for (MediumLockList::Base::const_iterator it = mediumListBegin;
7800 it != mediumListEnd;
7801 ++it)
7802 {
7803 const MediumLock &mediumLock = *it;
7804 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
7805 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
7806
7807 /* sanity check */
7808 if (it == mediumListLast)
7809 Assert(pMedium->m->state == MediumState_LockedWrite);
7810 else
7811 Assert(pMedium->m->state == MediumState_LockedRead);
7812
7813 /* Open all media but last in read-only mode. Do not handle
7814 * shareable media, as compaction and sharing are mutually
7815 * exclusive. */
7816 vrc = VDOpen(hdd,
7817 pMedium->m->strFormat.c_str(),
7818 pMedium->m->strLocationFull.c_str(),
7819 m->uOpenFlagsDef | (it == mediumListLast ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY),
7820 pMedium->m->vdImageIfaces);
7821 if (RT_FAILURE(vrc))
7822 throw setError(VBOX_E_FILE_ERROR,
7823 tr("Could not open the medium storage unit '%s'%s"),
7824 pMedium->m->strLocationFull.c_str(),
7825 i_vdError(vrc).c_str());
7826 }
7827
7828 Assert(m->state == MediumState_LockedWrite);
7829
7830 Utf8Str location(m->strLocationFull);
7831
7832 /* unlock before the potentially lengthy operation */
7833 thisLock.release();
7834
7835 vrc = VDCompact(hdd, VD_LAST_IMAGE, task.mVDOperationIfaces);
7836 if (RT_FAILURE(vrc))
7837 {
7838 if (vrc == VERR_NOT_SUPPORTED)
7839 throw setError(VBOX_E_NOT_SUPPORTED,
7840 tr("Compacting is not yet supported for medium '%s'"),
7841 location.c_str());
7842 else if (vrc == VERR_NOT_IMPLEMENTED)
7843 throw setError(E_NOTIMPL,
7844 tr("Compacting is not implemented, medium '%s'"),
7845 location.c_str());
7846 else
7847 throw setError(VBOX_E_FILE_ERROR,
7848 tr("Could not compact medium '%s'%s"),
7849 location.c_str(),
7850 i_vdError(vrc).c_str());
7851 }
7852 }
7853 catch (HRESULT aRC) { rc = aRC; }
7854
7855 VDDestroy(hdd);
7856 }
7857 catch (HRESULT aRC) { rc = aRC; }
7858
7859 /* Everything is explicitly unlocked when the task exits,
7860 * as the task destruction also destroys the media chain. */
7861
7862 return rc;
7863}
7864
7865/**
7866 * Implementation code for the "resize" task.
7867 *
7868 * @param task
7869 * @return
7870 */
7871HRESULT Medium::i_taskResizeHandler(Medium::ResizeTask &task)
7872{
7873 HRESULT rc = S_OK;
7874
7875 uint64_t size = 0, logicalSize = 0;
7876
7877 try
7878 {
7879 /* The lock is also used as a signal from the task initiator (which
7880 * releases it only after RTThreadCreate()) that we can start the job */
7881 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
7882
7883 PVBOXHDD hdd;
7884 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
7885 ComAssertRCThrow(vrc, E_FAIL);
7886
7887 try
7888 {
7889 /* Open all media in the chain. */
7890 MediumLockList::Base::const_iterator mediumListBegin =
7891 task.mpMediumLockList->GetBegin();
7892 MediumLockList::Base::const_iterator mediumListEnd =
7893 task.mpMediumLockList->GetEnd();
7894 MediumLockList::Base::const_iterator mediumListLast =
7895 mediumListEnd;
7896 mediumListLast--;
7897 for (MediumLockList::Base::const_iterator it = mediumListBegin;
7898 it != mediumListEnd;
7899 ++it)
7900 {
7901 const MediumLock &mediumLock = *it;
7902 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
7903 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
7904
7905 /* sanity check */
7906 if (it == mediumListLast)
7907 Assert(pMedium->m->state == MediumState_LockedWrite);
7908 else
7909 Assert(pMedium->m->state == MediumState_LockedRead);
7910
7911 /* Open all media but last in read-only mode. Do not handle
7912 * shareable media, as compaction and sharing are mutually
7913 * exclusive. */
7914 vrc = VDOpen(hdd,
7915 pMedium->m->strFormat.c_str(),
7916 pMedium->m->strLocationFull.c_str(),
7917 m->uOpenFlagsDef | (it == mediumListLast ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY),
7918 pMedium->m->vdImageIfaces);
7919 if (RT_FAILURE(vrc))
7920 throw setError(VBOX_E_FILE_ERROR,
7921 tr("Could not open the medium storage unit '%s'%s"),
7922 pMedium->m->strLocationFull.c_str(),
7923 i_vdError(vrc).c_str());
7924 }
7925
7926 Assert(m->state == MediumState_LockedWrite);
7927
7928 Utf8Str location(m->strLocationFull);
7929
7930 /* unlock before the potentially lengthy operation */
7931 thisLock.release();
7932
7933 VDGEOMETRY geo = {0, 0, 0}; /* auto */
7934 vrc = VDResize(hdd, task.mSize, &geo, &geo, task.mVDOperationIfaces);
7935 if (RT_FAILURE(vrc))
7936 {
7937 if (vrc == VERR_NOT_SUPPORTED)
7938 throw setError(VBOX_E_NOT_SUPPORTED,
7939 tr("Resizing to new size %llu is not yet supported for medium '%s'"),
7940 task.mSize, location.c_str());
7941 else if (vrc == VERR_NOT_IMPLEMENTED)
7942 throw setError(E_NOTIMPL,
7943 tr("Resiting is not implemented, medium '%s'"),
7944 location.c_str());
7945 else
7946 throw setError(VBOX_E_FILE_ERROR,
7947 tr("Could not resize medium '%s'%s"),
7948 location.c_str(),
7949 i_vdError(vrc).c_str());
7950 }
7951 size = VDGetFileSize(hdd, VD_LAST_IMAGE);
7952 logicalSize = VDGetSize(hdd, VD_LAST_IMAGE);
7953 }
7954 catch (HRESULT aRC) { rc = aRC; }
7955
7956 VDDestroy(hdd);
7957 }
7958 catch (HRESULT aRC) { rc = aRC; }
7959
7960 if (SUCCEEDED(rc))
7961 {
7962 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
7963 m->size = size;
7964 m->logicalSize = logicalSize;
7965 }
7966
7967 /* Everything is explicitly unlocked when the task exits,
7968 * as the task destruction also destroys the media chain. */
7969
7970 return rc;
7971}
7972
7973/**
7974 * Implementation code for the "export" task.
7975 *
7976 * This only gets started from Medium::exportFile() and always runs
7977 * asynchronously. It doesn't touch anything configuration related, so
7978 * we never save the VirtualBox.xml file here.
7979 *
7980 * @param task
7981 * @return
7982 */
7983HRESULT Medium::i_taskExportHandler(Medium::ExportTask &task)
7984{
7985 HRESULT rc = S_OK;
7986
7987 try
7988 {
7989 /* Lock all in {parent,child} order. The lock is also used as a
7990 * signal from the task initiator (which releases it only after
7991 * RTThreadCreate()) that we can start the job. */
7992 AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS);
7993
7994 PVBOXHDD hdd;
7995 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
7996 ComAssertRCThrow(vrc, E_FAIL);
7997
7998 try
7999 {
8000 /* Open all media in the source chain. */
8001 MediumLockList::Base::const_iterator sourceListBegin =
8002 task.mpSourceMediumLockList->GetBegin();
8003 MediumLockList::Base::const_iterator sourceListEnd =
8004 task.mpSourceMediumLockList->GetEnd();
8005 for (MediumLockList::Base::const_iterator it = sourceListBegin;
8006 it != sourceListEnd;
8007 ++it)
8008 {
8009 const MediumLock &mediumLock = *it;
8010 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
8011 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
8012
8013 /* sanity check */
8014 Assert(pMedium->m->state == MediumState_LockedRead);
8015
8016 /* Open all media in read-only mode. */
8017 vrc = VDOpen(hdd,
8018 pMedium->m->strFormat.c_str(),
8019 pMedium->m->strLocationFull.c_str(),
8020 VD_OPEN_FLAGS_READONLY | m->uOpenFlagsDef,
8021 pMedium->m->vdImageIfaces);
8022 if (RT_FAILURE(vrc))
8023 throw setError(VBOX_E_FILE_ERROR,
8024 tr("Could not open the medium storage unit '%s'%s"),
8025 pMedium->m->strLocationFull.c_str(),
8026 i_vdError(vrc).c_str());
8027 }
8028
8029 Utf8Str targetFormat(task.mFormat->i_getId());
8030 Utf8Str targetLocation(task.mFilename);
8031 uint64_t capabilities = task.mFormat->i_getCapabilities();
8032
8033 Assert(m->state == MediumState_LockedRead);
8034
8035 /* unlock before the potentially lengthy operation */
8036 thisLock.release();
8037
8038 /* ensure the target directory exists */
8039 if (capabilities & MediumFormatCapabilities_File)
8040 {
8041 rc = VirtualBox::i_ensureFilePathExists(targetLocation,
8042 !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
8043 if (FAILED(rc))
8044 throw rc;
8045 }
8046
8047 PVBOXHDD targetHdd;
8048 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &targetHdd);
8049 ComAssertRCThrow(vrc, E_FAIL);
8050
8051 try
8052 {
8053 vrc = VDCopy(hdd,
8054 VD_LAST_IMAGE,
8055 targetHdd,
8056 targetFormat.c_str(),
8057 targetLocation.c_str(),
8058 false /* fMoveByRename */,
8059 0 /* cbSize */,
8060 task.mVariant & ~MediumVariant_NoCreateDir,
8061 NULL /* pDstUuid */,
8062 VD_OPEN_FLAGS_NORMAL | VD_OPEN_FLAGS_SEQUENTIAL,
8063 NULL /* pVDIfsOperation */,
8064 task.mVDImageIfaces,
8065 task.mVDOperationIfaces);
8066 if (RT_FAILURE(vrc))
8067 throw setError(VBOX_E_FILE_ERROR,
8068 tr("Could not create the exported medium '%s'%s"),
8069 targetLocation.c_str(), i_vdError(vrc).c_str());
8070 }
8071 catch (HRESULT aRC) { rc = aRC; }
8072
8073 VDDestroy(targetHdd);
8074 }
8075 catch (HRESULT aRC) { rc = aRC; }
8076
8077 VDDestroy(hdd);
8078 }
8079 catch (HRESULT aRC) { rc = aRC; }
8080
8081 /* Everything is explicitly unlocked when the task exits,
8082 * as the task destruction also destroys the source chain. */
8083
8084 /* Make sure the source chain is released early, otherwise it can
8085 * lead to deadlocks with concurrent IAppliance activities. */
8086 task.mpSourceMediumLockList->Clear();
8087
8088 return rc;
8089}
8090
8091/**
8092 * Implementation code for the "import" task.
8093 *
8094 * This only gets started from Medium::importFile() and always runs
8095 * asynchronously. It potentially touches the media registry, so we
8096 * always save the VirtualBox.xml file when we're done here.
8097 *
8098 * @param task
8099 * @return
8100 */
8101HRESULT Medium::i_taskImportHandler(Medium::ImportTask &task)
8102{
8103 HRESULT rcTmp = S_OK;
8104
8105 const ComObjPtr<Medium> &pParent = task.mParent;
8106
8107 bool fCreatingTarget = false;
8108
8109 uint64_t size = 0, logicalSize = 0;
8110 MediumVariant_T variant = MediumVariant_Standard;
8111 bool fGenerateUuid = false;
8112
8113 try
8114 {
8115 /* Lock all in {parent,child} order. The lock is also used as a
8116 * signal from the task initiator (which releases it only after
8117 * RTThreadCreate()) that we can start the job. */
8118 AutoMultiWriteLock2 thisLock(this, pParent COMMA_LOCKVAL_SRC_POS);
8119
8120 fCreatingTarget = m->state == MediumState_Creating;
8121
8122 /* The object may request a specific UUID (through a special form of
8123 * the setLocation() argument). Otherwise we have to generate it */
8124 Guid targetId = m->id;
8125
8126 fGenerateUuid = targetId.isZero();
8127 if (fGenerateUuid)
8128 {
8129 targetId.create();
8130 /* VirtualBox::i_registerMedium() will need UUID */
8131 unconst(m->id) = targetId;
8132 }
8133
8134
8135 PVBOXHDD hdd;
8136 int vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &hdd);
8137 ComAssertRCThrow(vrc, E_FAIL);
8138
8139 try
8140 {
8141 /* Open source medium. */
8142 vrc = VDOpen(hdd,
8143 task.mFormat->i_getId().c_str(),
8144 task.mFilename.c_str(),
8145 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SEQUENTIAL | m->uOpenFlagsDef,
8146 task.mVDImageIfaces);
8147 if (RT_FAILURE(vrc))
8148 throw setError(VBOX_E_FILE_ERROR,
8149 tr("Could not open the medium storage unit '%s'%s"),
8150 task.mFilename.c_str(),
8151 i_vdError(vrc).c_str());
8152
8153 Utf8Str targetFormat(m->strFormat);
8154 Utf8Str targetLocation(m->strLocationFull);
8155 uint64_t capabilities = task.mFormat->i_getCapabilities();
8156
8157 Assert( m->state == MediumState_Creating
8158 || m->state == MediumState_LockedWrite);
8159 Assert( pParent.isNull()
8160 || pParent->m->state == MediumState_LockedRead);
8161
8162 /* unlock before the potentially lengthy operation */
8163 thisLock.release();
8164
8165 /* ensure the target directory exists */
8166 if (capabilities & MediumFormatCapabilities_File)
8167 {
8168 HRESULT rc = VirtualBox::i_ensureFilePathExists(targetLocation,
8169 !(task.mVariant & MediumVariant_NoCreateDir) /* fCreate */);
8170 if (FAILED(rc))
8171 throw rc;
8172 }
8173
8174 PVBOXHDD targetHdd;
8175 vrc = VDCreate(m->vdDiskIfaces, i_convertDeviceType(), &targetHdd);
8176 ComAssertRCThrow(vrc, E_FAIL);
8177
8178 try
8179 {
8180 /* Open all media in the target chain. */
8181 MediumLockList::Base::const_iterator targetListBegin =
8182 task.mpTargetMediumLockList->GetBegin();
8183 MediumLockList::Base::const_iterator targetListEnd =
8184 task.mpTargetMediumLockList->GetEnd();
8185 for (MediumLockList::Base::const_iterator it = targetListBegin;
8186 it != targetListEnd;
8187 ++it)
8188 {
8189 const MediumLock &mediumLock = *it;
8190 const ComObjPtr<Medium> &pMedium = mediumLock.GetMedium();
8191
8192 /* If the target medium is not created yet there's no
8193 * reason to open it. */
8194 if (pMedium == this && fCreatingTarget)
8195 continue;
8196
8197 AutoReadLock alock(pMedium COMMA_LOCKVAL_SRC_POS);
8198
8199 /* sanity check */
8200 Assert( pMedium->m->state == MediumState_LockedRead
8201 || pMedium->m->state == MediumState_LockedWrite);
8202
8203 unsigned uOpenFlags = VD_OPEN_FLAGS_NORMAL;
8204 if (pMedium->m->state != MediumState_LockedWrite)
8205 uOpenFlags = VD_OPEN_FLAGS_READONLY;
8206 if (pMedium->m->type == MediumType_Shareable)
8207 uOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
8208
8209 /* Open all media in appropriate mode. */
8210 vrc = VDOpen(targetHdd,
8211 pMedium->m->strFormat.c_str(),
8212 pMedium->m->strLocationFull.c_str(),
8213 uOpenFlags | m->uOpenFlagsDef,
8214 pMedium->m->vdImageIfaces);
8215 if (RT_FAILURE(vrc))
8216 throw setError(VBOX_E_FILE_ERROR,
8217 tr("Could not open the medium storage unit '%s'%s"),
8218 pMedium->m->strLocationFull.c_str(),
8219 i_vdError(vrc).c_str());
8220 }
8221
8222 /** @todo r=klaus target isn't locked, race getting the state */
8223 vrc = VDCopy(hdd,
8224 VD_LAST_IMAGE,
8225 targetHdd,
8226 targetFormat.c_str(),
8227 (fCreatingTarget) ? targetLocation.c_str() : (char *)NULL,
8228 false /* fMoveByRename */,
8229 0 /* cbSize */,
8230 task.mVariant & ~MediumVariant_NoCreateDir,
8231 targetId.raw(),
8232 VD_OPEN_FLAGS_NORMAL,
8233 NULL /* pVDIfsOperation */,
8234 m->vdImageIfaces,
8235 task.mVDOperationIfaces);
8236 if (RT_FAILURE(vrc))
8237 throw setError(VBOX_E_FILE_ERROR,
8238 tr("Could not create the imported medium '%s'%s"),
8239 targetLocation.c_str(), i_vdError(vrc).c_str());
8240
8241 size = VDGetFileSize(targetHdd, VD_LAST_IMAGE);
8242 logicalSize = VDGetSize(targetHdd, VD_LAST_IMAGE);
8243 unsigned uImageFlags;
8244 vrc = VDGetImageFlags(targetHdd, 0, &uImageFlags);
8245 if (RT_SUCCESS(vrc))
8246 variant = (MediumVariant_T)uImageFlags;
8247 }
8248 catch (HRESULT aRC) { rcTmp = aRC; }
8249
8250 VDDestroy(targetHdd);
8251 }
8252 catch (HRESULT aRC) { rcTmp = aRC; }
8253
8254 VDDestroy(hdd);
8255 }
8256 catch (HRESULT aRC) { rcTmp = aRC; }
8257
8258 ErrorInfoKeeper eik;
8259 MultiResult mrc(rcTmp);
8260
8261 /* Only do the parent changes for newly created media. */
8262 if (SUCCEEDED(mrc) && fCreatingTarget)
8263 {
8264 /* we set mParent & children() */
8265 AutoWriteLock treeLock(m->pVirtualBox->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
8266
8267 Assert(m->pParent.isNull());
8268
8269 if (pParent)
8270 {
8271 /* associate the imported medium with the parent and deassociate
8272 * from VirtualBox */
8273 m->pParent = pParent;
8274 pParent->m->llChildren.push_back(this);
8275
8276 /* register with mVirtualBox as the last step and move to
8277 * Created state only on success (leaving an orphan file is
8278 * better than breaking media registry consistency) */
8279 eik.restore();
8280 ComObjPtr<Medium> pMedium;
8281 mrc = pParent->m->pVirtualBox->i_registerMedium(this, &pMedium,
8282 DeviceType_HardDisk,
8283 treeLock);
8284 Assert(this == pMedium);
8285 eik.fetch();
8286
8287 if (FAILED(mrc))
8288 /* break parent association on failure to register */
8289 this->i_deparent(); // removes target from parent
8290 }
8291 else
8292 {
8293 /* just register */
8294 eik.restore();
8295 ComObjPtr<Medium> pMedium;
8296 mrc = m->pVirtualBox->i_registerMedium(this, &pMedium, DeviceType_HardDisk, treeLock);
8297 Assert(this == pMedium);
8298 eik.fetch();
8299 }
8300 }
8301
8302 if (fCreatingTarget)
8303 {
8304 AutoWriteLock mLock(this COMMA_LOCKVAL_SRC_POS);
8305
8306 if (SUCCEEDED(mrc))
8307 {
8308 m->state = MediumState_Created;
8309
8310 m->size = size;
8311 m->logicalSize = logicalSize;
8312 m->variant = variant;
8313 }
8314 else
8315 {
8316 /* back to NotCreated on failure */
8317 m->state = MediumState_NotCreated;
8318
8319 /* reset UUID to prevent it from being reused next time */
8320 if (fGenerateUuid)
8321 unconst(m->id).clear();
8322 }
8323 }
8324
8325 // now, at the end of this task (always asynchronous), save the settings
8326 {
8327 // save the settings
8328 i_markRegistriesModified();
8329 /* collect multiple errors */
8330 eik.restore();
8331 m->pVirtualBox->i_saveModifiedRegistries();
8332 eik.fetch();
8333 }
8334
8335 /* Everything is explicitly unlocked when the task exits,
8336 * as the task destruction also destroys the target chain. */
8337
8338 /* Make sure the target chain is released early, otherwise it can
8339 * lead to deadlocks with concurrent IAppliance activities. */
8340 task.mpTargetMediumLockList->Clear();
8341
8342 return mrc;
8343}
8344
8345/* 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