/**************************************************************
 * 
 * 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
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_store.hxx"

#include "storpage.hxx"

#include "sal/types.h"
#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 (void)
{
}

/*
 * ~OStorePageManager.
 */
OStorePageManager::~OStorePageManager (void)
{
}

/*
 * isKindOf.
 */
sal_Bool SAL_CALL 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());
    OSL_POSTCOND(rIndex < xPage->usageCount(), "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.
 */
#if 0  /* EXP */
storeError OStorePageManager::remove_Impl (entry & rEntry)
{
    // Find Node and Index.
    OStoreBTreeNodeObject aNode;
    sal_uInt16 nIndex = 0;
    eErrCode = m_aRoot.find_lookup (aNode, nIndex, entry::CompareGreater(rEntry), *this);

    // @@@

    PageHolderObject< page > xPage (aNode.get());
    page & rPage = (*xPage);

    // Check current page index.
    sal_uInt16 i = rPage.find (rEntry), n = rPage.usageCount();
    if (!(i < n))
    {
        // Path to entry not exists (Must not happen(?)).
        return store_E_NotExists;
    }

    // Compare entry.
	entry::CompareResult result = rEntry.compare (rPage.m_pData[i]);

    for (; result == entry::COMPARE_GREATER && xPage->depth() > 0; )
    {
        // Check next node address.
        sal_uInt32 const nAddr = rPage.m_pData[i].m_aLink.location();
        if (nAddr == STORE_PAGE_NULL)
        {
            // Path to entry not exists (Must not happen(?)).
            return store_E_NotExists;
        }

        // Load next node page.
        eErrCode = loadObjectAt (aNode, nAddr);

        PageHolderObject< page > xNext (aNode.get());
        xNext.swap (xPage);
    }

    aNode.remove (nIndex, rEntry, *this);


    do
    {
        // Load next node page.
        eErrCode = loadObjectAt (aNode, nAddr);

        page const & rPage = (*xPage);

        // Check current page index.
        sal_uInt16 i = rPage.find (rEntry), n = rPage.usageCount();
        if (!(i < n))
        {
            // Path to entry not exists (Must not happen(?)).
            return store_E_NotExists;
        }

        // Compare entry.
        result = rEntry.compare (rPage.m_pData[i]);

    } while (result == entry::COMPATE_GREATER);
}
#endif /* EXP */

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]);
	}

	OSL_POSTCOND(
		result != entry::COMPARE_LESS,
		"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 == store_AccessReadWrite)
			return store_E_NotExists;
		if (eMode == store_AccessReadOnly)
			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());
}

/*
 * attrib [nAttrib = ((nAttrib & ~nMask1) | nMask2)].
 * Precond: initialized.
 */
storeError OStorePageManager::attrib (
	const OStorePageKey &rKey,
	sal_uInt32           nMask1,
	sal_uInt32           nMask2,
	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 = find_lookup (aNode, i, rKey);
	if (eErrCode != store_E_None)
		return eErrCode;

	// Existing entry.
    PageHolderObject< page > xNode (aNode.get());
	entry e (xNode->m_pData[i]);
	if (nMask1 != nMask2)
	{
		// Evaluate new attributes.
		sal_uInt32 nAttrib = store::ntohl(e.m_nAttrib);

		nAttrib &= ~nMask1;
		nAttrib |=  nMask2;

		if (store::htonl(nAttrib) != e.m_nAttrib)
		{
			// Check access mode.
			if (base::isWriteable())
			{
				// Set new attributes.
				e.m_nAttrib = store::htonl(nAttrib);
				xNode->m_pData[i] = e;

				// Save modified NodePage.
				eErrCode = saveObjectAt (aNode, aNode.location());
			}
			else
			{
				// Access denied.
				eErrCode = store_E_AccessViolation;
			}
		}
	}

	// Obtain current attributes.
	rAttrib = store::ntohl(e.m_nAttrib);
	return eErrCode;
}

/*
 * link (insert 'Source' as hardlink to 'Destination').
 * Precond: initialized, writeable.
 */
