VirtualBox

source: vbox/trunk/src/VBox/Main/MediumImpl.cpp@ 35061

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

Main/Medium+Machine+Settings: new medium type MultiAttach, meant for having many VMs pointing at the same base medium and each having its own diff for changes

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