VirtualBox

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

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

Main/VirtualBox+Medium: resurrect the feature of changing the medium UUID when opening the image, which allows to resolve duplicate UUIDs without using external tools. Also fixes Medium::setIDs, which wasn't correctly working.
Frontends/*: corresponding changes.

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

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