storeError OStorePageManager::link (
	const OStorePageKey &rSrcKey,
	const OStorePageKey &rDstKey)
{
	// Acquire exclusive access.
	osl::MutexGuard aGuard(*this);

	// Check precond.
	if (!self::isValid())
		return store_E_InvalidAccess;

	if (!base::isWriteable())
		return store_E_AccessViolation;

	// Find 'Destination' NodePage and Index.
    OStoreBTreeNodeObject aDstNode;
    sal_uInt16 i = 0;
	storeError eErrCode = find_lookup (aDstNode, i, rDstKey);
	if (eErrCode != store_E_None)
		return eErrCode;

    // Existing 'Destination' entry.
    PageHolderObject< page > xDstNode (aDstNode.get());
    entry e (xDstNode->m_pData[i]);
    OStorePageLink aDstLink (e.m_aLink);

	// Find 'Source' NodePage and Index.
    OStoreBTreeNodeObject aSrcNode;
    eErrCode = m_aRoot.find_insert (aSrcNode, i, rSrcKey, *this);
	if (eErrCode != store_E_None)
		return eErrCode;

	// Insert 'Source' entry.
    PageHolderObject< page > xSrcNode (aSrcNode.get());
	xSrcNode->insert (i + 1, entry (rSrcKey, aDstLink, STORE_ATTRIB_ISLINK));
	return saveObjectAt (aSrcNode, aSrcNode.location());
}

/*
 * symlink (insert 'Source' DirectoryPage as symlink to 'Destination').
 * Precond: initialized, writeable.
 */
storeError OStorePageManager::symlink (
	const rtl_String    *pSrcPath,
	const rtl_String    *pSrcName,
	const OStorePageKey &rDstKey)
{
	// Acquire exclusive access.
	osl::MutexGuard aGuard(*this);

	// Check precond.
	if (!self::isValid())
		return store_E_InvalidAccess;

	if (!base::isWriteable())
		return store_E_AccessViolation;

	// Check 'Source' parameter.
	storeError eErrCode = store_E_InvalidParameter;
	if (!(pSrcPath && pSrcName))
		return eErrCode;

	// Setup 'Source' page key.
	OStorePageKey aSrcKey;
	eErrCode = namei (pSrcPath, pSrcName, aSrcKey);
	if (eErrCode != store_E_None)
		return eErrCode;

	// Find 'Source' NodePage and Index.
    OStoreBTreeNodeObject aSrcNode;
    sal_uInt16 i = 0;
    eErrCode = m_aRoot.find_insert (aSrcNode, i, aSrcKey, *this);
	if (eErrCode != store_E_None)
		return eErrCode;

	// Initialize directory page.
	OStoreDirectoryPageObject aPage;
    eErrCode = aPage.construct< inode >(base::allocator());
	if (eErrCode != store_E_None)
		return eErrCode;

	// Setup as 'Source' directory page.
    inode_holder_type xNode (aPage.get());
	aPage.key (aSrcKey);
	memcpy (
		&(xNode->m_aNameBlock.m_pData[0]),
		pSrcName->buffer, pSrcName->length);

	// Store 'Destination' page key.
	OStorePageKey aDstKey (rDstKey);
	memcpy (&(xNode->m_pData[0]), &aDstKey, sizeof(aDstKey));

	// Mark 'Source' as symbolic link to 'Destination'.
	aPage.attrib (STORE_ATTRIB_ISLINK);
	aPage.dataLength (sal_uInt32(sizeof(aDstKey)));

	// Allocate and save 'Source' directory page.
	eErrCode = base::allocate (aPage);
	if (eErrCode != store_E_None)
		return eErrCode;

	// Insert 'Source' entry.
    PageHolderObject< page > xSrcNode (aSrcNode.get());
	OStorePageLink aSrcLink (aPage.location());
	xSrcNode->insert (i + 1, entry(aSrcKey, aSrcLink));

	// Save modified NodePage.
	return saveObjectAt (aSrcNode, aSrcNode.location());
}

/*
 * rename.
 * Precond: initialized, writeable.
 */
