VirtualBox

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

Last change on this file since 49498 was 48967, checked in by vboxsync, 11 years ago

Main/MediumImpl: be more silent if Special medium properties are not there

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

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