VirtualBox

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

Last change on this file since 35119 was 35082, checked in by vboxsync, 14 years ago

Main: prevent attaching MultiAttach/Readonly media to pre-4.0 machines for backwards settings compatibility

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

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