VirtualBox

source: vbox/trunk/src/VBox/Main/MediumImpl.cpp@ 33082

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

Storage/VBoxHDD: allow specifying the open mode explicitly, it makes no sense to copy the open mode (even with the readonly hacks we had)

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

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