VirtualBox

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

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

Storage/VBoxHDD: replace custom open flags with regular IPRT file open flags, introduce user-providable filesystem access interface, eliminate dependency on PGM geometry structure, change pvBuffer/cbBuffer parameter ordering to the usual conventions, eliminate the remains of the old I/O code, make more plugin methods optional to reduce redundancy, lots of cleanups

Storage/DrvVD+testcases,Main/Medium+Frontends: adapt to VBoxHDD changes, logging fixes

Storage/VDI+VMDK+DMG+Raw+VHD+Parallels+VCI: made as similar to each other as possible, added inline VFS wrappers to improve readability, full VFS support, VDI files are now 4K aligned, eliminate the remains of the old I/O code, various more or less severe bugfixes, code sort

Storage/iSCSI: support disks bigger than 2T, streamline the code to be more similar to the file-based backends, memory leak fix, error code usage like file-based backends, code sort

log+err: added new error codes/log groups and eliminated unused old ones

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

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