VirtualBox

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

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

Main/Medium: Query the capabilites of the target format used for the diff image and not the capabilities of the image we create the diff for. Fixes creating snapshots of images which don't support diff images (Raw for example)

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