VirtualBox

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

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

Main/Medium+Machine+AutoLock: Integrate the queryInfo semaphore into the AutoLock infrastructure with the appropriate lock class. Adjust the locking rules for queryInfo and start propagating them up to the callers. Needs more work, but this is comparably simple: just get the mediaTree lock in the caller if the assert triggers.

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