VirtualBox

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

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

Main/Medium: provide error message for duplicate attachment of one medium to the current state of a VM, so that users have a hint what needs fixing

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