1121 lines
28 KiB
C++
1121 lines
28 KiB
C++
/* -*- 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 "storbios.hxx"
|
|
|
|
#include "sal/types.h"
|
|
#include "sal/macros.h"
|
|
|
|
#include "rtl/alloc.h"
|
|
#include "rtl/ref.hxx"
|
|
|
|
#include "osl/diagnose.h"
|
|
#include "osl/mutex.hxx"
|
|
|
|
#include "store/types.h"
|
|
#include "object.hxx"
|
|
#include "lockbyte.hxx"
|
|
#include "storcach.hxx"
|
|
|
|
using namespace store;
|
|
|
|
/*========================================================================
|
|
*
|
|
* OStoreSuperBlock.
|
|
*
|
|
*======================================================================*/
|
|
#define STORE_MAGIC_SUPERBLOCK sal_uInt32(0x484D5343)
|
|
|
|
struct OStoreSuperBlock
|
|
{
|
|
typedef OStorePageGuard G;
|
|
typedef OStorePageDescriptor D;
|
|
typedef OStorePageLink L;
|
|
|
|
/** Representation.
|
|
*/
|
|
G m_aGuard;
|
|
D m_aDescr;
|
|
sal_uInt32 m_nMarked;
|
|
L m_aMarked;
|
|
sal_uInt32 m_nUnused;
|
|
L m_aUnused;
|
|
|
|
/** theSize.
|
|
*/
|
|
static const size_t theSize = sizeof(G) + sizeof(D) + 2 * (sizeof(L) + sizeof(sal_uInt32));
|
|
|
|
/** Construction.
|
|
*/
|
|
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)
|
|
{}
|
|
|
|
OStoreSuperBlock (const OStoreSuperBlock & rhs)
|
|
: 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)
|
|
{}
|
|
|
|
OStoreSuperBlock& operator= (const OStoreSuperBlock & rhs)
|
|
{
|
|
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;
|
|
return *this;
|
|
}
|
|
|
|
/** Comparison.
|
|
*/
|
|
sal_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) );
|
|
}
|
|
|
|
/** unused(Count|Head|Insert|Remove|Reset).
|
|
*/
|
|
sal_uInt32 unusedCount (void) const
|
|
{
|
|
return store::ntohl(m_nUnused);
|
|
}
|
|
const L& unusedHead (void) 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 (void)
|
|
{
|
|
m_nUnused = store::htonl(0);
|
|
m_aUnused = L(0);
|
|
}
|
|
|
|
/** guard (external representation).
|
|
*/
|
|
void guard()
|
|
{
|
|
sal_uInt32 nCRC32 = 0;
|
|
nCRC32 = rtl_crc32 (nCRC32, &m_aGuard.m_nMagic, sizeof(sal_uInt32));
|
|
nCRC32 = rtl_crc32 (nCRC32, &m_aDescr, theSize - sizeof(G));
|
|
m_aGuard.m_nCRC32 = store::htonl(nCRC32);
|
|
}
|
|
|
|
/** verify (external representation).
|
|
*/
|
|
storeError verify() const
|
|
{
|
|
sal_uInt32 nMagic = store::ntohl(m_aGuard.m_nMagic);
|
|
if (nMagic != STORE_MAGIC_SUPERBLOCK)
|
|
return store_E_WrongFormat;
|
|
|
|
sal_uInt32 nCRC32 = 0;
|
|
nCRC32 = rtl_crc32 (nCRC32, &m_aGuard.m_nMagic, sizeof(sal_uInt32));
|
|
nCRC32 = rtl_crc32 (nCRC32, &m_aDescr, theSize - sizeof(G));
|
|
if (m_aGuard.m_nCRC32 != store::htonl(nCRC32))
|
|
return store_E_InvalidChecksum;
|
|
else
|
|
return store_E_None;
|
|
}
|
|
};
|
|
|
|
/*========================================================================
|
|
*
|
|
* SuperBlockPage interface.
|
|
*
|
|
*======================================================================*/
|
|
namespace store
|
|
{
|
|
|
|
struct SuperBlockPage
|
|
{
|
|
typedef OStoreSuperBlock SuperBlock;
|
|
|
|
/** Representation.
|
|
*/
|
|
SuperBlock m_aSuperOne;
|
|
SuperBlock m_aSuperTwo;
|
|
|
|
/** theSize.
|
|
*/
|
|
static const size_t theSize = 2 * SuperBlock::theSize;
|
|
static const sal_uInt16 thePageSize = theSize;
|
|
STORE_STATIC_ASSERT(STORE_MINIMUM_PAGESIZE >= thePageSize);
|
|
|
|
/** Allocation.
|
|
*/
|
|
static void * operator new (size_t n) SAL_THROW(())
|
|
{
|
|
return rtl_allocateMemory (sal::static_int_cast<sal_Size>(n));
|
|
}
|
|
static void operator delete (void * p) SAL_THROW(())
|
|
{
|
|
rtl_freeMemory (p);
|
|
}
|
|
|
|
static void * operator new (SAL_UNUSED_PARAMETER size_t, sal_uInt16 nPageSize) SAL_THROW(())
|
|
{
|
|
return rtl_allocateZeroMemory (sal::static_int_cast<sal_Size>(nPageSize));
|
|
}
|
|
static void operator delete (void * p, SAL_UNUSED_PARAMETER sal_uInt16) SAL_THROW(())
|
|
{
|
|
rtl_freeMemory (p);
|
|
}
|
|
|
|
/** Construction.
|
|
*/
|
|
explicit SuperBlockPage (sal_uInt16 nPageSize = thePageSize)
|
|
: m_aSuperOne(nPageSize),
|
|
m_aSuperTwo(nPageSize)
|
|
{}
|
|
|
|
/** save.
|
|
*/
|
|
storeError save (OStorePageBIOS & rBIOS, sal_uInt32 nSize = theSize)
|
|
{
|
|
m_aSuperOne.guard();
|
|
m_aSuperTwo = m_aSuperOne;
|
|
return rBIOS.write (0, this, nSize);
|
|
}
|
|
|
|
/** Page allocation.
|
|
*/
|
|
storeError unusedHead (
|
|
OStorePageBIOS & rBIOS,
|
|
PageData & rPageHead);
|
|
|
|
storeError unusedPop (
|
|
OStorePageBIOS & rBIOS,
|
|
PageData const & rPageHead);
|
|
|
|
storeError unusedPush (
|
|
OStorePageBIOS & rBIOS,
|
|
sal_uInt32 nAddr);
|
|
|
|
/** verify (with repair).
|
|
*/
|
|
storeError verify (OStorePageBIOS & rBIOS);
|
|
};
|
|
|
|
} // namespace store
|
|
|
|
/*========================================================================
|
|
*
|
|
* SuperBlockPage implementation.
|
|
*
|
|
*======================================================================*/
|
|
/*
|
|
* unusedHead(): get freelist head (alloc page, step 1).
|
|
*/
|
|
storeError SuperBlockPage::unusedHead (OStorePageBIOS & 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();
|
|
OSL_POSTCOND(nAddr != STORE_PAGE_NULL, "store::SuperBlock::unusedHead(): page not free");
|
|
if (nAddr == STORE_PAGE_NULL)
|
|
{
|
|
// Page in use.
|
|
rPageHead.location (STORE_PAGE_NULL);
|
|
|
|
// Recovery: Reset freelist to empty.
|
|
m_aSuperOne.unusedReset();
|
|
eErrCode = save (rBIOS);
|
|
}
|
|
return eErrCode;
|
|
}
|
|
|
|
/*
|
|
* unusedPop(): pop freelist head (alloc page, step 2).
|
|
*/
|
|
storeError SuperBlockPage::unusedPop (OStorePageBIOS & 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);
|
|
}
|
|
|
|
/*
|
|
* unusedPush(): push new freelist head.
|
|
*/
|
|
storeError SuperBlockPage::unusedPush (OStorePageBIOS & 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 & 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.
|
|
OSL_TRACE("OStoreSuperBlockPage::verify(): double failure.");
|
|
}
|
|
}
|
|
|
|
// Done.
|
|
return eErrCode;
|
|
}
|
|
|
|
/*========================================================================
|
|
*
|
|
* OStorePageBIOS::Ace implementation.
|
|
*
|
|
*======================================================================*/
|
|
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 * /* arg */)
|
|
{
|
|
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;
|
|
}
|
|
|
|
/*========================================================================
|
|
*
|
|
* OStorePageBIOS::AceCache interface.
|
|
*
|
|
*======================================================================*/
|
|
namespace store
|
|
{
|
|
|
|
class OStorePageBIOS::AceCache
|
|
{
|
|
rtl_cache_type * m_ace_cache;
|
|
|
|
public:
|
|
static AceCache & get();
|
|
|
|
OStorePageBIOS::Ace *
|
|
create (sal_uInt32 addr, sal_uInt32 used = 1);
|
|
|
|
void
|
|
destroy (OStorePageBIOS::Ace * ace);
|
|
|
|
protected:
|
|
AceCache();
|
|
~AceCache();
|
|
};
|
|
|
|
} // namespace store
|
|
|
|
/*========================================================================
|
|
*
|
|
* OStorePageBIOS::AceCache implementation.
|
|
*
|
|
*======================================================================*/
|
|
extern "C" typedef int (SAL_CALL * ace_constructor_type)(void*,void*);
|
|
|
|
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
|
|
reinterpret_cast<ace_constructor_type>( OStorePageBIOS::Ace::constructor),
|
|
0, // destructor,
|
|
0, // reclaim,
|
|
0, // userarg,
|
|
0, // default source,
|
|
0 // flags
|
|
);
|
|
}
|
|
|
|
OStorePageBIOS::AceCache::~AceCache()
|
|
{
|
|
rtl_cache_destroy (m_ace_cache), m_ace_cache = 0;
|
|
}
|
|
|
|
OStorePageBIOS::Ace *
|
|
OStorePageBIOS::AceCache::create (sal_uInt32 addr, sal_uInt32 used)
|
|
{
|
|
Ace * ace = static_cast<Ace*>(rtl_cache_alloc (m_ace_cache));
|
|
if (ace != 0)
|
|
{
|
|
// verify invariant state.
|
|
OSL_ASSERT((ace->m_next == ace) && (ace->m_prev == ace));
|
|
|
|
// initialize.
|
|
ace->m_addr = addr;
|
|
ace->m_used = used;
|
|
}
|
|
return ace;
|
|
}
|
|
|
|
void
|
|
OStorePageBIOS::AceCache::destroy (OStorePageBIOS::Ace * ace)
|
|
{
|
|
if (ace != 0)
|
|
{
|
|
// 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 implementation.
|
|
*
|
|
*======================================================================*/
|
|
/*
|
|
* OStorePageBIOS.
|
|
*/
|
|
OStorePageBIOS::OStorePageBIOS (void)
|
|
: m_xLockBytes (NULL),
|
|
m_pSuper (NULL),
|
|
m_bWriteable (false)
|
|
{
|
|
}
|
|
|
|
/*
|
|
* ~OStorePageBIOS.
|
|
*/
|
|
OStorePageBIOS::~OStorePageBIOS (void)
|
|
{
|
|
cleanup_Impl();
|
|
}
|
|
|
|
/*
|
|
* initialize.
|
|
* Precond: none.
|
|
*/
|
|
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.
|
|
* Internal: Precond: 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 != store_AccessReadOnly);
|
|
|
|
// Check access mode.
|
|
storeError eErrCode = store_E_None;
|
|
if (eAccessMode != store_AccessCreate)
|
|
{
|
|
// Load SuperBlock page.
|
|
if ((m_pSuper = new SuperBlockPage()) == 0)
|
|
return store_E_OutOfMemory;
|
|
|
|
eErrCode = read (0, m_pSuper, 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 == store_AccessReadOnly)
|
|
return store_E_NotExists;
|
|
if (eAccessMode == store_AccessReadWrite)
|
|
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).
|
|
if ((m_pSuper = new(rnPageSize) SuperBlockPage(rnPageSize)) == 0)
|
|
return store_E_OutOfMemory;
|
|
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;
|
|
}
|
|
|
|
/*
|
|
* cleanup_Impl.
|
|
* Internal: Precond: exclusive access.
|
|
*/
|
|
void OStorePageBIOS::cleanup_Impl()
|
|
{
|
|
// Check referer count.
|
|
if (m_ace_head.m_used > 0)
|
|
{
|
|
// Report remaining referer count.
|
|
OSL_TRACE("store::PageBIOS::cleanup_Impl(): referer count: %d", 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.
|
|
delete m_pSuper, m_pSuper = 0;
|
|
|
|
// Release PageCache.
|
|
m_xCache.clear();
|
|
|
|
// Release PageAllocator.
|
|
m_xAllocator.clear();
|
|
|
|
// Release LockBytes.
|
|
m_xLockBytes.clear();
|
|
}
|
|
|
|
/*
|
|
* read.
|
|
* Low Level: Precond: initialized, exclusive access.
|
|
*/
|
|
storeError OStorePageBIOS::read (
|
|
sal_uInt32 nAddr, void *pData, sal_uInt32 nSize)
|
|
{
|
|
// Check precond.
|
|
if (!m_xLockBytes.is())
|
|
return store_E_InvalidAccess;
|
|
|
|
// Read Data.
|
|
return m_xLockBytes->readAt (nAddr, pData, nSize);
|
|
}
|
|
|
|
/*
|
|
* write.
|
|
* Low Level: Precond: initialized, writeable, exclusive access.
|
|
*/
|
|
storeError OStorePageBIOS::write (
|
|
sal_uInt32 nAddr, const void *pData, sal_uInt32 nSize)
|
|
{
|
|
// 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);
|
|
}
|
|
|
|
/*
|
|
* acquirePage.
|
|
* Precond: 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 == store_AccessReadOnly)))
|
|
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 == store_AccessReadOnly)
|
|
ace->m_used += 1;
|
|
else
|
|
return store_E_AccessViolation;
|
|
}
|
|
else
|
|
{
|
|
// Insert new entry.
|
|
Ace * entry = AceCache::get().create (rDescr.m_nAddr, 1);
|
|
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;
|
|
}
|
|
|
|
/*
|
|
* releasePage.
|
|
* Precond: 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;
|
|
}
|
|
|
|
/*
|
|
* getRefererCount.
|
|
* Precond: none.
|
|
*/
|
|
sal_uInt32 OStorePageBIOS::getRefererCount (void)
|
|
{
|
|
// Acquire exclusive access.
|
|
osl::MutexGuard aGuard (m_aMutex);
|
|
|
|
// Obtain total referer count.
|
|
return m_ace_head.m_used;
|
|
}
|
|
|
|
/*
|
|
* allocate.
|
|
* Precond: initialized, writeable.
|
|
*/
|
|
storeError OStorePageBIOS::allocate (
|
|
OStorePageObject& rPage, Allocation eAlloc)
|
|
{
|
|
// 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;
|
|
if (eAlloc != ALLOCATE_EOF)
|
|
{
|
|
// 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);
|
|
}
|
|
|
|
/*
|
|
* free.
|
|
* Precond: 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);
|
|
}
|
|
|
|
/*
|
|
* loadObjectAt.
|
|
* Precond: 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);
|
|
}
|
|
|
|
/*
|
|
* loadObjectAt_Impl.
|
|
* Internal: Precond: initialized, readable, exclusive access.
|
|
*/
|
|
storeError OStorePageBIOS::loadObjectAt_Impl (OStorePageObject & rPage, sal_uInt32 nAddr)
|
|
{
|
|
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);
|
|
}
|
|
|
|
/*
|
|
* saveObjectAt.
|
|
* Precond: 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);
|
|
}
|
|
|
|
/*
|
|
* saveObjectAt_Impl.
|
|
* Internal: Precond: initialized, writeable, exclusive access.
|
|
*/
|
|
storeError OStorePageBIOS::saveObjectAt_Impl (OStorePageObject & rPage, sal_uInt32 nAddr)
|
|
{
|
|
// 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);
|
|
}
|
|
|
|
/*
|
|
* close.
|
|
* Precond: none.
|
|
*/
|
|
storeError OStorePageBIOS::close()
|
|
{
|
|
// Acquire exclusive access.
|
|
osl::MutexGuard aGuard (m_aMutex);
|
|
|
|
// Cleanup.
|
|
cleanup_Impl();
|
|
|
|
// Done.
|
|
return store_E_None;
|
|
}
|
|
|
|
/*
|
|
* flush.
|
|
* Precond: initialized.
|
|
*/
|
|
storeError OStorePageBIOS::flush (void)
|
|
{
|
|
// 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();
|
|
}
|
|
|
|
/*
|
|
* size.
|
|
* Precond: initialized.
|
|
*/
|
|
storeError OStorePageBIOS::size (sal_uInt32 &rnSize)
|
|
{
|
|
// Acquire exclusive access.
|
|
osl::MutexGuard aGuard (m_aMutex);
|
|
|
|
// Initialize [out] param.
|
|
rnSize = 0;
|
|
|
|
// Check precond.
|
|
if (!m_xLockBytes.is())
|
|
return store_E_InvalidAccess;
|
|
|
|
// Obtain LockBytes size.
|
|
return m_xLockBytes->getSize (rnSize);
|
|
}
|
|
|
|
/*
|
|
* scanBegin.
|
|
* Precond: initialized.
|
|
*/
|
|
storeError OStorePageBIOS::scanBegin (
|
|
ScanContext &rCtx, sal_uInt32 nMagic)
|
|
{
|
|
// Acquire exclusive access.
|
|
osl::MutexGuard aGuard (m_aMutex);
|
|
|
|
// Initialize [out] param.
|
|
rCtx.m_aDescr = OStorePageDescriptor(0, 0, 0);
|
|
rCtx.m_nSize = 0;
|
|
rCtx.m_nMagic = nMagic;
|
|
|
|
// Check precond.
|
|
if (!m_xLockBytes.is())
|
|
return store_E_InvalidAccess;
|
|
|
|
// Check SuperBlock page.
|
|
storeError eErrCode = m_pSuper->verify (*this);
|
|
if (eErrCode != store_E_None)
|
|
{
|
|
// Damaged. Determine page size (NYI).
|
|
OSL_TRACE ("OStorePageBIOS::scanBegin(): damaged.\n");
|
|
return eErrCode;
|
|
}
|
|
|
|
// Setup Context descriptor.
|
|
rCtx.m_aDescr = m_pSuper->m_aSuperOne.m_aDescr;
|
|
rCtx.m_aDescr.m_nSize = store::ntohs(rCtx.m_aDescr.m_nSize);
|
|
rCtx.m_aDescr.m_nAddr = rCtx.m_aDescr.m_nSize;
|
|
|
|
// Setup Context size.
|
|
eErrCode = size (rCtx.m_nSize);
|
|
if (eErrCode != store_E_None)
|
|
rCtx.m_nSize = ((sal_uInt32)(~0));
|
|
|
|
// Done.
|
|
return store_E_None;
|
|
}
|
|
|
|
/*
|
|
* scanNext.
|
|
* Precond: initialized.
|
|
*/
|
|
storeError OStorePageBIOS::scanNext (
|
|
ScanContext &rCtx, OStorePageObject &rPage)
|
|
{
|
|
// Acquire exclusive access.
|
|
osl::MutexGuard aGuard (m_aMutex);
|
|
|
|
// Check precond.
|
|
if (!m_xLockBytes.is())
|
|
return store_E_InvalidAccess;
|
|
|
|
// Setup PageHead.
|
|
PageData aPageHead;
|
|
|
|
// Check context.
|
|
while (rCtx.isValid())
|
|
{
|
|
// Assign next location.
|
|
sal_uInt32 nAddr = rCtx.m_aDescr.m_nAddr;
|
|
rCtx.m_aDescr.m_nAddr += rCtx.m_aDescr.m_nSize;
|
|
|
|
// Read PageHead.
|
|
storeError eErrCode = read (nAddr, &aPageHead, PageData::theSize);
|
|
if (eErrCode != store_E_None)
|
|
continue;
|
|
|
|
// Verify PageHead.
|
|
eErrCode = aPageHead.verify (nAddr);
|
|
if (eErrCode != store_E_None)
|
|
continue;
|
|
|
|
// Check PageHead Magic number.
|
|
if (aPageHead.m_aGuard.m_nMagic != rCtx.m_nMagic)
|
|
continue;
|
|
|
|
// Check PageHead Unused link.
|
|
if (aPageHead.m_aUnused.m_nAddr != STORE_PAGE_NULL)
|
|
continue;
|
|
|
|
// Load page.
|
|
eErrCode = loadObjectAt_Impl (rPage, nAddr);
|
|
if (eErrCode != store_E_None)
|
|
continue;
|
|
|
|
// Deliver page.
|
|
return store_E_None;
|
|
}
|
|
|
|
// Done.
|
|
return store_E_CantSeek;
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|