/* $Id: MediumLock.cpp $ */ /** @file * * Medium lock management helper classes */ /* * Copyright (C) 2010-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 . * * SPDX-License-Identifier: GPL-3.0-only */ #include "MediumLock.h" #include "MediumImpl.h" #include "MediumAttachmentImpl.h" MediumLock::MediumLock() : mMedium(NULL), mMediumCaller(NULL), mLockWrite(false), mIsLocked(false), mLockSkipped(false) { } MediumLock::~MediumLock() { // destroying medium locks is routinely done as part of error handling // and it's not expected to lose error info ErrorInfoKeeper eik; Unlock(); } MediumLock::MediumLock(const MediumLock &aMediumLock) : mMedium(aMediumLock.mMedium), mMediumCaller(NULL), mLockWrite(aMediumLock.mLockWrite), mIsLocked(false), mLockSkipped(false) { } MediumLock::MediumLock(const ComObjPtr &aMedium, bool aLockWrite) : mMedium(aMedium), mMediumCaller(NULL), mLockWrite(aLockWrite), mIsLocked(false), mLockSkipped(false) { } HRESULT MediumLock::UpdateLock(bool aLockWrite) { bool fPrevLockWrite = mLockWrite; if (mIsLocked) { Unlock(); mLockWrite = aLockWrite; HRESULT rc = Lock(); if (FAILED(rc)) { mLockWrite = fPrevLockWrite; Lock(); return rc; } return S_OK; } mLockWrite = aLockWrite; return S_OK; } const ComObjPtr &MediumLock::GetMedium() const { return mMedium; } bool MediumLock::GetLockRequest() const { return mLockWrite; } bool MediumLock::IsLocked() const { return mIsLocked; } HRESULT MediumLock::Lock(bool aIgnoreLockedMedia) { if (mIsLocked) return S_OK; mMediumCaller.attach(mMedium); if (FAILED(mMediumCaller.rc())) { mMediumCaller.attach(NULL); return VBOX_E_INVALID_OBJECT_STATE; } HRESULT rc = S_OK; MediumState_T state; { AutoReadLock alock(mMedium COMMA_LOCKVAL_SRC_POS); state = mMedium->i_getState(); } switch (state) { case MediumState_NotCreated: case MediumState_Creating: case MediumState_Deleting: mLockSkipped = true; break; default: if (mLockWrite) { if (aIgnoreLockedMedia && ( state == MediumState_LockedRead || state == MediumState_LockedWrite)) return S_OK; else rc = mMedium->LockWrite(mToken.asOutParam()); } else { if (aIgnoreLockedMedia && state == MediumState_LockedWrite) return S_OK; else rc = mMedium->LockRead(mToken.asOutParam()); } } if (SUCCEEDED(rc)) { mIsLocked = true; return S_OK; } else { mMediumCaller.attach(NULL); return VBOX_E_INVALID_OBJECT_STATE; } } HRESULT MediumLock::Unlock() { HRESULT rc = S_OK; if (mIsLocked && !mLockSkipped && mToken) { mToken->Abandon(); mToken.setNull(); } mMediumCaller.attach(NULL); mLockSkipped = false; mIsLocked = false; return rc; } MediumLockList::MediumLockList() { mIsLocked = false; } MediumLockList::~MediumLockList() { // destroying medium lock lists is routinely done as part of error handling // and it's not expected to lose error info ErrorInfoKeeper eik; Clear(); // rest is done by the list object's destructor } bool MediumLockList::IsEmpty() { return mMediumLocks.empty(); } HRESULT MediumLockList::Append(const ComObjPtr &aMedium, bool aLockWrite) { if (mIsLocked) return VBOX_E_INVALID_OBJECT_STATE; mMediumLocks.push_back(MediumLock(aMedium, aLockWrite)); return S_OK; } HRESULT MediumLockList::Prepend(const ComObjPtr &aMedium, bool aLockWrite) { if (mIsLocked) return VBOX_E_INVALID_OBJECT_STATE; mMediumLocks.push_front(MediumLock(aMedium, aLockWrite)); return S_OK; } HRESULT MediumLockList::Update(const ComObjPtr &aMedium, bool aLockWrite) { for (MediumLockList::Base::iterator it = mMediumLocks.begin(); it != mMediumLocks.end(); ++it) { if (it->GetMedium() == aMedium) return it->UpdateLock(aLockWrite); } return VBOX_E_INVALID_OBJECT_STATE; } HRESULT MediumLockList::RemoveByIterator(Base::iterator &aIt) { HRESULT rc = aIt->Unlock(); aIt = mMediumLocks.erase(aIt); return rc; } HRESULT MediumLockList::Clear() { HRESULT rc = Unlock(); mMediumLocks.clear(); return rc; } MediumLockList::Base::iterator MediumLockList::GetBegin() { return mMediumLocks.begin(); } MediumLockList::Base::iterator MediumLockList::GetEnd() { return mMediumLocks.end(); } HRESULT MediumLockList::Lock(bool fSkipOverLockedMedia /* = false */) { if (mIsLocked) return S_OK; HRESULT rc = S_OK; for (MediumLockList::Base::iterator it = mMediumLocks.begin(); it != mMediumLocks.end(); ++it) { rc = it->Lock(fSkipOverLockedMedia); if (FAILED(rc)) { for (MediumLockList::Base::iterator it2 = mMediumLocks.begin(); it2 != it; ++it2) { HRESULT rc2 = it2->Unlock(); AssertComRC(rc2); } break; } } if (SUCCEEDED(rc)) mIsLocked = true; return rc; } HRESULT MediumLockList::Unlock() { if (!mIsLocked) return S_OK; HRESULT rc = S_OK; for (MediumLockList::Base::iterator it = mMediumLocks.begin(); it != mMediumLocks.end(); ++it) { HRESULT rc2 = it->Unlock(); if (SUCCEEDED(rc) && FAILED(rc2)) rc = rc2; } mIsLocked = false; return rc; } MediumLockListMap::MediumLockListMap() { mIsLocked = false; } MediumLockListMap::~MediumLockListMap() { // destroying medium lock list maps is routinely done as part of // error handling and it's not expected to lose error info ErrorInfoKeeper eik; Clear(); // rest is done by the map object's destructor } bool MediumLockListMap::IsEmpty() { return mMediumLocks.empty(); } HRESULT MediumLockListMap::Insert(const ComObjPtr &aMediumAttachment, MediumLockList *aMediumLockList) { if (mIsLocked) return VBOX_E_INVALID_OBJECT_STATE; mMediumLocks[aMediumAttachment] = aMediumLockList; return S_OK; } HRESULT MediumLockListMap::ReplaceKey(const ComObjPtr &aMediumAttachmentOld, const ComObjPtr &aMediumAttachmentNew) { MediumLockListMap::Base::iterator it = mMediumLocks.find(aMediumAttachmentOld); if (it == mMediumLocks.end()) return VBOX_E_INVALID_OBJECT_STATE; MediumLockList *pMediumLockList = it->second; mMediumLocks.erase(it); mMediumLocks[aMediumAttachmentNew] = pMediumLockList; return S_OK; } HRESULT MediumLockListMap::Remove(const ComObjPtr &aMediumAttachment) { MediumLockListMap::Base::iterator it = mMediumLocks.find(aMediumAttachment); if (it == mMediumLocks.end()) return VBOX_E_INVALID_OBJECT_STATE; MediumLockList *pMediumLockList = it->second; mMediumLocks.erase(it); delete pMediumLockList; return S_OK; } HRESULT MediumLockListMap::Clear() { HRESULT rc = Unlock(); for (MediumLockListMap::Base::iterator it = mMediumLocks.begin(); it != mMediumLocks.end(); ++it) { MediumLockList *pMediumLockList = it->second; delete pMediumLockList; } mMediumLocks.clear(); return rc; } HRESULT MediumLockListMap::Get(const ComObjPtr &aMediumAttachment, MediumLockList * &aMediumLockList) { MediumLockListMap::Base::iterator it = mMediumLocks.find(aMediumAttachment); if (it == mMediumLocks.end()) { aMediumLockList = NULL; return VBOX_E_INVALID_OBJECT_STATE; } aMediumLockList = it->second; return S_OK; } HRESULT MediumLockListMap::Lock() { if (mIsLocked) return S_OK; HRESULT rc = S_OK; for (MediumLockListMap::Base::const_iterator it = mMediumLocks.begin(); it != mMediumLocks.end(); ++it) { rc = it->second->Lock(); if (FAILED(rc)) { for (MediumLockListMap::Base::const_iterator it2 = mMediumLocks.begin(); it2 != it; ++it2) { HRESULT rc2 = it2->second->Unlock(); AssertComRC(rc2); } break; } } if (SUCCEEDED(rc)) mIsLocked = true; return rc; } HRESULT MediumLockListMap::Unlock() { if (!mIsLocked) return S_OK; HRESULT rc = S_OK; for (MediumLockListMap::Base::const_iterator it = mMediumLocks.begin(); it != mMediumLocks.end(); ++it) { MediumLockList *pMediumLockList = it->second; HRESULT rc2 = pMediumLockList->Unlock(); if (SUCCEEDED(rc) && FAILED(rc2)) rc = rc2; } mIsLocked = false; return rc; }