VirtualBox

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

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

Main/NetworkAdapter+Machine+Appliance+SystemProperties+Medium+Console+Settings+IDL: make NAT networking a separate network attachment type which improves the user experience, store the necessary settings, plus changing the design of the methods which will move images and entire VMs, they lacked a progress object
Frontends/VirtualBox: adapted fully, can configure NAT networks with proper drop down list support
Frontends/VBoxManage: also supports NAT networks completely, and adds the long missing code to list intnets

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