VirtualBox

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

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

Main: do not return first item from list of registry IDs unconditionally since that list may be empty; this fixes the problem that media registry do not get saved properly if a new immutable image gets attached to a machine

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