storeError OStorePageManager::rename (
	const OStorePageKey &rSrcKey,
	const rtl_String    *pDstPath,
	const rtl_String    *pDstName)
{
	// Acquire exclusive access.
	osl::MutexGuard aGuard(*this);

	// Check precond.
	if (!self::isValid())
		return store_E_InvalidAccess;

	if (!base::isWriteable())
		return store_E_AccessViolation;

	// Check 'Destination' parameter.
	storeError eErrCode = store_E_InvalidParameter;
	if (!(pDstPath && pDstName))
		return eErrCode;

	// Setup 'Destination' page key.
	OStorePageKey aDstKey;
	eErrCode = namei (pDstPath, pDstName, aDstKey);
	if (eErrCode != store_E_None)
		return eErrCode;

	// Find 'Source' NodePage and Index.
    OStoreBTreeNodeObject aSrcNode;
    sal_uInt16 i = 0;
	eErrCode = find_lookup (aSrcNode, i, rSrcKey);
	if (eErrCode != store_E_None)
		return eErrCode;

	// Existing 'Source' entry.
    PageHolderObject< page > xSrcNode (aSrcNode.get());
	entry e (xSrcNode->m_pData[i]);

	// Check for (not a) hardlink.
    OStoreDirectoryPageObject aPage;
	if (!(store::ntohl(e.m_nAttrib) & STORE_ATTRIB_ISLINK))
	{
		// Load directory page.
		eErrCode = base::loadObjectAt (aPage, e.m_aLink.location());
		if (eErrCode != store_E_None)
			return eErrCode;

		// Check for directory.
		if (aPage.attrib() & STORE_ATTRIB_ISDIR)
		{
			// Ugly, but necessary (backward compatibility).
			aDstKey.m_nLow = store::htonl(rtl_crc32 (store::ntohl(aDstKey.m_nLow), "/", 1));
		}
	}

	// Let 'Source' entry be 'Destination' entry.
	e.m_aKey = aDstKey;

	// Find 'Destination' NodePage and Index.
	OStoreBTreeNodeObject aDstNode;
    eErrCode = m_aRoot.find_insert (aDstNode, i, e.m_aKey, *this);
	if (eErrCode != store_E_None)
		return eErrCode;

	// Insert 'Destination' entry.
    PageHolderObject< page > xDstNode (aDstNode.get());
	xDstNode->insert (i + 1, e);

	eErrCode = saveObjectAt (aDstNode, aDstNode.location());
	if (eErrCode != store_E_None)
		return eErrCode;

	// Check for (not a) hardlink.
	if (!(store::ntohl(e.m_nAttrib) & STORE_ATTRIB_ISLINK))
	{
        // Modify 'Source' directory page.
        inode_holder_type xNode (aPage.get());

		// Setup 'Destination' NameBlock.
		sal_Int32 nDstLen = pDstName->length;
		memcpy (
			&(xNode->m_aNameBlock.m_pData[0]),
			pDstName->buffer, pDstName->length);
		memset (
			&(xNode->m_aNameBlock.m_pData[nDstLen]),
			0, STORE_MAXIMUM_NAMESIZE - nDstLen);
		aPage.key (e.m_aKey);

		// Save directory page.
		eErrCode = base::saveObjectAt (aPage, e.m_aLink.location());
		if (eErrCode != store_E_None)
			return eErrCode;
	}

	// Remove 'Source' entry.
	e.m_aKey = rSrcKey;
	return remove_Impl (e);
}

/*
 * 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, store_AccessReadWrite);
		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.
		eErrCode = base::releasePage (aDescr, store_AccessReadWrite);

		// Release and free directory page.
		eErrCode = base::free (aPage.location());
	}

	// Remove entry.
	return remove_Impl (e);
}

/*
 * RebuildContext.
 */
struct RebuildContext
{
	/** Representation.
	*/
	rtl::Reference<OStorePageBIOS> m_xBIOS;
	OStorePageBIOS::ScanContext    m_aCtx;
    sal_uInt16                     m_nPageSize;

	/** Construction.
	 */
	RebuildContext (void)
		: m_xBIOS     (new OStorePageBIOS()),
          m_nPageSize (0)
	{}

	/** initialize (PageBIOS and ScanContext).
	*/
	storeError initialize (ILockBytes *pLockBytes, sal_uInt32 nMagic = 0)
	{
		storeError eErrCode = store_E_InvalidParameter;
		if (pLockBytes)
		{
			m_xBIOS->initialize (pLockBytes, store_AccessReadOnly, m_nPageSize);
			eErrCode = m_xBIOS->scanBegin (m_aCtx, nMagic);
		}
		return eErrCode;
	}

	/** initialize (ScanContext).
	*/
	storeError initialize (sal_uInt32 nMagic = 0)
	{
		return m_xBIOS->scanBegin (m_aCtx, nMagic);
	}

	/** load (next ScanContext matching page).
	*/
	storeError load (OStorePageObject &rPage)
	{
		if (m_aCtx.isValid())
			return m_xBIOS->scanNext (m_aCtx, rPage);
		else
			return store_E_CantSeek;
	}
};

/*
 * rebuild.
 * Precond: none.
 */
