VirtualBox

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

Last change on this file since 37985 was 37985, checked in by vboxsync, 13 years ago

Main/Machine: fix the medium registry logic for linked clones. Previously creating a linked clone of a snapshot of a linked clone didn't result in the right medium registry update.

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