/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org 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 Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 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.
 */

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

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */