VirtualBox

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

最後變更 在這個檔案從34772是 34734,由 vboxsync 提交於 14 年 前

Main/Medium: for newly created media which doesn't support creating (iSCSI) mark the medium as Inaccessible, as otherwise no one fills in the "small details", such as the exact medium variant. Easiest and most consistent fix which doesn't need queryInfo() which might take really long, especially for iSCSI.

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

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