storeError OStorePageManager::rebuild (
	ILockBytes *pSrcLB, ILockBytes *pDstLB)
{
	// Acquire exclusive access.
	osl::MutexGuard aGuard(*this);

	// Check arguments.
	storeError eErrCode = store_E_InvalidParameter;
	if (!(pSrcLB && pDstLB))
		return eErrCode;

	// Initialize 'Source' rebuild context.
	RebuildContext aCtx;
	eErrCode = aCtx.initialize (pSrcLB, STORE_MAGIC_DIRECTORYPAGE);
	if (eErrCode != store_E_None)
		return eErrCode;
    rtl::Reference<OStorePageBIOS> xSrcBIOS (aCtx.m_xBIOS);

	// Initialize as 'Destination' with 'Source' page size.
	eErrCode = self::initialize (pDstLB, store_AccessCreate, aCtx.m_nPageSize);
	if (eErrCode != store_E_None)
		return eErrCode;

	// Pass One: Scan 'Source' directory pages.
    {
        // Scan 'Source' directory pages.
        OStoreDirectoryPageObject aSrcPage;
        while ((eErrCode = aCtx.load(aSrcPage)) == store_E_None)
        {
            OStoreDirectoryPageObject aDstPage;
            eErrCode = aDstPage.construct< inode >(base::allocator());
            if (eErrCode != store_E_None)
                break;

            inode_holder_type xSrcDir (aSrcPage.get());
            inode_holder_type xDstDir (aDstPage.get());

            // Copy NameBlock @@@ OLD @@@
            memcpy (&(xDstDir->m_aNameBlock), &(xSrcDir->m_aNameBlock), sizeof(xSrcDir->m_aNameBlock));

            // Obtain 'Source' data length.
            sal_uInt32 nDataLen = aSrcPage.dataLength();
            if (nDataLen > 0)
            {
                // Copy internal data area @@@ OLD @@@
                memcpy (&(xDstDir->m_pData[0]), &(xSrcDir->m_pData[0]), xSrcDir->capacity());
            }

            // Insert 'Destination' directory page.
            eErrCode = save_dirpage_Impl (aDstPage.key(), aDstPage);
            if (eErrCode != store_E_None)
                break;

            // Check for external data page scope.
            if (xSrcDir->scope(nDataLen) != inode::SCOPE_INTERNAL)
            {
                // Initialize 'Destination' data page.
                typedef OStoreDataPageData data;
                PageHolderObject< data > xData;
                if (!xData.construct(base::allocator()))
                    return store_E_OutOfMemory;

                // Determine data page count.
                inode::ChunkDescriptor aDescr (
                    nDataLen - xDstDir->capacity(), xData->capacity());
				
                sal_uInt32 i, n = aDescr.m_nPage;
                if (aDescr.m_nOffset) n += 1;

                // Copy data pages.
                OStoreDataPageObject aData;
                for (i = 0; i < n; i++)
                {
                    // Read 'Source' data page.
                    osl::MutexGuard aSrcGuard (*xSrcBIOS);

                    eErrCode = aSrcPage.read (i, aData, *xSrcBIOS);
                    if (eErrCode != store_E_None)
                        continue;

                    // Write 'Destination' data page. @@@ READONLY @@@
                    eErrCode = aDstPage.write (i, aData, *this);
                }
            }

            // Update 'Destination' directory page.
            aDstPage.dataLength (nDataLen);
            eErrCode = base::saveObjectAt (aDstPage, aDstPage.location());
        }

        // Save directory scan results.
        flush();
    }

    // Pass Two: Scan 'Source' BTree nodes.
    {
        // Re-start 'Source' rebuild context.
        aCtx.initialize (STORE_MAGIC_BTREENODE);

        // Scan 'Source' BTree nodes.
        OStoreBTreeNodeObject aNode;
        while ((eErrCode = aCtx.load(aNode)) == store_E_None)
        {
            // Check for leaf node.
            PageHolderObject< page > xNode (aNode.get());
            if (xNode->depth() == 0)
            {
                sal_uInt16 i, n = xNode->usageCount();
                for (i = 0; i < n; i++)
                {
                    entry e (xNode->m_pData[i]);

                    // Check for Hard link.
                    if (e.m_nAttrib & STORE_ATTRIB_ISLINK)
                    {
                        // Load the hard link destination.
                        OStoreDirectoryPageObject aSrcPage;
                        eErrCode = xSrcBIOS->loadObjectAt (aSrcPage, e.m_aLink.location());
                        if (eErrCode == store_E_None)
                        {
                            OStorePageKey aDstKey (aSrcPage.key());
                            eErrCode = link (e.m_aKey, aDstKey);
                        }
                        e.m_nAttrib &= ~STORE_ATTRIB_ISLINK;
                    }

                    if (e.m_nAttrib)
                    {
                        // Ordinary attributes.
                        sal_uInt32 nAttrib = 0;
                        eErrCode = attrib (e.m_aKey, 0, e.m_nAttrib, nAttrib);
                    }
                }
            }
        }

        // Save BTree node scan results.
        flush();
    }

	// Done.
	return store_E_None;
}
