summaryrefslogtreecommitdiffstats
path: root/store/source/storbios.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'store/source/storbios.cxx')
-rw-r--r--store/source/storbios.cxx890
1 files changed, 890 insertions, 0 deletions
diff --git a/store/source/storbios.cxx b/store/source/storbios.cxx
new file mode 100644
index 0000000000..af74091632
--- /dev/null
+++ b/store/source/storbios.cxx
@@ -0,0 +1,890 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include "storbios.hxx"
+
+#include <sal/types.h>
+#include <sal/log.hxx>
+
+#include <rtl/alloc.h>
+#include <rtl/ref.hxx>
+
+#include <osl/diagnose.h>
+#include <osl/mutex.hxx>
+
+#include <store/types.h>
+#include "lockbyte.hxx"
+#include "storcach.hxx"
+
+using namespace store;
+
+constexpr sal_uInt32 STORE_MAGIC_SUPERBLOCK = 0x484D5343;
+
+namespace {
+
+struct OStoreSuperBlock
+{
+ typedef OStorePageGuard G;
+ typedef OStorePageDescriptor D;
+ typedef OStorePageLink L;
+
+ G m_aGuard;
+ D m_aDescr;
+ sal_uInt32 m_nMarked;
+ L m_aMarked;
+ sal_uInt32 m_nUnused;
+ L m_aUnused;
+
+ static const size_t theSize = sizeof(G) + sizeof(D) + 2 * (sizeof(L) + sizeof(sal_uInt32));
+
+ explicit OStoreSuperBlock (sal_uInt16 nPageSize)
+ : m_aGuard (STORE_MAGIC_SUPERBLOCK),
+ m_aDescr (nPageSize, nPageSize, STORE_MINIMUM_PAGESIZE),
+ m_nMarked (store::htonl(0)),
+ m_aMarked (0),
+ m_nUnused (store::htonl(0)),
+ m_aUnused (0)
+ {}
+
+ bool operator== (const OStoreSuperBlock & rhs) const
+ {
+ return ((m_aGuard == rhs.m_aGuard ) &&
+ (m_aDescr == rhs.m_aDescr ) &&
+ (m_nMarked == rhs.m_nMarked) &&
+ (m_aMarked == rhs.m_aMarked) &&
+ (m_nUnused == rhs.m_nUnused) &&
+ (m_aUnused == rhs.m_aUnused) );
+ }
+
+ sal_uInt32 unusedCount() const
+ {
+ return store::ntohl(m_nUnused);
+ }
+
+ const L& unusedHead() const
+ {
+ return m_aUnused;
+ }
+
+ void unusedInsert (const L& rLink)
+ {
+ sal_uInt32 nUnused = unusedCount();
+ m_nUnused = store::htonl(nUnused + 1);
+ m_aUnused = rLink;
+ }
+
+ void unusedRemove (const L& rLink)
+ {
+ sal_uInt32 nUnused = unusedCount();
+ m_nUnused = store::htonl(nUnused - 1);
+ m_aUnused = rLink;
+ }
+
+ void unusedReset()
+ {
+ m_nUnused = store::htonl(0);
+ m_aUnused = L(0);
+ }
+
+ void guard()
+ {
+ sal_uInt32 nCRC32 = rtl_crc32 (0, &m_aGuard.m_nMagic, sizeof(sal_uInt32));
+ nCRC32 = rtl_crc32 (nCRC32, &m_aDescr, static_cast<sal_uInt32>(theSize - sizeof(G)));
+ m_aGuard.m_nCRC32 = store::htonl(nCRC32);
+ }
+
+ storeError verify() const
+ {
+ sal_uInt32 nMagic = store::ntohl(m_aGuard.m_nMagic);
+ if (nMagic != STORE_MAGIC_SUPERBLOCK)
+ return store_E_WrongFormat;
+
+ sal_uInt32 nCRC32 = rtl_crc32 (0, &m_aGuard.m_nMagic, sizeof(sal_uInt32));
+ nCRC32 = rtl_crc32 (nCRC32, &m_aDescr, static_cast<sal_uInt32>(theSize - sizeof(G)));
+ if (m_aGuard.m_nCRC32 != store::htonl(nCRC32))
+ return store_E_InvalidChecksum;
+ else
+ return store_E_None;
+ }
+};
+
+}
+
+namespace store
+{
+
+struct SuperBlockPage
+{
+ typedef OStoreSuperBlock SuperBlock;
+
+ SuperBlock m_aSuperOne;
+ SuperBlock m_aSuperTwo;
+
+ static const size_t theSize = 2 * SuperBlock::theSize;
+ static const sal_uInt16 thePageSize = theSize;
+ static_assert(STORE_MINIMUM_PAGESIZE >= thePageSize, "must be at least thePageSize");
+
+ static void * operator new (size_t n)
+ {
+ return std::malloc(sal::static_int_cast<sal_Size>(n));
+ }
+
+ static void operator delete (void * p)
+ {
+ std::free (p);
+ }
+
+ static void * operator new (SAL_UNUSED_PARAMETER size_t, sal_uInt16 nPageSize)
+ {
+ return rtl_allocateZeroMemory (sal::static_int_cast<sal_Size>(nPageSize));
+ }
+
+ static void operator delete (void * p, SAL_UNUSED_PARAMETER sal_uInt16)
+ {
+ std::free (p);
+ }
+
+ explicit SuperBlockPage (sal_uInt16 nPageSize = thePageSize)
+ : m_aSuperOne(nPageSize),
+ m_aSuperTwo(nPageSize)
+ {}
+
+ storeError save (OStorePageBIOS const & rBIOS, sal_uInt32 nSize = theSize)
+ {
+ m_aSuperOne.guard();
+ m_aSuperTwo = m_aSuperOne;
+ return rBIOS.write (0, this, nSize);
+ }
+
+ /** Page allocation.
+ */
+ storeError unusedHead (
+ OStorePageBIOS const & rBIOS,
+ PageData & rPageHead);
+
+ storeError unusedPop (
+ OStorePageBIOS const & rBIOS,
+ PageData const & rPageHead);
+
+ storeError unusedPush (
+ OStorePageBIOS const & rBIOS,
+ sal_uInt32 nAddr);
+
+ storeError verify (OStorePageBIOS const & rBIOS);
+};
+
+} // namespace store
+
+/**
+ Get freelist head (alloc page, step 1).
+ */
+storeError SuperBlockPage::unusedHead (OStorePageBIOS const & rBIOS, PageData & rPageHead)
+{
+ storeError eErrCode = verify (rBIOS);
+ if (eErrCode != store_E_None)
+ return eErrCode;
+
+ // Check freelist head.
+ OStorePageLink const aListHead (m_aSuperOne.unusedHead());
+ if (aListHead.location() == 0)
+ {
+ // Freelist empty, see SuperBlock::ctor().
+ rPageHead.location (STORE_PAGE_NULL);
+ return store_E_None;
+ }
+
+ // Load PageHead.
+ eErrCode = rBIOS.read (aListHead.location(), &rPageHead, PageData::theSize);
+ if (eErrCode != store_E_None)
+ return eErrCode;
+
+ eErrCode = rPageHead.verify (aListHead.location());
+ if (eErrCode != store_E_None)
+ return eErrCode;
+
+ // Verify page is unused.
+ sal_uInt32 const nAddr = rPageHead.m_aUnused.location();
+ if (nAddr == STORE_PAGE_NULL)
+ {
+ SAL_WARN("store", "store::SuperBlock::unusedHead(): page not free");
+
+ // Page in use.
+ rPageHead.location (STORE_PAGE_NULL);
+
+ // Recovery: Reset freelist to empty.
+ m_aSuperOne.unusedReset();
+ eErrCode = save (rBIOS);
+ }
+ return eErrCode;
+}
+
+/**
+ Pop freelist head (alloc page, step 2).
+ */
+storeError SuperBlockPage::unusedPop (OStorePageBIOS const & rBIOS, PageData const & rPageHead)
+{
+ sal_uInt32 const nAddr = rPageHead.m_aUnused.location();
+ OSL_PRECOND(nAddr != STORE_PAGE_NULL, "store::SuperBlock::unusedPop(): page not free");
+ if (nAddr == STORE_PAGE_NULL)
+ return store_E_CantSeek;
+
+ // Pop from FreeList.
+ OStorePageLink const aListHead (nAddr);
+ m_aSuperOne.unusedRemove (aListHead);
+ return save (rBIOS);
+}
+
+/**
+ Push new freelist head.
+ */
+storeError SuperBlockPage::unusedPush (OStorePageBIOS const & rBIOS, sal_uInt32 nAddr)
+{
+ storeError eErrCode = verify (rBIOS);
+ if (eErrCode != store_E_None)
+ return eErrCode;
+
+ PageData aPageHead;
+ eErrCode = rBIOS.read (nAddr, &aPageHead, PageData::theSize);
+ if (eErrCode != store_E_None)
+ return eErrCode;
+
+ eErrCode = aPageHead.verify (nAddr);
+ if (eErrCode != store_E_None)
+ return eErrCode;
+
+ aPageHead.m_aUnused = m_aSuperOne.unusedHead();
+ aPageHead.guard (nAddr);
+
+ eErrCode = rBIOS.write (nAddr, &aPageHead, PageData::theSize);
+ if (eErrCode != store_E_None)
+ return eErrCode;
+
+ OStorePageLink const aListHead (nAddr);
+ m_aSuperOne.unusedInsert(aListHead);
+ return save (rBIOS);
+}
+
+/**
+ Verify (with repair).
+ */
+storeError SuperBlockPage::verify (OStorePageBIOS const & rBIOS)
+{
+ // Verify 1st copy.
+ storeError eErrCode = m_aSuperOne.verify();
+ if (eErrCode == store_E_None)
+ {
+ // Ok. Verify 2nd copy.
+ eErrCode = m_aSuperTwo.verify();
+ if (eErrCode == store_E_None)
+ {
+ // Ok. Ensure identical copies (1st copy wins).
+ if (!(m_aSuperOne == m_aSuperTwo))
+ {
+ // Different. Replace 2nd copy with 1st copy.
+ m_aSuperTwo = m_aSuperOne;
+
+ // Write back.
+ if (rBIOS.isWriteable())
+ eErrCode = rBIOS.write (0, this, theSize);
+ else
+ eErrCode = store_E_None;
+ }
+ }
+ else
+ {
+ // Failure. Replace 2nd copy with 1st copy.
+ m_aSuperTwo = m_aSuperOne;
+
+ // Write back.
+ if (rBIOS.isWriteable())
+ eErrCode = rBIOS.write (0, this, theSize);
+ else
+ eErrCode = store_E_None;
+ }
+ }
+ else
+ {
+ // Failure. Verify 2nd copy.
+ eErrCode = m_aSuperTwo.verify();
+ if (eErrCode == store_E_None)
+ {
+ // Ok. Replace 1st copy with 2nd copy.
+ m_aSuperOne = m_aSuperTwo;
+
+ // Write back.
+ if (rBIOS.isWriteable())
+ eErrCode = rBIOS.write (0, this, theSize);
+ else
+ eErrCode = store_E_None;
+ }
+ else
+ {
+ // Double Failure.
+ SAL_WARN("store", "OStoreSuperBlockPage::verify(): double failure.");
+ }
+ }
+
+ // Done.
+ return eErrCode;
+}
+
+OStorePageBIOS::Ace::Ace()
+ : m_next (this), m_prev (this), m_addr (STORE_PAGE_NULL), m_used (0)
+{}
+
+OStorePageBIOS::Ace::~Ace()
+{
+ m_next->m_prev = m_prev;
+ m_prev->m_next = m_next;
+}
+
+int
+SAL_CALL OStorePageBIOS::Ace::constructor (
+ void * obj, SAL_UNUSED_PARAMETER void*)
+{
+ Ace * ace = static_cast<Ace*>(obj);
+ ace->m_next = ace->m_prev = ace;
+ return 1;
+}
+
+OStorePageBIOS::Ace *
+OStorePageBIOS::Ace::find (OStorePageBIOS::Ace * head, sal_uInt32 addr)
+{
+ OStorePageBIOS::Ace * entry;
+ for (entry = head->m_next; entry != head; entry = entry->m_next)
+ {
+ if (entry->m_addr >= addr)
+ return entry;
+ }
+ return head;
+}
+
+void
+OStorePageBIOS::Ace::insert (OStorePageBIOS::Ace * head, OStorePageBIOS::Ace * entry)
+{
+ // insert entry at queue tail (before head).
+ entry->m_next = head;
+ entry->m_prev = head->m_prev;
+ head->m_prev = entry;
+ entry->m_prev->m_next = entry;
+}
+
+namespace store
+{
+
+class OStorePageBIOS::AceCache
+{
+ rtl_cache_type * m_ace_cache;
+
+public:
+ static AceCache & get();
+
+ OStorePageBIOS::Ace *
+ create (sal_uInt32 addr);
+
+ void
+ destroy (OStorePageBIOS::Ace * ace);
+
+protected:
+ AceCache();
+ ~AceCache();
+};
+
+} // namespace store
+
+OStorePageBIOS::AceCache &
+OStorePageBIOS::AceCache::get()
+{
+ static AceCache g_ace_cache;
+ return g_ace_cache;
+}
+
+OStorePageBIOS::AceCache::AceCache()
+{
+ m_ace_cache = rtl_cache_create (
+ "store_ace_cache",
+ sizeof (OStorePageBIOS::Ace),
+ 0, // objalign
+ OStorePageBIOS::Ace::constructor,
+ nullptr, // destructor,
+ nullptr, // reclaim,
+ nullptr, // userarg,
+ nullptr, // default source,
+ 0 // flags
+ );
+}
+
+OStorePageBIOS::AceCache::~AceCache()
+{
+ rtl_cache_destroy (m_ace_cache);
+ m_ace_cache = nullptr;
+}
+
+OStorePageBIOS::Ace *
+OStorePageBIOS::AceCache::create (sal_uInt32 addr)
+{
+ Ace * ace = static_cast<Ace*>(rtl_cache_alloc (m_ace_cache));
+ if (ace != nullptr)
+ {
+ // verify invariant state.
+ OSL_ASSERT((ace->m_next == ace) && (ace->m_prev == ace));
+
+ // initialize.
+ ace->m_addr = addr;
+ ace->m_used = 1;
+ }
+ return ace;
+}
+
+void
+OStorePageBIOS::AceCache::destroy (OStorePageBIOS::Ace * ace)
+{
+ if (ace != nullptr)
+ {
+ // remove from queue (if any).
+ ace->m_next->m_prev = ace->m_prev;
+ ace->m_prev->m_next = ace->m_next;
+
+ // restore invariant state.
+ ace->m_next = ace->m_prev = ace;
+
+ // return to cache.
+ rtl_cache_free (m_ace_cache, ace);
+ }
+}
+
+OStorePageBIOS::OStorePageBIOS()
+ : m_bWriteable (false)
+{
+}
+
+OStorePageBIOS::~OStorePageBIOS()
+{
+ cleanup_Impl();
+}
+
+storeError OStorePageBIOS::initialize (
+ ILockBytes * pLockBytes,
+ storeAccessMode eAccessMode,
+ sal_uInt16 & rnPageSize)
+{
+ // Acquire exclusive access.
+ osl::MutexGuard aGuard (m_aMutex);
+
+ // Initialize.
+ storeError eErrCode = initialize_Impl (pLockBytes, eAccessMode, rnPageSize);
+ if (eErrCode != store_E_None)
+ {
+ // Cleanup.
+ cleanup_Impl();
+ }
+ return eErrCode;
+}
+
+/**
+ initialize_Impl.
+ @pre exclusive access
+ */
+storeError OStorePageBIOS::initialize_Impl (
+ ILockBytes * pLockBytes,
+ storeAccessMode eAccessMode,
+ sal_uInt16 & rnPageSize)
+{
+ // Cleanup.
+ cleanup_Impl();
+
+ // Initialize.
+ m_xLockBytes = pLockBytes;
+ if (!m_xLockBytes.is())
+ return store_E_InvalidParameter;
+ m_bWriteable = (eAccessMode != storeAccessMode::ReadOnly);
+
+ // Check access mode.
+ storeError eErrCode = store_E_None;
+ if (eAccessMode != storeAccessMode::Create)
+ {
+ // Load SuperBlock page.
+ m_pSuper.reset(new SuperBlockPage());
+
+ eErrCode = read (0, m_pSuper.get(), SuperBlockPage::theSize);
+ if (eErrCode == store_E_None)
+ {
+ // Verify SuperBlock page (with repair).
+ eErrCode = m_pSuper->verify (*this);
+ }
+ }
+ else
+ {
+ // Truncate to zero length.
+ eErrCode = m_xLockBytes->setSize(0);
+ if (eErrCode != store_E_None)
+ return eErrCode;
+
+ // Mark as not existing.
+ eErrCode = store_E_NotExists;
+ }
+
+ if (eErrCode != store_E_None)
+ {
+ // Check reason.
+ if (eErrCode != store_E_NotExists)
+ return eErrCode;
+
+ // Check mode.
+ if (eAccessMode == storeAccessMode::ReadOnly)
+ return store_E_NotExists;
+ if (eAccessMode == storeAccessMode::ReadWrite)
+ return store_E_NotExists;
+
+ // Check PageSize.
+ if ((STORE_MINIMUM_PAGESIZE > rnPageSize) || (rnPageSize > STORE_MAXIMUM_PAGESIZE))
+ return store_E_InvalidParameter;
+ rnPageSize = ((rnPageSize + STORE_MINIMUM_PAGESIZE - 1) & ~(STORE_MINIMUM_PAGESIZE - 1));
+
+ // Create initial page (w/ SuperBlock).
+ m_pSuper.reset(new(rnPageSize) SuperBlockPage(rnPageSize));
+ eErrCode = m_pSuper->save (*this, rnPageSize);
+ }
+ if (eErrCode == store_E_None)
+ {
+ // Obtain page size.
+ rnPageSize = store::ntohs(m_pSuper->m_aSuperOne.m_aDescr.m_nSize);
+
+ // Create page allocator.
+ eErrCode = m_xLockBytes->initialize (m_xAllocator, rnPageSize);
+ if (eErrCode != store_E_None)
+ return eErrCode;
+
+ // Create page cache.
+ eErrCode = PageCache_createInstance (m_xCache, rnPageSize);
+ }
+ return eErrCode;
+}
+
+/*
+ @pre exclusive access.
+ */
+void OStorePageBIOS::cleanup_Impl()
+{
+ // Check referer count.
+ if (m_ace_head.m_used > 0)
+ {
+ // Report remaining referer count.
+ SAL_INFO("store", "referer count: " << m_ace_head.m_used);
+ for (Ace * ace = m_ace_head.m_next; ace != &m_ace_head; ace = m_ace_head.m_next)
+ {
+ m_ace_head.m_used -= ace->m_used;
+ AceCache::get().destroy (ace);
+ }
+ OSL_ENSURE(m_ace_head.m_used == 0, "store::PageBIOS::cleanup_Impl(): logic error");
+ }
+
+ // Release SuperBlock page.
+ m_pSuper.reset();
+
+ // Release PageCache.
+ m_xCache.clear();
+
+ // Release PageAllocator.
+ m_xAllocator.clear();
+
+ // Release LockBytes.
+ m_xLockBytes.clear();
+}
+
+/**
+ @pre initialized, exclusive access.
+ */
+storeError OStorePageBIOS::read (
+ sal_uInt32 nAddr, void *pData, sal_uInt32 nSize) const
+{
+ // Check precond.
+ if (!m_xLockBytes.is())
+ return store_E_InvalidAccess;
+
+ // Read Data.
+ return m_xLockBytes->readAt (nAddr, pData, nSize);
+}
+
+/**
+ @pre initialized, writeable, exclusive access.
+ */
+storeError OStorePageBIOS::write (
+ sal_uInt32 nAddr, const void *pData, sal_uInt32 nSize) const
+{
+ // Check precond.
+ if (!m_xLockBytes.is())
+ return store_E_InvalidAccess;
+ if (!m_bWriteable)
+ return store_E_AccessViolation;
+
+ // Write Data.
+ return m_xLockBytes->writeAt (nAddr, pData, nSize);
+}
+
+/**
+ @pre initialized.
+ */
+storeError OStorePageBIOS::acquirePage (
+ const OStorePageDescriptor& rDescr, storeAccessMode eMode)
+{
+ // Acquire exclusive access.
+ osl::MutexGuard aGuard (m_aMutex);
+
+ // Check precond.
+ if (!m_xLockBytes.is())
+ return store_E_InvalidAccess;
+
+ // Check access mode.
+ if (!(m_bWriteable || (eMode == storeAccessMode::ReadOnly)))
+ return store_E_AccessViolation;
+
+ // Find access control list entry.
+ Ace * ace = Ace::find (&m_ace_head, rDescr.m_nAddr);
+ if (ace->m_addr == rDescr.m_nAddr)
+ {
+ // Acquire existing entry (with ShareDenyWrite).
+ if (eMode == storeAccessMode::ReadOnly)
+ ace->m_used += 1;
+ else
+ return store_E_AccessViolation;
+ }
+ else
+ {
+ // Insert new entry.
+ Ace * entry = AceCache::get().create (rDescr.m_nAddr);
+ if (!entry)
+ return store_E_OutOfMemory;
+ Ace::insert (ace, entry);
+ }
+
+ // Increment total referer count and finish.
+ m_ace_head.m_used += 1;
+ return store_E_None;
+}
+
+/**
+ @pre initialized.
+ */
+storeError OStorePageBIOS::releasePage (const OStorePageDescriptor& rDescr)
+{
+ // Acquire exclusive access.
+ osl::MutexGuard aGuard (m_aMutex);
+
+ // Check precond.
+ if (!m_xLockBytes.is())
+ return store_E_InvalidAccess;
+
+ // Find access control list entry.
+ Ace * ace = Ace::find (&m_ace_head, rDescr.m_nAddr);
+ if (ace->m_addr != rDescr.m_nAddr)
+ return store_E_NotExists;
+
+ // Release existing entry.
+ if (ace->m_used > 1)
+ ace->m_used -= 1;
+ else
+ AceCache::get().destroy (ace);
+
+ // Decrement total referer count and finish.
+ m_ace_head.m_used -= 1;
+ return store_E_None;
+}
+
+/**
+ @pre initialized, writeable.
+ */
+storeError OStorePageBIOS::allocate (
+ OStorePageObject& rPage)
+{
+ // Acquire exclusive access.
+ osl::MutexGuard aGuard (m_aMutex);
+
+ // Check precond.
+ if (!m_xLockBytes.is())
+ return store_E_InvalidAccess;
+ if (!m_bWriteable)
+ return store_E_AccessViolation;
+
+ // Check allocation type.
+ storeError eErrCode = store_E_None;
+ // Try freelist head.
+ PageData aPageHead;
+ eErrCode = m_pSuper->unusedHead (*this, aPageHead);
+ if (eErrCode != store_E_None)
+ return eErrCode;
+
+ sal_uInt32 const nAddr = aPageHead.location();
+ if (nAddr != STORE_PAGE_NULL)
+ {
+ // Save page.
+ eErrCode = saveObjectAt_Impl (rPage, nAddr);
+ if (eErrCode != store_E_None)
+ return eErrCode;
+
+ // Pop freelist head and finish.
+ return m_pSuper->unusedPop (*this, aPageHead);
+ }
+
+ // Allocate from EOF. Determine current size.
+ sal_uInt32 nSize = STORE_PAGE_NULL;
+ eErrCode = m_xLockBytes->getSize (nSize);
+ if (eErrCode != store_E_None)
+ return eErrCode;
+
+ // Save page at current EOF.
+ return saveObjectAt_Impl (rPage, nSize);
+}
+
+/**
+ @pre initialized, writeable.
+ */
+storeError OStorePageBIOS::free (sal_uInt32 nAddr)
+{
+ // Acquire exclusive access.
+ osl::MutexGuard aGuard (m_aMutex);
+
+ // Check precond.
+ if (!m_xLockBytes.is())
+ return store_E_InvalidAccess;
+ if (!m_bWriteable)
+ return store_E_AccessViolation;
+
+ // Invalidate cache.
+ (void) m_xCache->removePageAt (nAddr);
+
+ // Push onto freelist.
+ return m_pSuper->unusedPush (*this, nAddr);
+}
+
+/**
+ @pre initialized, readable.
+ */
+storeError OStorePageBIOS::loadObjectAt (OStorePageObject & rPage, sal_uInt32 nAddr)
+{
+ // Acquire exclusive access.
+ osl::MutexGuard aGuard (m_aMutex);
+
+ // Check precond.
+ if (!m_xLockBytes.is())
+ return store_E_InvalidAccess;
+
+ return loadObjectAt_Impl (rPage, nAddr);
+}
+
+/**
+ @pre initialized, readable, exclusive access.
+ */
+storeError OStorePageBIOS::loadObjectAt_Impl (OStorePageObject & rPage, sal_uInt32 nAddr) const
+{
+ storeError eErrCode = m_xCache->lookupPageAt (rPage.get(), nAddr);
+ if (eErrCode != store_E_NotExists)
+ return eErrCode;
+
+ // Read page.
+ eErrCode = m_xLockBytes->readPageAt (rPage.get(), nAddr);
+ if (eErrCode != store_E_None)
+ return eErrCode;
+
+ // Verify page.
+ eErrCode = rPage.verify (nAddr);
+ if (eErrCode != store_E_None)
+ return eErrCode;
+
+ // Mark page as clean.
+ rPage.clean();
+
+ // Cache page.
+ return m_xCache->insertPageAt (rPage.get(), nAddr);
+}
+
+/**
+ @pre initialized, writeable.
+ */
+storeError OStorePageBIOS::saveObjectAt (OStorePageObject & rPage, sal_uInt32 nAddr)
+{
+ // Acquire exclusive access.
+ osl::MutexGuard aGuard (m_aMutex);
+
+ // Check precond.
+ if (!m_xLockBytes.is())
+ return store_E_InvalidAccess;
+ if (!m_bWriteable)
+ return store_E_AccessViolation;
+
+ // Save Page.
+ return saveObjectAt_Impl (rPage, nAddr);
+}
+
+/**
+ @pre initialized, writeable, exclusive access.
+ */
+storeError OStorePageBIOS::saveObjectAt_Impl (OStorePageObject & rPage, sal_uInt32 nAddr) const
+{
+ // Guard page (incl. set location).
+ storeError eErrCode = rPage.guard (nAddr);
+ if (eErrCode != store_E_None)
+ return eErrCode;
+
+ // Write page.
+ eErrCode = m_xLockBytes->writePageAt(rPage.get(), nAddr);
+ if (eErrCode != store_E_None)
+ return eErrCode;
+
+ // Mark page as clean.
+ rPage.clean();
+
+ // Cache page.
+ return m_xCache->updatePageAt (rPage.get(), nAddr);
+}
+
+/**
+ @pre none.
+ */
+storeError OStorePageBIOS::close()
+{
+ // Acquire exclusive access.
+ osl::MutexGuard aGuard (m_aMutex);
+
+ // Cleanup.
+ cleanup_Impl();
+
+ // Done.
+ return store_E_None;
+}
+
+/**
+ @pre initialized.
+ */
+storeError OStorePageBIOS::flush()
+{
+ // Acquire exclusive access.
+ osl::MutexGuard aGuard (m_aMutex);
+
+ // Check precond.
+ if (!m_xLockBytes.is())
+ return store_E_InvalidAccess;
+
+ // Flush LockBytes and finish.
+ return m_xLockBytes->flush();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */