summaryrefslogtreecommitdiffstats
path: root/src/VBox/Main/src-server/MediumAttachmentImpl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Main/src-server/MediumAttachmentImpl.cpp')
-rw-r--r--src/VBox/Main/src-server/MediumAttachmentImpl.cpp644
1 files changed, 644 insertions, 0 deletions
diff --git a/src/VBox/Main/src-server/MediumAttachmentImpl.cpp b/src/VBox/Main/src-server/MediumAttachmentImpl.cpp
new file mode 100644
index 00000000..c29bc621
--- /dev/null
+++ b/src/VBox/Main/src-server/MediumAttachmentImpl.cpp
@@ -0,0 +1,644 @@
+/* $Id: MediumAttachmentImpl.cpp $ */
+/** @file
+ * VirtualBox COM class implementation
+ */
+
+/*
+ * Copyright (C) 2006-2022 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#define LOG_GROUP LOG_GROUP_MAIN_MEDIUMATTACHMENT
+#include "MediumAttachmentImpl.h"
+#include "MachineImpl.h"
+#include "MediumImpl.h"
+#include "Global.h"
+#include "StringifyEnums.h"
+
+#include "AutoCaller.h"
+#include "LoggingNew.h"
+
+#include <iprt/cpp/utils.h>
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// private member data definition
+//
+////////////////////////////////////////////////////////////////////////////////
+
+struct BackupableMediumAttachmentData
+{
+ BackupableMediumAttachmentData()
+ : fImplicit(false)
+ { }
+
+ ComObjPtr<Medium> pMedium;
+ /* Since MediumAttachment is not a first class citizen when it
+ * comes to managing settings, having a reference to the storage
+ * controller will not work - when settings are changed it will point
+ * to the old, uninitialized instance. Changing this requires
+ * substantial changes to MediumImpl.cpp. */
+ /* Same counts for the assigned bandwidth group */
+ bool fImplicit;
+ const Utf8Str strControllerName;
+ settings::AttachedDevice mData;
+};
+
+struct MediumAttachment::Data
+{
+ Data(Machine * const aMachine = NULL)
+ : pMachine(aMachine),
+ fIsEjected(false)
+ { }
+
+ /** Reference to Machine object, for checking mutable state. */
+ Machine * const pMachine;
+ /* later: const ComObjPtr<MediumAttachment> mPeer; */
+ bool fIsEjected;
+ Backupable<BackupableMediumAttachmentData> bd;
+};
+
+// constructor / destructor
+/////////////////////////////////////////////////////////////////////////////
+
+DEFINE_EMPTY_CTOR_DTOR(MediumAttachment)
+
+HRESULT MediumAttachment::FinalConstruct()
+{
+ LogFlowThisFunc(("\n"));
+ return BaseFinalConstruct();
+}
+
+void MediumAttachment::FinalRelease()
+{
+ LogFlowThisFuncEnter();
+ uninit();
+ BaseFinalRelease();
+ LogFlowThisFuncLeave();
+}
+
+// public initializer/uninitializer for internal purposes only
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Initializes the medium attachment object.
+ *
+ * @param aParent Machine object.
+ * @param aMedium Medium object.
+ * @param aControllerName Controller the hard disk is attached to.
+ * @param aPort Port number.
+ * @param aDevice Device number on the port.
+ * @param aType Device type.
+ * @param aImplicit
+ * @param aPassthrough Whether accesses are directly passed to the host drive.
+ * @param aTempEject Whether guest-triggered eject results in unmounting the medium.
+ * @param aNonRotational Whether this medium is non-rotational (aka SSD).
+ * @param aDiscard Whether this medium supports discarding unused blocks.
+ * @param aHotPluggable Whether this medium is hot-pluggable.
+ * @param strBandwidthGroup Bandwidth group.
+ */
+HRESULT MediumAttachment::init(Machine *aParent,
+ Medium *aMedium,
+ const Utf8Str &aControllerName,
+ LONG aPort,
+ LONG aDevice,
+ DeviceType_T aType,
+ bool aImplicit,
+ bool aPassthrough,
+ bool aTempEject,
+ bool aNonRotational,
+ bool aDiscard,
+ bool aHotPluggable,
+ const Utf8Str &strBandwidthGroup)
+{
+ LogFlowThisFuncEnter();
+ LogFlowThisFunc(("aParent=%p aMedium=%p aControllerName=%s aPort=%d aDevice=%d aType=%d aImplicit=%d aPassthrough=%d aTempEject=%d aNonRotational=%d aDiscard=%d aHotPluggable=%d strBandwithGroup=%s\n", aParent, aMedium, aControllerName.c_str(), aPort, aDevice, aType, aImplicit, aPassthrough, aTempEject, aNonRotational, aDiscard, aHotPluggable, strBandwidthGroup.c_str()));
+
+ if (aType == DeviceType_HardDisk)
+ AssertReturn(aMedium, E_INVALIDARG);
+
+ /* Enclose the state transition NotReady->InInit->Ready */
+ AutoInitSpan autoInitSpan(this);
+ AssertReturn(autoInitSpan.isOk(), E_FAIL);
+
+ m = new Data();
+
+ unconst(m->pMachine) = aParent;
+
+ m->bd.allocate();
+ m->bd->pMedium = aMedium;
+ m->bd->mData.strBwGroup = strBandwidthGroup;
+ unconst(m->bd->strControllerName) = aControllerName;
+ m->bd->mData.lPort = aPort;
+ m->bd->mData.lDevice = aDevice;
+ m->bd->mData.deviceType = aType;
+
+ m->bd->mData.fPassThrough = aPassthrough;
+ m->bd->mData.fTempEject = aTempEject;
+ m->bd->mData.fNonRotational = aNonRotational;
+ m->bd->mData.fDiscard = aDiscard;
+ m->bd->fImplicit = aImplicit;
+ m->bd->mData.fHotPluggable = aHotPluggable;
+
+ /* Confirm a successful initialization when it's the case */
+ autoInitSpan.setSucceeded();
+
+ /* Construct a short log name for this attachment. */
+ i_updateLogName();
+
+ LogFlowThisFunc(("LEAVE - %s\n", i_getLogName()));
+ return S_OK;
+}
+
+/**
+ * Initializes the medium attachment object given another guest object
+ * (a kind of copy constructor). This object makes a private copy of data
+ * of the original object passed as an argument.
+ */
+HRESULT MediumAttachment::initCopy(Machine *aParent, MediumAttachment *aThat)
+{
+ LogFlowThisFunc(("aParent=%p, aThat=%p\n", aParent, aThat));
+
+ ComAssertRet(aParent && aThat, E_INVALIDARG);
+
+ /* Enclose the state transition NotReady->InInit->Ready */
+ AutoInitSpan autoInitSpan(this);
+ AssertReturn(autoInitSpan.isOk(), E_FAIL);
+
+ m = new Data(aParent);
+ /* m->pPeer is left null */
+
+ AutoCaller thatCaller(aThat);
+ AssertComRCReturnRC(thatCaller.rc());
+
+ AutoReadLock thatlock(aThat COMMA_LOCKVAL_SRC_POS);
+ m->bd.attachCopy(aThat->m->bd);
+
+ /* Confirm a successful initialization */
+ autoInitSpan.setSucceeded();
+
+ /* Construct a short log name for this attachment. */
+ i_updateLogName();
+
+ LogFlowThisFunc(("LEAVE - %s\n", i_getLogName()));
+ return S_OK;
+}
+
+/**
+ * Uninitializes the instance.
+ * Called from FinalRelease().
+ */
+void MediumAttachment::uninit()
+{
+ LogFlowThisFunc(("ENTER - %s\n", i_getLogName()));
+
+ /* Enclose the state transition Ready->InUninit->NotReady */
+ AutoUninitSpan autoUninitSpan(this);
+ if (autoUninitSpan.uninitDone())
+ return;
+
+ m->bd.free();
+
+ unconst(m->pMachine) = NULL;
+
+ delete m;
+ m = NULL;
+
+ LogFlowThisFuncLeave();
+}
+
+// IHardDiskAttachment properties
+/////////////////////////////////////////////////////////////////////////////
+
+
+HRESULT MediumAttachment::getMachine(ComPtr<IMachine> &aMachine)
+{
+ LogFlowThisFuncEnter();
+
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ ComObjPtr<Machine> pMachine(m->pMachine);
+ pMachine.queryInterfaceTo(aMachine.asOutParam());
+
+ LogFlowThisFuncLeave();
+ return S_OK;
+}
+
+
+HRESULT MediumAttachment::getMedium(ComPtr<IMedium> &aHardDisk)
+{
+ LogFlowThisFuncEnter();
+
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ aHardDisk = m->bd->pMedium;
+
+ LogFlowThisFuncLeave();
+ return S_OK;
+}
+
+
+HRESULT MediumAttachment::getController(com::Utf8Str &aController)
+{
+ LogFlowThisFuncEnter();
+
+ /* m->controller is constant during life time, no need to lock */
+ aController = Utf8Str(m->bd->strControllerName);
+
+ LogFlowThisFuncLeave();
+ return S_OK;
+}
+
+
+HRESULT MediumAttachment::getPort(LONG *aPort)
+{
+ LogFlowThisFuncEnter();
+
+ /* m->bd->port is constant during life time, no need to lock */
+ *aPort = m->bd->mData.lPort;
+
+ LogFlowThisFuncLeave();
+ return S_OK;
+}
+
+HRESULT MediumAttachment::getDevice(LONG *aDevice)
+{
+ LogFlowThisFuncEnter();
+
+ /* m->bd->device is constant during life time, no need to lock */
+ *aDevice = m->bd->mData.lDevice;
+
+ LogFlowThisFuncLeave();
+ return S_OK;
+}
+
+HRESULT MediumAttachment::getType(DeviceType_T *aType)
+{
+ LogFlowThisFuncEnter();
+
+ /* m->bd->type is constant during life time, no need to lock */
+ *aType = m->bd->mData.deviceType;
+
+ LogFlowThisFuncLeave();
+ return S_OK;
+}
+
+
+HRESULT MediumAttachment::getPassthrough(BOOL *aPassthrough)
+{
+ LogFlowThisFuncEnter();
+
+ AutoReadLock lock(this COMMA_LOCKVAL_SRC_POS);
+
+ *aPassthrough = m->bd->mData.fPassThrough;
+
+ LogFlowThisFuncLeave();
+ return S_OK;
+}
+
+
+HRESULT MediumAttachment::getTemporaryEject(BOOL *aTemporaryEject)
+{
+ LogFlowThisFuncEnter();
+
+ AutoReadLock lock(this COMMA_LOCKVAL_SRC_POS);
+
+ *aTemporaryEject = m->bd->mData.fTempEject;
+
+ LogFlowThisFuncLeave();
+ return S_OK;
+}
+
+
+HRESULT MediumAttachment::getIsEjected(BOOL *aEjected)
+{
+ LogFlowThisFuncEnter();
+
+ AutoReadLock lock(this COMMA_LOCKVAL_SRC_POS);
+
+ *aEjected = m->fIsEjected;
+
+ LogFlowThisFuncLeave();
+ return S_OK;
+}
+
+
+HRESULT MediumAttachment::getNonRotational(BOOL *aNonRotational)
+{
+ LogFlowThisFuncEnter();
+
+ AutoReadLock lock(this COMMA_LOCKVAL_SRC_POS);
+
+ *aNonRotational = m->bd->mData.fNonRotational;
+
+ LogFlowThisFuncLeave();
+ return S_OK;
+}
+
+HRESULT MediumAttachment::getDiscard(BOOL *aDiscard)
+{
+ LogFlowThisFuncEnter();
+
+ AutoReadLock lock(this COMMA_LOCKVAL_SRC_POS);
+
+ *aDiscard = m->bd->mData.fDiscard;
+
+ LogFlowThisFuncLeave();
+ return S_OK;
+}
+
+
+HRESULT MediumAttachment::getBandwidthGroup(ComPtr<IBandwidthGroup> &aBandwidthGroup)
+{
+ LogFlowThisFuncEnter();
+
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ HRESULT hrc = S_OK;
+ if (m->bd->mData.strBwGroup.isNotEmpty())
+ {
+ ComObjPtr<BandwidthGroup> pBwGroup;
+ hrc = m->pMachine->i_getBandwidthGroup(m->bd->mData.strBwGroup, pBwGroup, true /* fSetError */);
+
+ Assert(SUCCEEDED(hrc)); /* This is not allowed to fail because the existence of the
+ group was checked when it was attached. */
+
+ if (SUCCEEDED(hrc))
+ pBwGroup.queryInterfaceTo(aBandwidthGroup.asOutParam());
+ }
+
+ LogFlowThisFuncLeave();
+ return hrc;
+}
+
+HRESULT MediumAttachment::getHotPluggable(BOOL *aHotPluggable)
+{
+ LogFlowThisFuncEnter();
+
+ AutoReadLock lock(this COMMA_LOCKVAL_SRC_POS);
+
+ *aHotPluggable = m->bd->mData.fHotPluggable;
+
+ LogFlowThisFuncLeave();
+ return S_OK;
+}
+
+/**
+ * @note Locks this object for writing.
+ */
+void MediumAttachment::i_rollback()
+{
+ LogFlowThisFunc(("ENTER - %s\n", i_getLogName()));
+
+ /* sanity */
+ AutoCaller autoCaller(this);
+ AssertComRCReturnVoid(autoCaller.rc());
+
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ m->bd.rollback();
+
+ LogFlowThisFunc(("LEAVE - %s\n", i_getLogName()));
+}
+
+/**
+ * @note Locks this object for writing.
+ */
+void MediumAttachment::i_commit()
+{
+ LogFlowThisFunc(("ENTER - %s\n", i_getLogName()));
+
+ /* sanity */
+ AutoCaller autoCaller(this);
+ AssertComRCReturnVoid(autoCaller.rc());
+
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ if (m->bd.isBackedUp())
+ m->bd.commit();
+
+ LogFlowThisFunc(("LEAVE - %s\n", i_getLogName()));
+}
+
+bool MediumAttachment::i_isImplicit() const
+{
+ return m->bd->fImplicit;
+}
+
+void MediumAttachment::i_setImplicit(bool aImplicit)
+{
+ Assert(!m->pMachine->i_isSnapshotMachine());
+ m->bd->fImplicit = aImplicit;
+
+ /* Construct a short log name for this attachment. */
+ i_updateLogName();
+}
+
+const ComObjPtr<Medium>& MediumAttachment::i_getMedium() const
+{
+ return m->bd->pMedium;
+}
+
+const Utf8Str &MediumAttachment::i_getControllerName() const
+{
+ return m->bd->strControllerName;
+}
+
+LONG MediumAttachment::i_getPort() const
+{
+ return m->bd->mData.lPort;
+}
+
+LONG MediumAttachment::i_getDevice() const
+{
+ return m->bd->mData.lDevice;
+}
+
+DeviceType_T MediumAttachment::i_getType() const
+{
+ return m->bd->mData.deviceType;
+}
+
+bool MediumAttachment::i_getPassthrough() const
+{
+ AutoReadLock lock(this COMMA_LOCKVAL_SRC_POS);
+ return m->bd->mData.fPassThrough;
+}
+
+bool MediumAttachment::i_getTempEject() const
+{
+ AutoReadLock lock(this COMMA_LOCKVAL_SRC_POS);
+ return m->bd->mData.fTempEject;
+}
+
+bool MediumAttachment::i_getNonRotational() const
+{
+ AutoReadLock lock(this COMMA_LOCKVAL_SRC_POS);
+ return m->bd->mData.fNonRotational;
+}
+
+bool MediumAttachment::i_getDiscard() const
+{
+ AutoReadLock lock(this COMMA_LOCKVAL_SRC_POS);
+ return m->bd->mData.fDiscard;
+}
+
+bool MediumAttachment::i_getHotPluggable() const
+{
+ AutoReadLock lock(this COMMA_LOCKVAL_SRC_POS);
+ return m->bd->mData.fHotPluggable;
+}
+
+Utf8Str& MediumAttachment::i_getBandwidthGroup() const
+{
+ return m->bd->mData.strBwGroup;
+}
+
+bool MediumAttachment::i_matches(const Utf8Str &aControllerName, LONG aPort, LONG aDevice)
+{
+ return ( aControllerName == m->bd->strControllerName
+ && aPort == m->bd->mData.lPort
+ && aDevice == m->bd->mData.lDevice);
+}
+
+/** Must be called from under this object's write lock. */
+void MediumAttachment::i_updateName(const Utf8Str &aName)
+{
+ Assert(isWriteLockOnCurrentThread());
+ Assert(!m->pMachine->i_isSnapshotMachine());
+
+ m->bd.backup();
+ unconst(m->bd->strControllerName) = aName;
+
+ /* Construct a short log name for this attachment. */
+ i_updateLogName();
+}
+
+/**
+ * Sets the medium of this attachment and unsets the "implicit" flag.
+ * @param aMedium
+ */
+void MediumAttachment::i_updateMedium(const ComObjPtr<Medium> &aMedium)
+{
+ Assert(isWriteLockOnCurrentThread());
+ /* No assertion for a snapshot. Method used in deleting snapshot. */
+
+ m->bd.backup();
+ m->bd->pMedium = aMedium;
+ m->bd->fImplicit = false;
+ m->fIsEjected = false;
+}
+
+/** Must be called from under this object's write lock. */
+void MediumAttachment::i_updatePassthrough(bool aPassthrough)
+{
+ Assert(isWriteLockOnCurrentThread());
+ Assert(!m->pMachine->i_isSnapshotMachine());
+
+ m->bd.backup();
+ m->bd->mData.fPassThrough = aPassthrough;
+}
+
+/** Must be called from under this object's write lock. */
+void MediumAttachment::i_updateTempEject(bool aTempEject)
+{
+ Assert(isWriteLockOnCurrentThread());
+ Assert(!m->pMachine->i_isSnapshotMachine());
+
+ m->bd.backup();
+ m->bd->mData.fTempEject = aTempEject;
+}
+
+/** Must be called from under this object's write lock. */
+void MediumAttachment::i_updateEjected()
+{
+ Assert(isWriteLockOnCurrentThread());
+ Assert(!m->pMachine->i_isSnapshotMachine());
+
+ m->fIsEjected = true;
+}
+
+/** Must be called from under this object's write lock. */
+void MediumAttachment::i_updateNonRotational(bool aNonRotational)
+{
+ Assert(isWriteLockOnCurrentThread());
+ Assert(!m->pMachine->i_isSnapshotMachine());
+
+ m->bd.backup();
+ m->bd->mData.fNonRotational = aNonRotational;
+}
+
+/** Must be called from under this object's write lock. */
+void MediumAttachment::i_updateDiscard(bool aDiscard)
+{
+ Assert(isWriteLockOnCurrentThread());
+ Assert(!m->pMachine->i_isSnapshotMachine());
+
+ m->bd.backup();
+ m->bd->mData.fDiscard = aDiscard;
+}
+
+/** Must be called from under this object's write lock. */
+void MediumAttachment::i_updateHotPluggable(bool aHotPluggable)
+{
+ Assert(isWriteLockOnCurrentThread());
+ Assert(!m->pMachine->i_isSnapshotMachine());
+
+ m->bd.backup();
+ m->bd->mData.fHotPluggable = aHotPluggable;
+}
+
+void MediumAttachment::i_updateBandwidthGroup(const Utf8Str &aBandwidthGroup)
+{
+ LogFlowThisFuncEnter();
+ Assert(isWriteLockOnCurrentThread());
+ Assert(!m->pMachine->i_isSnapshotMachine());
+
+ m->bd.backup();
+ m->bd->mData.strBwGroup = aBandwidthGroup;
+
+ LogFlowThisFuncLeave();
+}
+
+void MediumAttachment::i_updateParentMachine(Machine * const pMachine)
+{
+ LogFlowThisFunc(("ENTER - %s\n", i_getLogName()));
+ /* sanity */
+ AutoCaller autoCaller(this);
+ AssertComRCReturnVoid(autoCaller.rc());
+ Assert(!m->pMachine->i_isSnapshotMachine());
+
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ unconst(m->pMachine) = pMachine;
+
+ LogFlowThisFunc(("LEAVE - %s\n", i_getLogName()));
+}
+
+void MediumAttachment::i_updateLogName()
+{
+ const char *pszName = m->bd->strControllerName.c_str();
+ const char *pszEndNick = strpbrk(pszName, " \t:-");
+ mLogName = Utf8StrFmt("MA%p[%.*s:%u:%u:%s%s]",
+ this,
+ pszEndNick ? pszEndNick - pszName : 4, pszName,
+ m->bd->mData.lPort, m->bd->mData.lDevice, ::stringifyDeviceType(m->bd->mData.deviceType),
+ m->bd->fImplicit ? ":I" : "");
+}