e58324aaca
Change-Id: I67705cdff0440487019a2992571147d648be1bfc
514 lines
14 KiB
C++
514 lines
14 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 "storpage.hxx"
|
|
|
|
#include "sal/types.h"
|
|
#include "sal/log.hxx"
|
|
#include "rtl/string.h"
|
|
#include "rtl/ref.hxx"
|
|
#include "osl/diagnose.h"
|
|
#include "osl/mutex.hxx"
|
|
|
|
#include "store/types.h"
|
|
|
|
#include "object.hxx"
|
|
#include "lockbyte.hxx"
|
|
|
|
#include "storbase.hxx"
|
|
#include "stordata.hxx"
|
|
#include "stortree.hxx"
|
|
|
|
using namespace store;
|
|
|
|
/*========================================================================
|
|
*
|
|
* OStorePageManager implementation.
|
|
*
|
|
*======================================================================*/
|
|
const sal_uInt32 OStorePageManager::m_nTypeId = sal_uInt32(0x62190120);
|
|
|
|
/*
|
|
* OStorePageManager.
|
|
*/
|
|
OStorePageManager::OStorePageManager()
|
|
{
|
|
}
|
|
|
|
/*
|
|
* ~OStorePageManager.
|
|
*/
|
|
OStorePageManager::~OStorePageManager()
|
|
{
|
|
}
|
|
|
|
/*
|
|
* isKindOf.
|
|
*/
|
|
bool OStorePageManager::isKindOf (sal_uInt32 nTypeId)
|
|
{
|
|
return (nTypeId == m_nTypeId);
|
|
}
|
|
|
|
/*
|
|
* initialize (two-phase construction).
|
|
* Precond: none.
|
|
*/
|
|
storeError OStorePageManager::initialize (
|
|
ILockBytes * pLockBytes,
|
|
storeAccessMode eAccessMode,
|
|
sal_uInt16 & rnPageSize)
|
|
{
|
|
// Acquire exclusive access.
|
|
osl::MutexGuard aGuard(*this);
|
|
|
|
// Check arguments.
|
|
if (!pLockBytes)
|
|
return store_E_InvalidParameter;
|
|
|
|
// Initialize base.
|
|
storeError eErrCode = base::initialize (pLockBytes, eAccessMode, rnPageSize);
|
|
if (eErrCode != store_E_None)
|
|
return eErrCode;
|
|
|
|
// Check for (not) writeable.
|
|
if (!base::isWriteable())
|
|
{
|
|
// Readonly. Load RootNode.
|
|
return base::loadObjectAt (m_aRoot, rnPageSize);
|
|
}
|
|
|
|
// Writeable. Load or Create RootNode.
|
|
eErrCode = m_aRoot.loadOrCreate (rnPageSize, *this);
|
|
if (eErrCode == store_E_Pending)
|
|
{
|
|
// Creation notification.
|
|
PageHolderObject< page > xRoot (m_aRoot.get());
|
|
|
|
// Pre-allocate left most entry (ugly, but we can't insert to left).
|
|
OStorePageKey aKey (rtl_crc32 (0, "/", 1), 0);
|
|
xRoot->insert (0, entry(aKey));
|
|
|
|
// Save RootNode.
|
|
eErrCode = base::saveObjectAt (m_aRoot, rnPageSize);
|
|
}
|
|
|
|
// Done.
|
|
return eErrCode;
|
|
}
|
|
|
|
/*
|
|
* find_lookup (w/o split()).
|
|
* Internal: Precond: initialized, readable, exclusive access.
|
|
*/
|
|
storeError OStorePageManager::find_lookup (
|
|
OStoreBTreeNodeObject & rNode,
|
|
sal_uInt16 & rIndex,
|
|
OStorePageKey const & rKey)
|
|
{
|
|
// Find Node and Index.
|
|
storeError eErrCode = m_aRoot.find_lookup (rNode, rIndex, rKey, *this);
|
|
if (eErrCode != store_E_None)
|
|
return eErrCode;
|
|
|
|
// Greater or Equal.
|
|
PageHolderObject< page > xPage (rNode.get());
|
|
SAL_WARN_IF(rIndex >= xPage->usageCount(), "store", "store::PageManager::find_lookup(): logic error");
|
|
entry e (xPage->m_pData[rIndex]);
|
|
|
|
// Check for exact match.
|
|
if (e.compare(entry(rKey)) != entry::COMPARE_EQUAL)
|
|
{
|
|
// Page not present.
|
|
return store_E_NotExists;
|
|
}
|
|
|
|
// Check address.
|
|
if (e.m_aLink.location() == STORE_PAGE_NULL)
|
|
{
|
|
// Page not present.
|
|
return store_E_NotExists;
|
|
}
|
|
|
|
return store_E_None;
|
|
}
|
|
|
|
/*
|
|
* remove_Impl (possibly down from root).
|
|
* Internal: Precond: initialized, writeable, exclusive access.
|
|
*/
|
|
|
|
storeError OStorePageManager::remove_Impl (entry & rEntry)
|
|
{
|
|
OStoreBTreeNodeObject aNode (m_aRoot.get());
|
|
|
|
// Check current page index.
|
|
PageHolderObject< page > xPage (aNode.get());
|
|
sal_uInt16 i = xPage->find (rEntry), n = xPage->usageCount();
|
|
if (!(i < n))
|
|
{
|
|
// Path to entry not exists (Must not happen(?)).
|
|
return store_E_NotExists;
|
|
}
|
|
|
|
// Compare entry.
|
|
entry::CompareResult result = rEntry.compare (xPage->m_pData[i]);
|
|
|
|
// Iterate down until equal match.
|
|
while ((result == entry::COMPARE_GREATER) && (xPage->depth() > 0))
|
|
{
|
|
// Check link address.
|
|
sal_uInt32 const nAddr = xPage->m_pData[i].m_aLink.location();
|
|
if (nAddr == STORE_PAGE_NULL)
|
|
{
|
|
// Path to entry not exists (Must not happen(?)).
|
|
return store_E_NotExists;
|
|
}
|
|
|
|
// Load link page.
|
|
storeError eErrCode = loadObjectAt (aNode, nAddr);
|
|
if (eErrCode != store_E_None)
|
|
return eErrCode;
|
|
|
|
PageHolderObject< page > xNext (aNode.get());
|
|
xNext.swap (xPage);
|
|
|
|
// Check index.
|
|
i = xPage->find (rEntry);
|
|
n = xPage->usageCount();
|
|
if (!(i < n))
|
|
{
|
|
// Path to entry not exists (Must not happen(?)).
|
|
return store_E_NotExists;
|
|
}
|
|
|
|
// Compare entry.
|
|
result = rEntry.compare (xPage->m_pData[i]);
|
|
}
|
|
|
|
SAL_WARN_IF(
|
|
result == entry::COMPARE_LESS,
|
|
"store",
|
|
"OStorePageManager::remove(): find failed");
|
|
|
|
// Check entry comparison.
|
|
if (result == entry::COMPARE_LESS)
|
|
{
|
|
// Must not happen.
|
|
return store_E_Unknown;
|
|
}
|
|
|
|
// Remove down from current page (recursive).
|
|
return aNode.remove (i, rEntry, *this);
|
|
}
|
|
|
|
/*
|
|
* namei.
|
|
* Precond: none (static).
|
|
*/
|
|
storeError OStorePageManager::namei (
|
|
const rtl_String *pPath, const rtl_String *pName, OStorePageKey &rKey)
|
|
{
|
|
// Check parameter.
|
|
if (!(pPath && pName))
|
|
return store_E_InvalidParameter;
|
|
|
|
// Check name length.
|
|
if (!(pName->length < STORE_MAXIMUM_NAMESIZE))
|
|
return store_E_NameTooLong;
|
|
|
|
// Transform pathname into key.
|
|
rKey.m_nLow = store::htonl(rtl_crc32 (0, pName->buffer, pName->length));
|
|
rKey.m_nHigh = store::htonl(rtl_crc32 (0, pPath->buffer, pPath->length));
|
|
|
|
// Done.
|
|
return store_E_None;
|
|
}
|
|
|
|
/*
|
|
* iget.
|
|
* Precond: initialized.
|
|
*/
|
|
storeError OStorePageManager::iget (
|
|
OStoreDirectoryPageObject & rPage,
|
|
sal_uInt32 nAttrib,
|
|
const rtl_String * pPath,
|
|
const rtl_String * pName,
|
|
storeAccessMode eMode)
|
|
{
|
|
// Acquire exclusive access.
|
|
osl::MutexGuard aGuard(*this);
|
|
|
|
// Check precond.
|
|
if (!self::isValid())
|
|
return store_E_InvalidAccess;
|
|
|
|
// Setup inode page key.
|
|
OStorePageKey aKey;
|
|
storeError eErrCode = namei (pPath, pName, aKey);
|
|
if (eErrCode != store_E_None)
|
|
return eErrCode;
|
|
|
|
// Check for directory.
|
|
if (nAttrib & STORE_ATTRIB_ISDIR)
|
|
{
|
|
// Ugly, but necessary (backward compatibility).
|
|
aKey.m_nLow = store::htonl(rtl_crc32 (store::ntohl(aKey.m_nLow), "/", 1));
|
|
}
|
|
|
|
// Load inode page.
|
|
eErrCode = load_dirpage_Impl (aKey, rPage);
|
|
if (eErrCode != store_E_None)
|
|
{
|
|
// Check mode and reason.
|
|
if (eErrCode != store_E_NotExists)
|
|
return eErrCode;
|
|
|
|
if (eMode == storeAccessMode::ReadWrite)
|
|
return store_E_NotExists;
|
|
if (eMode == storeAccessMode::ReadOnly)
|
|
return store_E_NotExists;
|
|
|
|
if (!base::isWriteable())
|
|
return store_E_AccessViolation;
|
|
|
|
// Create inode page.
|
|
eErrCode = rPage.construct< inode >(base::allocator());
|
|
if (eErrCode != store_E_None)
|
|
return eErrCode;
|
|
|
|
// Setup inode nameblock.
|
|
PageHolderObject< inode > xPage (rPage.get());
|
|
|
|
rPage.key (aKey);
|
|
rPage.attrib (nAttrib);
|
|
|
|
memcpy (
|
|
&(xPage->m_aNameBlock.m_pData[0]),
|
|
pName->buffer, pName->length);
|
|
|
|
// Save inode page.
|
|
eErrCode = save_dirpage_Impl (aKey, rPage);
|
|
if (eErrCode != store_E_None)
|
|
return eErrCode;
|
|
}
|
|
|
|
// Check for symbolic link.
|
|
if (rPage.attrib() & STORE_ATTRIB_ISLINK)
|
|
{
|
|
// Obtain 'Destination' page key.
|
|
PageHolderObject< inode > xPage (rPage.get());
|
|
OStorePageKey aDstKey;
|
|
memcpy (&aDstKey, &(xPage->m_pData[0]), sizeof(aDstKey));
|
|
|
|
// Load 'Destination' inode.
|
|
eErrCode = load_dirpage_Impl (aDstKey, rPage);
|
|
if (eErrCode != store_E_None)
|
|
return eErrCode;
|
|
}
|
|
|
|
// Done.
|
|
return store_E_None;
|
|
}
|
|
|
|
/*
|
|
* iterate.
|
|
* Precond: initialized.
|
|
* ToDo: skip hardlink entries.
|
|
*/
|
|
storeError OStorePageManager::iterate (
|
|
OStorePageKey & rKey,
|
|
OStorePageLink & rLink,
|
|
sal_uInt32 & rAttrib)
|
|
{
|
|
// Acquire exclusive access.
|
|
osl::MutexGuard aGuard(*this);
|
|
|
|
// Check precond.
|
|
if (!self::isValid())
|
|
return store_E_InvalidAccess;
|
|
|
|
// Find NodePage and Index.
|
|
OStoreBTreeNodeObject aNode;
|
|
sal_uInt16 i = 0;
|
|
storeError eErrCode = m_aRoot.find_lookup (aNode, i, rKey, *this);
|
|
if (eErrCode != store_E_None)
|
|
return eErrCode;
|
|
|
|
// GreaterEqual. Found next entry.
|
|
PageHolderObject< page > xNode (aNode.get());
|
|
entry e (xNode->m_pData[i]);
|
|
|
|
// Setup result.
|
|
rKey = e.m_aKey;
|
|
rLink = e.m_aLink;
|
|
rAttrib = store::ntohl(e.m_nAttrib);
|
|
|
|
// Done.
|
|
return store_E_None;
|
|
}
|
|
|
|
/*
|
|
* load => private: iget() @@@
|
|
* Internal: Precond: initialized, exclusive access.
|
|
*/
|
|
storeError OStorePageManager::load_dirpage_Impl (
|
|
const OStorePageKey &rKey,
|
|
OStoreDirectoryPageObject &rPage)
|
|
{
|
|
// Find Node and Index.
|
|
OStoreBTreeNodeObject aNode;
|
|
sal_uInt16 i = 0;
|
|
storeError eErrCode = find_lookup (aNode, i, rKey);
|
|
if (eErrCode != store_E_None)
|
|
return eErrCode;
|
|
|
|
// Existing entry. Load page.
|
|
PageHolderObject< page > xNode (aNode.get());
|
|
entry e (xNode->m_pData[i]);
|
|
return loadObjectAt (rPage, e.m_aLink.location());
|
|
}
|
|
|
|
/*
|
|
* save => private: iget(), rebuild() @@@
|
|
* Internal: Precond: initialized, writeable, exclusive access.
|
|
*/
|
|
storeError OStorePageManager::save_dirpage_Impl (
|
|
const OStorePageKey &rKey,
|
|
OStoreDirectoryPageObject &rPage)
|
|
{
|
|
// Find NodePage and Index.
|
|
node aNode;
|
|
sal_uInt16 i = 0;
|
|
|
|
storeError eErrCode = m_aRoot.find_insert (aNode, i, rKey, *this);
|
|
PageHolderObject< page > xNode (aNode.get());
|
|
if (eErrCode != store_E_None)
|
|
{
|
|
if (eErrCode != store_E_AlreadyExists)
|
|
return eErrCode;
|
|
|
|
// Existing entry.
|
|
entry e (xNode->m_pData[i]);
|
|
if (e.m_aLink.location() != STORE_PAGE_NULL)
|
|
{
|
|
// Save page to existing location.
|
|
return saveObjectAt (rPage, e.m_aLink.location());
|
|
}
|
|
|
|
// Allocate page.
|
|
eErrCode = base::allocate (rPage);
|
|
if (eErrCode != store_E_None)
|
|
return eErrCode;
|
|
|
|
// Update page location.
|
|
xNode->m_pData[i].m_aLink = rPage.location();
|
|
|
|
// Save modified NodePage.
|
|
return saveObjectAt (aNode, aNode.location());
|
|
}
|
|
|
|
// Allocate page.
|
|
eErrCode = base::allocate (rPage);
|
|
if (eErrCode != store_E_None)
|
|
return eErrCode;
|
|
|
|
// Insert.
|
|
OStorePageLink aLink (rPage.location());
|
|
xNode->insert (i + 1, entry (rKey, aLink));
|
|
|
|
// Save modified NodePage.
|
|
return saveObjectAt (aNode, aNode.location());
|
|
}
|
|
|
|
/*
|
|
* remove.
|
|
* Precond: initialized, writeable.
|
|
*/
|
|
storeError OStorePageManager::remove (const OStorePageKey &rKey)
|
|
{
|
|
// Acquire exclusive access.
|
|
osl::MutexGuard aGuard(*this);
|
|
|
|
// Check precond.
|
|
if (!self::isValid())
|
|
return store_E_InvalidAccess;
|
|
|
|
if (!base::isWriteable())
|
|
return store_E_AccessViolation;
|
|
|
|
// Find NodePage and index.
|
|
OStoreBTreeNodeObject aNodePage;
|
|
sal_uInt16 i = 0;
|
|
storeError eErrCode = find_lookup (aNodePage, i, rKey);
|
|
if (eErrCode != store_E_None)
|
|
return eErrCode;
|
|
|
|
// Existing entry.
|
|
PageHolderObject< page > xNodePage (aNodePage.get());
|
|
entry e (xNodePage->m_pData[i]);
|
|
|
|
// Check for (not a) hardlink.
|
|
if (!(store::ntohl(e.m_nAttrib) & STORE_ATTRIB_ISLINK))
|
|
{
|
|
// Load directory page.
|
|
OStoreDirectoryPageObject aPage;
|
|
eErrCode = base::loadObjectAt (aPage, e.m_aLink.location());
|
|
if (eErrCode != store_E_None)
|
|
return eErrCode;
|
|
|
|
inode_holder_type xNode (aPage.get());
|
|
|
|
// Acquire page write access.
|
|
OStorePageDescriptor aDescr (xNode->m_aDescr);
|
|
eErrCode = base::acquirePage (aDescr, storeAccessMode::ReadWrite);
|
|
if (eErrCode != store_E_None)
|
|
return eErrCode;
|
|
|
|
// Check for symbolic link.
|
|
if (!(aPage.attrib() & STORE_ATTRIB_ISLINK))
|
|
{
|
|
// Ordinary inode. Determine 'Data' scope.
|
|
inode::ChunkScope eScope = xNode->scope (aPage.dataLength());
|
|
if (eScope == inode::SCOPE_EXTERNAL)
|
|
{
|
|
// External 'Data' scope. Truncate all external data pages.
|
|
eErrCode = aPage.truncate (0, *this);
|
|
if (eErrCode != store_E_None)
|
|
return eErrCode;
|
|
}
|
|
|
|
// Truncate internal data page.
|
|
memset (&(xNode->m_pData[0]), 0, xNode->capacity());
|
|
aPage.dataLength (0);
|
|
}
|
|
|
|
// Release page write access.
|
|
base::releasePage (aDescr);
|
|
|
|
// Release and free directory page.
|
|
(void)base::free (aPage.location());
|
|
}
|
|
|
|
// Remove entry.
|
|
return remove_Impl (e);
|
|
}
|
|
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|