VirtualBox

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

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

Main/Medium: Fix incorrect forcing of absolute paths for non-file based media. Triggered with iSCSI, causing strange full location values. Also clean up code a bit which checks backend capabilities.

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

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