VirtualBox

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

Last change on this file since 50213 was 49951, checked in by vboxsync, 11 years ago

6813 - stage 5 - Make use of server side API wrapper code in all interfaces

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