/** @file * * VirtualBox COM class implementation */ /* * Copyright (C) 2006-2009 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; * you can redistribute it and/or modify it under the terms of the GNU * General Public License (GPL) as published by the Free Software * Foundation, in version 2 as it comes in the "COPYING" file of the * VirtualBox OSE distribution. VirtualBox OSE is distributed in the * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. */ #include "MediumAttachmentImpl.h" #include "MachineImpl.h" #include "MediumImpl.h" #include "Global.h" #include "AutoCaller.h" #include "Logging.h" #include //////////////////////////////////////////////////////////////////////////////// // // private member data definition // //////////////////////////////////////////////////////////////////////////////// struct BackupableMediumAttachmentData { BackupableMediumAttachmentData() : lPort(0), lDevice(0), type(DeviceType_Null), fPassthrough(false), fImplicit(false), mBandwidthLimit(0) { } ComObjPtr 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. */ const Bstr bstrControllerName; const LONG lPort; const LONG lDevice; const DeviceType_T type; bool fPassthrough; bool fImplicit; ULONG mBandwidthLimit; }; struct MediumAttachment::Data { Data() : pMachine(NULL) { } /** Reference to Machine object, for checking mutable state. */ Machine * const pMachine; /* later: const ComObjPtr mPeer; */ Backupable bd; }; // constructor / destructor ///////////////////////////////////////////////////////////////////////////// HRESULT MediumAttachment::FinalConstruct() { LogFlowThisFunc(("\n")); return S_OK; } void MediumAttachment::FinalRelease() { LogFlowThisFuncEnter(); uninit(); LogFlowThisFuncLeave(); } // public initializer/uninitializer for internal purposes only ///////////////////////////////////////////////////////////////////////////// /** * Initializes the medium attachment object. * * @param aParent Machine object. * @param aMedium Medium object. * @param aController Controller the hard disk is attached to. * @param aPort Port number. * @param aDevice Device number on the port. * @param aPassthrough Whether accesses are directly passed to the host drive. * @param aBandwidthLimit Bandwidth limit in Mbps */ HRESULT MediumAttachment::init(Machine *aParent, Medium *aMedium, const Bstr &aControllerName, LONG aPort, LONG aDevice, DeviceType_T aType, bool aPassthrough, ULONG aBandwidthLimit) { LogFlowThisFuncEnter(); LogFlowThisFunc(("aParent=%p aMedium=%p aControllerName=%ls aPort=%d aDevice=%d aType=%d aPassthrough=%d\n", aParent, aMedium, aControllerName.raw(), aPort, aDevice, aType, aPassthrough)); 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; unconst(m->bd->bstrControllerName) = aControllerName; unconst(m->bd->lPort) = aPort; unconst(m->bd->lDevice) = aDevice; unconst(m->bd->type) = aType; m->bd->fPassthrough = aPassthrough; /* Newly created attachments never have an implicitly created medium * associated with them. Implicit diff image creation happens later. */ m->bd->fImplicit = false; m->bd->mBandwidthLimit = aBandwidthLimit; /* Confirm a successful initialization when it's the case */ autoInitSpan.setSucceeded(); /* Construct a short log name for this attachment. */ Utf8Str ctlName(aControllerName); const char *psz = strpbrk(ctlName.c_str(), " \t:-"); mLogName = Utf8StrFmt("MA%p[%.*s:%u:%u:%s%s]", this, psz ? psz - ctlName.c_str() : 4, ctlName.c_str(), aPort, aDevice, Global::stringifyDeviceType(aType), m->bd->fImplicit ? ":I" : ""); LogFlowThisFunc(("LEAVE - %s\n", getLogName())); return S_OK; } /** * Uninitializes the instance. * Called from FinalRelease(). */ void MediumAttachment::uninit() { LogFlowThisFunc(("ENTER - %s\n", 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 ///////////////////////////////////////////////////////////////////////////// STDMETHODIMP MediumAttachment::COMGETTER(Medium)(IMedium **aHardDisk) { LogFlowThisFuncEnter(); CheckComArgOutPointerValid(aHardDisk); AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); m->bd->pMedium.queryInterfaceTo(aHardDisk); LogFlowThisFuncLeave(); return S_OK; } STDMETHODIMP MediumAttachment::COMGETTER(Controller)(BSTR *aController) { LogFlowThisFuncEnter(); CheckComArgOutPointerValid(aController); AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); /* m->controller is constant during life time, no need to lock */ m->bd->bstrControllerName.cloneTo(aController); LogFlowThisFuncLeave(); return S_OK; } STDMETHODIMP MediumAttachment::COMGETTER(Port)(LONG *aPort) { LogFlowThisFuncEnter(); CheckComArgOutPointerValid(aPort); AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); /* m->bd->port is constant during life time, no need to lock */ *aPort = m->bd->lPort; LogFlowThisFuncLeave(); return S_OK; } STDMETHODIMP MediumAttachment::COMGETTER(Device)(LONG *aDevice) { LogFlowThisFuncEnter(); CheckComArgOutPointerValid(aDevice); AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); /* m->bd->device is constant during life time, no need to lock */ *aDevice = m->bd->lDevice; LogFlowThisFuncLeave(); return S_OK; } STDMETHODIMP MediumAttachment::COMGETTER(Type)(DeviceType_T *aType) { LogFlowThisFuncEnter(); CheckComArgOutPointerValid(aType); AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); /* m->bd->type is constant during life time, no need to lock */ *aType = m->bd->type; LogFlowThisFuncLeave(); return S_OK; } STDMETHODIMP MediumAttachment::COMGETTER(Passthrough)(BOOL *aPassthrough) { LogFlowThisFuncEnter(); CheckComArgOutPointerValid(aPassthrough); AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); AutoReadLock lock(this COMMA_LOCKVAL_SRC_POS); *aPassthrough = m->bd->fPassthrough; LogFlowThisFuncLeave(); return S_OK; } STDMETHODIMP MediumAttachment::COMGETTER(BandwidthLimit) (ULONG *aLimit) { CheckComArgOutPointerValid(aLimit); AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); *aLimit = m->bd->mBandwidthLimit; return S_OK; } STDMETHODIMP MediumAttachment::COMSETTER(BandwidthLimit) (ULONG aLimit) { AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); /* the machine doesn't need to be mutable */ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); if (aLimit != m->bd->mBandwidthLimit) { m->bd.backup(); m->bd->mBandwidthLimit = aLimit; /* todo: not all storage attachments will support this. */ } return S_OK; } /** * @note Locks this object for writing. */ void MediumAttachment::rollback() { LogFlowThisFunc(("ENTER - %s\n", getLogName())); /* sanity */ AutoCaller autoCaller(this); AssertComRCReturnVoid(autoCaller.rc()); AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); m->bd.rollback(); LogFlowThisFunc(("LEAVE - %s\n", getLogName())); } /** * @note Locks this object for writing. */ void MediumAttachment::commit() { LogFlowThisFuncEnter(); /* sanity */ AutoCaller autoCaller(this); AssertComRCReturnVoid (autoCaller.rc()); AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); if (m->bd.isBackedUp()) m->bd.commit(); LogFlowThisFuncLeave(); } bool MediumAttachment::isImplicit() const { return m->bd->fImplicit; } void MediumAttachment::setImplicit(bool aImplicit) { m->bd->fImplicit = aImplicit; } const ComObjPtr& MediumAttachment::getMedium() const { return m->bd->pMedium; } Bstr MediumAttachment::getControllerName() const { return m->bd->bstrControllerName; } LONG MediumAttachment::getPort() const { return m->bd->lPort; } LONG MediumAttachment::getDevice() const { return m->bd->lDevice; } DeviceType_T MediumAttachment::getType() const { return m->bd->type; } bool MediumAttachment::getPassthrough() const { AutoReadLock lock(this COMMA_LOCKVAL_SRC_POS); return m->bd->fPassthrough; } bool MediumAttachment::matches(CBSTR aControllerName, LONG aPort, LONG aDevice) { return ( aControllerName == m->bd->bstrControllerName && aPort == m->bd->lPort && aDevice == m->bd->lDevice); } /** * Sets the medium of this attachment and unsets the "implicit" flag. * @param aMedium */ void MediumAttachment::updateMedium(const ComObjPtr &aMedium) { Assert(isWriteLockOnCurrentThread()); m->bd.backup(); m->bd->pMedium = aMedium; m->bd->fImplicit = false; } /** Must be called from under this object's write lock. */ void MediumAttachment::updatePassthrough(bool aPassthrough) { Assert(isWriteLockOnCurrentThread()); m->bd.backup(); m->bd->fPassthrough = aPassthrough; }