VirtualBox

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

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

Main: AutoCaller/VirtualBoxBase refactoring, cleanly splitting out the object state handling, and moving all caller synchronization to one file. Also eliminated a not so vital template (AutoCallerBase) by much simpler inheritance. Theoretically has no visible effects, the behavior should be identical. Done as a preparation for reimplementing the caller synchronization.

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

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