VirtualBox

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

Last change on this file since 38709 was 38709, checked in by vboxsync, 14 years ago

Main/Medium: replace the event semaphore used to wait for queryInfo by a RW semaphore

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

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