VirtualBox

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

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

Main/Medium: Fix VBoxSVC crashes when quering iSCSI targets with authentication configured

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