VirtualBox

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

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

Main/Medium: added new attribute for getting the possible medium types, not implemented right now

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

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