Files
mars-flaim/xflaim/src/flverify.cpp
ahodgkinson 0f853ff59e Fixed problems with various utilities.
git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@547 0109f412-320b-0410-ab79-c3e0c5ffbbe6
2006-06-12 20:10:52 +00:00

4919 lines
108 KiB
C++

//------------------------------------------------------------------------------
// Desc: Verify data in a database.
//
// Tabs: 3
//
// Copyright (c) 1991-2006 Novell, Inc. All Rights Reserved.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of version 2 of the GNU General Public
// License as published by the Free Software Foundation.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, contact Novell, Inc.
//
// To contact Novell about this file by physical or electronic mail,
// you may find current contact information at www.novell.com
//
// $Id: flverify.cpp 3114 2006-01-19 13:22:45 -0700 (Thu, 19 Jan 2006) dsanders $
//------------------------------------------------------------------------------
#include "flaimsys.h"
FSTATIC FLMBYTE * getEntryEnd(
FLMBYTE * pucEntry,
FLMUINT uiBlkType);
FLMUINT64 getLinkVal(
FLMUINT uiBMId,
NODE_RS_ENTRY * pRSEntry);
FSTATIC RCODE flmVerifyKeyOrder(
STATE_INFO * pStateInfo,
LFILE * pLFile,
IXD * pIxd,
F_BLK_HDR * pBlkHdr,
FLMBYTE * pucElmKey,
FLMUINT uiElmKeyLen,
FLMUINT uiElmOffset);
FSTATIC FLMBOOL flmVerifyElementChain(
STATE_INFO * pStateInfo,
LFILE * pLFile);
FSTATIC RCODE verifyRootLink(
NODE_RS_ENTRY * pRSEntry,
FLMUINT uiRSEntrySize,
NODE_RS_ENTRY * pTmpRSEntry,
F_BtResultSet * pResult,
FLMINT * piErrCode);
FSTATIC RCODE verifyParentLink(
NODE_RS_ENTRY * pRSEntry,
NODE_RS_ENTRY * pTmpRSEntry,
F_BtResultSet * pResult,
FLMINT * piErrCode);
FSTATIC RCODE verifyFirstChildLink(
NODE_RS_ENTRY * pRSEntry,
NODE_RS_ENTRY * pTmpRSEntry,
F_BtResultSet * pResult,
FLMINT * piErrCode);
FSTATIC RCODE verifyLastChildLink(
NODE_RS_ENTRY * pRSEntry,
NODE_RS_ENTRY * pTmpRSEntry,
F_BtResultSet * pResult,
FLMINT * piErrCode);
FSTATIC RCODE verifyPrevSiblingLink(
NODE_RS_ENTRY * pRSEntry,
NODE_RS_ENTRY * pTmpRSEntry,
F_BtResultSet * pResult,
FLMINT * piErrCode);
FSTATIC RCODE verifyNextSiblingLink(
NODE_RS_ENTRY * pRSEntry,
NODE_RS_ENTRY * pTmpRSEntry,
F_BtResultSet * pResult,
FLMINT * piErrCode);
FSTATIC RCODE verifyAnnotationLink(
NODE_RS_ENTRY * pRSEntry,
NODE_RS_ENTRY * pTmpRSEntry,
F_BtResultSet * pResult,
FLMINT * piErrCode);
/********************************************************************
Desc: Verifies a block's header and sets up the STATE_INFO structure
to verify the rest of the block.
*********************************************************************/
FLMINT flmVerifyBlockHeader(
STATE_INFO * pStateInfo,
BLOCK_INFO * pBlockInfo,
FLMUINT uiBlockSize,
FLMUINT uiExpNextBlkAddr,
FLMUINT uiExpPrevBlkAddr,
FLMBOOL bCheckEOF)
{
F_BLK_HDR * pBlkHdr = pStateInfo->pBlkHdr;
if (pBlockInfo)
{
pBlockInfo->uiBlockCount++;
}
pStateInfo->ui32NextBlkAddr = pBlkHdr->ui32NextBlkInChain;
if( (FLMUINT)pBlkHdr->ui16BlkBytesAvail > uiBlockSize - blkHdrSize( pBlkHdr))
{
return( FLM_BAD_BLK_HDR_BLK_END);
}
else
{
if( pBlockInfo)
{
pBlockInfo->ui64BytesUsed +=
(FLMUINT64)(uiBlockSize - pBlkHdr->ui16BlkBytesAvail -
blkHdrSize( pBlkHdr));
}
}
// Verify the block address.
if ((FLMUINT)pBlkHdr->ui32BlkAddr != pStateInfo->ui32BlkAddress)
{
return( FLM_BAD_BLK_HDR_ADDR);
}
// Verify that block address is below the logical EOF
// Rebuild passes in FALSE for bCheckEOF
if( bCheckEOF && pStateInfo->pDb)
{
if( !FSAddrIsBelow( pStateInfo->ui32BlkAddress,
pStateInfo->pDb->getLogicalEOF()))
{
return( FLM_BAD_FILE_SIZE);
}
}
// Verify the block type.
if( pStateInfo->uiBlkType != 0xFF &&
pStateInfo->uiBlkType != (FLMUINT)pBlkHdr->ui8BlkType)
{
return( FLM_BAD_BLK_HDR_TYPE);
}
// Verify the block level - if there is one
if( pStateInfo->uiBlkType != BT_DATA_ONLY &&
pStateInfo->uiLevel != 0xFF &&
blkIsBTree( pBlkHdr) &&
pStateInfo->uiLevel !=
(FLMUINT)(((F_BTREE_BLK_HDR *)pBlkHdr)->ui8BlkLevel))
{
return( FLM_BAD_BLK_HDR_LEVEL);
}
// Verify the previous block address. If uiExpPrevBlkAddr is 0xFFFFFFFF,
// we do not know what the previous address should be, so we don't verify.
if (uiExpPrevBlkAddr != 0xFFFFFFFF &&
uiExpPrevBlkAddr != (FLMUINT)pBlkHdr->ui32PrevBlkInChain)
{
return( FLM_BAD_BLK_HDR_PREV);
}
// Verify the next block address. If uiExpNextBlkAddr is 0xFFFFFFFF,
// we do not know what the next address should be, se we don't verify.
if( uiExpNextBlkAddr != 0xFFFFFFFF &&
uiExpNextBlkAddr != pStateInfo->ui32NextBlkAddr)
{
return( FLM_BAD_BLK_HDR_NEXT);
}
// Verify that if it is a root block, the root bit flags is set,
// or if it is NOT a root block, that the root bit flag is NOT set.
// Note that Data Only blocks don't have the extended header, so we
// can't do this check...
if( pStateInfo->pCollection &&
(pStateInfo->uiBlkType != BT_DATA_ONLY) )
{
if( pStateInfo->uiLevel != 0xFF)
{
F_BTREE_BLK_HDR * pBTreeBlkHdr = (F_BTREE_BLK_HDR *)pBlkHdr;
FLMBOOL bShouldBeRootBlk =
(pStateInfo->uiLevel ==
pStateInfo->uiRootLevel)
? TRUE
: FALSE;
if ((bShouldBeRootBlk && !isRootBlk( pBTreeBlkHdr)) ||
(!bShouldBeRootBlk && isRootBlk( pBTreeBlkHdr)))
{
return( FLM_BAD_BLK_HDR_ROOT_BIT);
}
}
// Verify the logical file number - if any.
if( (pBlkHdr->ui8BlkType != BT_DATA_ONLY) &&
(pStateInfo->pCollection->lfInfo.uiLfNum !=
(FLMUINT)(((F_BTREE_BLK_HDR *)pBlkHdr)->ui16LogicalFile)) )
{
return( FLM_BAD_BLK_HDR_LF_NUM);
}
}
return( 0);
}
/********************************************************************
Desc: Verify an index or data element in a b-tree and set pStateInfo.
*********************************************************************/
RCODE flmVerifyElement(
STATE_INFO * pStateInfo,
LFILE * pLFile,
IXD * pIxd,
FLMINT * piErrCode)
{
RCODE rc = NE_XFLM_OK;
FLMBYTE * pucEntry;
FLMUINT32 ui32DOAddr = 0;
FLMUINT32 ui32ChildAddr = 0;
FLMUINT uiCounts = 0;
*piErrCode = 0;
// Get the pointer to the element.
pucEntry = BtEntry( (FLMBYTE *)pStateInfo->pBlkHdr, pStateInfo->uiElmOffset);
pStateInfo->pucElm = pucEntry;
// Verify that the address of the entry is within the block.
if ((FLMUINT)pucEntry > (FLMUINT)pStateInfo->pBlkHdr +
pStateInfo->pDb->getDatabase()->getBlockSize())
{
*piErrCode = FLM_BAD_ELM_OFFSET;
goto Exit;
}
switch( pStateInfo->pBlkHdr->ui8BlkType)
{
case BT_LEAF:
{
pStateInfo->uiElmKeyLen = FB2UW( pucEntry);
f_memcpy( pStateInfo->pucElmKey, pucEntry + 2, pStateInfo->uiElmKeyLen);
pStateInfo->uiElmLen = pStateInfo->uiElmKeyLen + 2;
break;
}
case BT_LEAF_DATA:
{
FLMBYTE ucFlags = *pucEntry;
FLMBYTE * pucPtr = pucEntry + 1;
FLMUINT uiElmLen = 1;
if( ucFlags & BTE_FLAG_KEY_LEN)
{
// 2 byte key length
pStateInfo->uiElmKeyLen = FB2UW( pucPtr);
pucPtr += 2;
uiElmLen += 2;
}
else
{
// 1 byte key length
pStateInfo->uiElmKeyLen = (FLMUINT)*pucPtr;
pucPtr++;
uiElmLen++;
}
if( ucFlags & BTE_FLAG_DATA_LEN)
{
// 2 byte data length
pStateInfo->uiElmDataLen = FB2UW( pucPtr);
pucPtr += 2;
uiElmLen += 2;
}
else
{
// 1 byte data length
pStateInfo->uiElmDataLen = (FLMUINT)*pucPtr;
pucPtr++;
uiElmLen++;
}
if( ucFlags & BTE_FLAG_OA_DATA_LEN)
{
pStateInfo->uiElmOADataLen = FB2UD( pucPtr);
pucPtr += 4;
uiElmLen += 4;
}
f_memcpy( pStateInfo->pucElmKey, pucPtr, pStateInfo->uiElmKeyLen);
pucPtr += pStateInfo->uiElmKeyLen;
uiElmLen += pStateInfo->uiElmKeyLen;
pStateInfo->pucElmData = pucPtr;
uiElmLen += pStateInfo->uiElmDataLen;
if( ucFlags & BTE_FLAG_DATA_BLOCK)
{
ui32DOAddr = FB2UD( pucPtr);
}
pStateInfo->uiElmLen = uiElmLen;
break;
}
case BT_NON_LEAF:
{
FLMBYTE * pucPtr = pucEntry;
ui32ChildAddr = FB2UD( pucPtr);
pucPtr += 4;
pStateInfo->uiElmKeyLen = FB2UW( pucPtr);
pucPtr += 2;
f_memcpy( pStateInfo->pucElmKey, pucPtr, pStateInfo->uiElmKeyLen);
pStateInfo->uiElmLen = pStateInfo->uiElmKeyLen + 6;
break;
}
case BT_NON_LEAF_COUNTS:
{
FLMBYTE * pucPtr = pucEntry;
ui32ChildAddr = FB2UD( pucPtr);
pucPtr += 4;
uiCounts = FB2UD( pucPtr);
pucPtr += 4;
pStateInfo->uiElmKeyLen = FB2UW( pucPtr);
pucPtr += 2;
f_memcpy( pStateInfo->pucElmKey, pucPtr, pStateInfo->uiElmKeyLen);
pStateInfo->uiElmLen = pStateInfo->uiElmKeyLen + 10;
break;
}
default:
{
*piErrCode = FLM_BAD_BLK_TYPE;
goto Exit;
}
}
// Verify that the end of the element is not past the end of the block.
if( pStateInfo->pucElm + pStateInfo->uiElmLen >
(FLMBYTE *)pStateInfo->pBlkHdr +
pStateInfo->pDb->getDatabase()->getBlockSize())
{
*piErrCode = FLM_BAD_ELM_LEN;
goto Exit;
}
// Verify the key length is not too big
if( pStateInfo->uiElmKeyLen > XFLM_MAX_KEY_SIZE)
{
*piErrCode = FLM_BAD_ELM_KEY_SIZE;
goto Exit;
}
// Verify the key order. First the previous key, then the next key.
if( RC_BAD( rc = flmVerifyKeyOrder( pStateInfo, pLFile, pIxd,
pStateInfo->pBlkHdr, pStateInfo->pucElmKey, pStateInfo->uiElmKeyLen,
pStateInfo->uiElmOffset)))
{
*piErrCode = FLM_BAD_ELM_KEY_ORDER;
goto Exit;
}
pStateInfo->bValidKey = TRUE;
if( !isIndexBlk( (F_BTREE_BLK_HDR *)pStateInfo->pBlkHdr))
{
switch( pStateInfo->pBlkHdr->ui8BlkType)
{
case BT_LEAF:
case BT_LEAF_DATA:
{
FLMBOOL bNeg;
FLMUINT uiBytesProcessed;
if( pStateInfo->pucElm[0] & BTE_FLAG_FIRST_ELEMENT)
{
if( !flmVerifyElementChain( pStateInfo, pLFile))
{
*piErrCode = FLM_BAD_ELEMENT_CHAIN;
goto Exit;
}
}
// The key length may be zero on a LEM
if( pStateInfo->uiElmKeyLen)
{
if( RC_BAD( rc = flmCollation2Number(
pStateInfo->uiElmKeyLen, pStateInfo->pucElmKey,
&pStateInfo->ui64ElmNodeId, &bNeg, &uiBytesProcessed)))
{
*piErrCode = FLM_BAD_ELM_KEY;
goto Exit;
}
if( bNeg || uiBytesProcessed != pStateInfo->uiElmKeyLen)
{
*piErrCode = FLM_BAD_ELM_KEY;
goto Exit;
}
if( !pStateInfo->ui64ElmNodeId)
{
flmAssert( 0);
*piErrCode = FLM_BAD_ELM_KEY;
goto Exit;
}
}
else
{
// If the key length is zero, then this MUST be the last block!
if( pStateInfo->pBlkHdr->ui32NextBlkInChain)
{
*piErrCode = FLM_BAD_ELM_KEY;
goto Exit;
}
}
break;
}
case BT_NON_LEAF:
{
break;
}
case BT_NON_LEAF_COUNTS:
{
break;
}
}
}
Exit:
return( rc);
}
/***************************************************************************
Desc: Verify that the key order is correct.
*****************************************************************************/
FSTATIC RCODE flmVerifyKeyOrder(
STATE_INFO * pStateInfo,
LFILE * pLFile,
IXD * pIxd,
F_BLK_HDR * pBlkHdr,
FLMBYTE * pucElmKey,
FLMUINT uiElmKeyLen,
FLMUINT uiElmOffset)
{
RCODE rc = NE_XFLM_OK;
F_CachedBlock * pPrevSCache = NULL;
F_CachedBlock * pNextSCache = NULL;
F_BTREE_BLK_HDR * pPrevBlk;
F_BTREE_BLK_HDR * pNextBlk;
FLMBYTE * pucEntry = NULL;
FLMBYTE * pucKey = NULL;
FLMUINT uiKeyLen = 0;
FLMBOOL bCheckPrev = FALSE;
FLMBOOL bCheckNext = FALSE;
FLMUINT uiBlockSize;
uiBlockSize = pStateInfo->pDb->getDatabase()->getBlockSize();
// Get the previous key
if( uiElmOffset)
{
flmAssert( uiElmOffset < ((F_BTREE_BLK_HDR *)pBlkHdr)->ui16NumKeys);
pucEntry = BtEntry( (FLMBYTE *)pBlkHdr, uiElmOffset - 1);
if( (FLMUINT)pucEntry > (FLMUINT)pBlkHdr + uiBlockSize)
{
rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE);
goto Exit;
}
bCheckPrev = TRUE;
}
else if( uiElmOffset == 0 && pBlkHdr->ui32PrevBlkInChain)
{
if( pLFile)
{
// Need to get the previous block.
if( RC_BAD( rc = pStateInfo->pDb->getDatabase()->getBlock(
pStateInfo->pDb, pLFile, pBlkHdr->ui32PrevBlkInChain, NULL,
&pPrevSCache)))
{
goto Exit;
}
pPrevBlk = (F_BTREE_BLK_HDR *)pPrevSCache->getBlockPtr();
pucEntry = BtEntry( (FLMBYTE *)pPrevBlk, pPrevBlk->ui16NumKeys - 1);
if( (FLMUINT)pucEntry > (FLMUINT)pPrevBlk + uiBlockSize)
{
rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE);
goto Exit;
}
if( pBlkHdr->ui8BlkType != pPrevBlk->stdBlkHdr.ui8BlkType)
{
rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE);
goto Exit;
}
bCheckPrev = TRUE;
}
}
if( bCheckPrev)
{
// Get the key
switch (pBlkHdr->ui8BlkType)
{
case BT_LEAF:
{
uiKeyLen = FB2UW( pucEntry);
pucKey = pucEntry + 2;
break;
}
case BT_LEAF_DATA:
{
FLMBYTE ucFlags = *pucEntry;
FLMBYTE * pucPtr = pucEntry + 1;
flmAssert( (*pucEntry & 0x03) == 0);
if (ucFlags & BTE_FLAG_KEY_LEN)
{
uiKeyLen = FB2UW( pucPtr);
pucPtr += 2;
}
else
{
uiKeyLen = *pucPtr;
pucPtr++;
}
if( ucFlags & BTE_FLAG_DATA_LEN)
{
pucPtr += 2;
}
else
{
pucPtr++;
}
if (ucFlags & BTE_FLAG_OA_DATA_LEN)
{
pucPtr += 4;
}
pucKey = pucPtr;
break;
}
case BT_NON_LEAF:
{
FLMBYTE * pucPtr = pucEntry + 4;
uiKeyLen = FB2UW( pucPtr);
pucKey = pucPtr + 2;
break;
}
case BT_NON_LEAF_COUNTS:
{
FLMBYTE * pucPtr = pucEntry + 8;
uiKeyLen = FB2UW( pucPtr);
pucKey = pucPtr + 2;
break;
}
}
if( uiKeyLen)
{
if( !pLFile || pLFile->eLfType == XFLM_LF_COLLECTION)
{
if( f_memcmp( pucElmKey, pucKey, uiElmKeyLen < uiKeyLen
? uiElmKeyLen
: uiKeyLen) < 0)
{
rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE);
goto Exit;
}
}
else
{
// If uiElmKeyLen == 0, it is the LEM, in which case we won't
// do the comparison, because pucElmKey is greater than pucKey
// by definition.
if( uiElmKeyLen)
{
FLMINT iCompare;
if( RC_BAD( rc = ixKeyCompare( pStateInfo->pDb, pIxd, NULL,
NULL, NULL, TRUE, TRUE, pucElmKey, uiElmKeyLen,
pucKey, uiKeyLen, &iCompare)))
{
goto Exit;
}
if( iCompare < 0)
{
rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE);
goto Exit;
}
}
}
}
else
{
rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE);
goto Exit;
}
}
// Get the next key
if( uiElmOffset < (FLMUINT)((F_BTREE_BLK_HDR *)pBlkHdr)->ui16NumKeys - 1)
{
pucEntry = BtEntry( (FLMBYTE *)pBlkHdr, uiElmOffset + 1);
if( pucEntry > (FLMBYTE *)pBlkHdr + uiBlockSize)
{
rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE);
goto Exit;
}
bCheckNext = TRUE;
}
else if( uiElmOffset ==
(FLMUINT)((F_BTREE_BLK_HDR *)pBlkHdr)->ui16NumKeys - 1 &&
pBlkHdr->ui32NextBlkInChain)
{
if( pLFile)
{
if( RC_BAD( rc = pStateInfo->pDb->getDatabase()->getBlock(
pStateInfo->pDb, pLFile, pBlkHdr->ui32NextBlkInChain,
NULL, &pNextSCache)))
{
goto Exit;
}
pNextBlk = (F_BTREE_BLK_HDR *)pNextSCache->getBlockPtr();
pucEntry = BtEntry( (FLMBYTE *)pNextBlk, 0);
if( pucEntry > (FLMBYTE *)pNextBlk + uiBlockSize)
{
rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE);
goto Exit;
}
if( pBlkHdr->ui8BlkType != pNextBlk->stdBlkHdr.ui8BlkType)
{
rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE);
goto Exit;
}
bCheckNext = TRUE;
}
}
if( bCheckNext)
{
switch (pBlkHdr->ui8BlkType)
{
case BT_LEAF:
{
uiKeyLen = FB2UW( pucEntry);
pucKey = pucEntry + 2;
break;
}
case BT_LEAF_DATA:
{
FLMBYTE ucFlags = *pucEntry;
FLMBYTE * pucPtr = pucEntry + 1;
if( (*pucEntry & 0x03) != 0)
{
rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR);
goto Exit;
}
if( ucFlags & BTE_FLAG_KEY_LEN)
{
uiKeyLen = FB2UW( pucPtr);
pucPtr += 2;
}
else
{
uiKeyLen = *pucPtr;
pucPtr++;
}
if( ucFlags & BTE_FLAG_DATA_LEN)
{
pucPtr += 2;
}
else
{
pucPtr++;
}
if( ucFlags & BTE_FLAG_OA_DATA_LEN)
{
pucPtr += 4;
}
pucKey = pucPtr;
break;
}
case BT_NON_LEAF:
{
FLMBYTE * pucPtr = pucEntry + 4;
uiKeyLen = FB2UW( pucPtr);
pucKey = pucPtr + 2;
break;
}
case BT_NON_LEAF_COUNTS:
{
FLMBYTE * pucPtr = pucEntry + 8;
uiKeyLen = FB2UW( pucPtr);
pucKey = pucPtr + 2;
break;
}
}
if (uiKeyLen)
{
if (!pLFile || pLFile->eLfType == XFLM_LF_COLLECTION)
{
if (f_memcmp( pucKey, pucElmKey, uiElmKeyLen < uiKeyLen
? uiElmKeyLen
: uiKeyLen) < 0)
{
rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE);
goto Exit;
}
}
else
{
// If uiElmKeyLen == 0, it is the LEM, in which case we won't
// do the comparison, because pucKey will always be less than
// the LEM, but it is not an error in that case.
if( uiElmKeyLen)
{
FLMINT iCompare;
if (RC_BAD( rc = ixKeyCompare( pStateInfo->pDb, pIxd, NULL,
NULL, NULL, TRUE, TRUE, pucKey, uiKeyLen, pucElmKey,
uiElmKeyLen, &iCompare)))
{
goto Exit;
}
if( iCompare < 0)
{
rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE);
goto Exit;
}
}
}
}
else
{
// A zero length key should only occur on the last block in the chain.
if( pBlkHdr->ui32NextBlkInChain)
{
rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE);
goto Exit;
}
}
}
Exit:
if( pPrevSCache)
{
ScaReleaseCache( pPrevSCache, FALSE);
}
if( pNextSCache)
{
ScaReleaseCache( pNextSCache, FALSE);
}
return( rc);
}
/***************************************************************************
Desc:
*****************************************************************************/
FSTATIC FLMBOOL flmVerifyElementChain(
STATE_INFO * pStateInfo,
LFILE * pLFile)
{
RCODE rc = NE_XFLM_OK;
FLMBOOL bResult = TRUE;
F_BTREE_BLK_HDR * pBlkHdr = (F_BTREE_BLK_HDR *)pStateInfo->pBlkHdr;
FLMBYTE * pucElmKey = pStateInfo->pucElmKey;
FLMUINT uiElmKeyLen = pStateInfo->uiElmKeyLen;
FLMUINT uiElmOffset = pStateInfo->uiElmOffset;
FLMBYTE * pucNextKey = NULL;
FLMUINT uiNextKeySize;
FLMBYTE ucFlags = pStateInfo->pucElm[ 0];
F_CachedBlock * pSCache = NULL;
if (pLFile)
{
while (!(ucFlags & BTE_FLAG_LAST_ELEMENT))
{
// Get the next element.
if (uiElmOffset >= (FLMUINT)(pBlkHdr->ui16NumKeys - 1))
{
if (pSCache)
{
ScaReleaseCache( pSCache, FALSE);
pSCache = NULL;
}
// Will have to go the the next block to find the key we want.
if (RC_BAD( rc = pStateInfo->pDb->getDatabase()->getBlock(
pStateInfo->pDb, pLFile, pBlkHdr->stdBlkHdr.ui32NextBlkInChain,
NULL, &pSCache)))
{
RC_UNEXPECTED_ASSERT( rc);
bResult = FALSE;
goto Exit;
}
flmAssert( pBlkHdr->stdBlkHdr.ui8BlkType ==
pSCache->getBlockPtr()->ui8BlkType);
pBlkHdr = (F_BTREE_BLK_HDR *)pSCache->getBlockPtr();
uiElmOffset = 0; // Get the first element...
}
else
{
uiElmOffset++;
}
pucNextKey = BtEntry( (FLMBYTE *)pBlkHdr, uiElmOffset);
// Update the flag for the next iteration
ucFlags = pucNextKey[ 0];
if (ucFlags & BTE_FLAG_FIRST_ELEMENT)
{
flmAssert( 0);
bResult = FALSE;
goto Exit;
}
// Find the key.
pucNextKey++;
if (ucFlags & BTE_FLAG_KEY_LEN)
{
uiNextKeySize = FB2UW( pucNextKey);
pucNextKey += 2;
}
else
{
uiNextKeySize = *pucNextKey;
pucNextKey++;
}
// Key size is now set. They should match.
if (uiElmKeyLen != uiNextKeySize)
{
flmAssert( 0);
bResult = FALSE;
goto Exit;
}
// Scoot past the info we don't need
if (ucFlags & BTE_FLAG_DATA_LEN)
{
pucNextKey += 2;
}
else
{
pucNextKey++;
}
if (ucFlags & BTE_FLAG_OA_DATA_LEN)
{
pucNextKey += 4;
}
// Make sure the keys match.
if ( f_memcmp( pucElmKey, pucNextKey, uiElmKeyLen) != 0)
{
flmAssert( 0);
bResult = FALSE;
goto Exit;
}
}
}
Exit:
if (pSCache)
{
ScaReleaseCache( pSCache, FALSE);
}
return TRUE;
}
/********************************************************************
Desc: Build the result set of all keys in the database.
*********************************************************************/
RCODE F_DbCheck::buildIndexKeyList(
FLMUINT64 * pui64TotalKeys)
{
RCODE rc = NE_XFLM_OK;
F_KeyCollector * pKeyColl = NULL;
DOC_IXD_XREF XRef;
IXD * pIxd;
LFILE * pLFile;
F_DOMNode * pDocNode = NULL;
F_Dict * pDict;
FLMBOOL bUpdTranStarted = FALSE;
RCODE tmpRc = NE_XFLM_OK;
FLMUINT uiSizeRV;
F_Btree * pBTree = NULL;
// Set information for the result set sort phase.
m_Progress.iCheckPhase = XFLM_CHECK_RS_SORT;
m_Progress.bStartFlag = TRUE;
if ((pKeyColl = f_new F_KeyCollector( this)) == NULL)
{
rc = RC_SET( NE_XFLM_MEM);
goto Exit;
}
// Save the key collector in the m_pDb to redirect the
// keys generated into the key result set.
m_pDb->setKeyCollector( pKeyColl);
// Start an update transaction, end the read trans.
if (m_pDb->getTransType() == XFLM_READ_TRANS)
{
if (RC_OK( rc = m_pDb->transCommit()))
{
if (RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS)))
{
goto Exit;
}
bUpdTranStarted = TRUE;
}
else
{
goto Exit;
}
}
if (RC_BAD( rc = m_pDb->getDictionary( &pDict)))
{
goto Exit;
}
// For each entry in the Xref result set, call indexDocument to generate the
// keys.
if (RC_BAD( rc = m_pXRefRS->getBTree( NULL, NULL, &pBTree)))
{
goto Exit;
}
if( RC_BAD( rc = m_pXRefRS->getFirst( NULL, NULL, pBTree, (FLMBYTE *)&XRef,
sizeof( XRef), &uiSizeRV, NULL, 0, NULL)))
{
goto Exit;
}
for( ;;)
{
if( RC_BAD( rc = m_pDb->getNode( XRef.uiCollection, XRef.ui64DocId,
&pDocNode)))
{
goto Exit;
}
if( pDocNode->getDocumentId() == XRef.ui64DocId)
{
if( RC_BAD( rc = pDict->getIndex( XRef.uiIndexNum, &pLFile, &pIxd)))
{
// If the index is offline, skip it and move to the next, if any
if( rc == NE_XFLM_INDEX_OFFLINE)
{
if( RC_BAD( rc = m_pXRefRS->getNext( NULL, NULL, pBTree,
(FLMBYTE *)&XRef, sizeof( XRef), &uiSizeRV, NULL,
0, NULL)))
{
if( rc == NE_XFLM_NOT_FOUND || rc == NE_XFLM_EOF_HIT)
{
rc = NE_XFLM_OK;
}
break;
}
continue;
}
goto Exit;
}
if (RC_BAD( rc = m_pDb->indexDocument( pIxd, pDocNode)))
{
goto Exit;
}
m_Progress.ui64NumKeys++; // += ui64KeysProcessed;
}
// Get the next document...
if (RC_BAD( rc = m_pXRefRS->getNext( NULL, NULL, pBTree,
(FLMBYTE *)&XRef, sizeof( XRef), &uiSizeRV, NULL, 0, NULL)))
{
if (rc == NE_XFLM_NOT_FOUND || rc == NE_XFLM_EOF_HIT)
{
rc = NE_XFLM_OK;
break;
}
else
{
goto Exit;
}
}
if( RC_BAD( rc = chkCallProgFunc()))
{
goto Exit;
}
}
m_pXRefRS->freeBTree( &pBTree);
if( bUpdTranStarted)
{
if (RC_BAD( rc = m_pDb->transCommit()))
{
goto Exit;
}
bUpdTranStarted = FALSE;
if( RC_BAD( rc = m_pDb->transBegin( XFLM_READ_TRANS)))
{
goto Exit;
}
}
*pui64TotalKeys = pKeyColl->getTotalKeys();
Exit:
if( pBTree)
{
m_pXRefRS->freeBTree( &pBTree);
}
if( bUpdTranStarted)
{
// If we fail here, the whole thing should abort.
if( RC_OK( rc))
{
if( RC_OK ( rc = m_pDb->transCommit()))
{
rc = m_pDb->transBegin( XFLM_READ_TRANS);
}
else
{
m_pDb->transAbort();
}
}
else
{
m_pDb->transAbort();
if( RC_BAD( tmpRc = m_pDb->transBegin( XFLM_READ_TRANS)))
{
rc = tmpRc;
}
}
}
if( pDocNode)
{
pDocNode->Release();
}
// Be sure we don't leave it this way.
m_pDb->setKeyCollector( NULL);
if( pKeyColl)
{
pKeyColl->Release();
}
m_Progress.bStartFlag = TRUE;
return( rc);
}
/***************************************************************************
Desc: This routine checks all of the B-TREES in the database -- all
indexes and containers.
*****************************************************************************/
RCODE F_DbCheck::verifyBTrees(
FLMBOOL * pbStartOverRV)
{
RCODE rc = NE_XFLM_OK;
FLMUINT uiCurrLf;
FLMUINT uiCurrLevel;
FLMBYTE * pucKeyBuffer = NULL;
FLMUINT uiKeysAllocated = 0;
STATE_INFO State [BH_MAX_LEVELS];
FLMBOOL bStateInitialized [BH_MAX_LEVELS];
FLMBYTE * pucResetKey = NULL;
FLMUINT uiResetKeyLen = ~(FLMUINT)0;
FLMUINT64 ui64ResetNodeId = 0;
LF_HDR * pLogicalFile;
FLMUINT uiSaveDictSeq;
FLMBOOL bRSFinalized = FALSE;
char szTmpIoPath [F_PATH_MAX_SIZE];
char szBaseName [F_FILENAME_SIZE];
F_NodeVerifier * pNodeVerifier = NULL;
F_BLK_HDR * pBlkHdr = NULL;
F_CachedBlock * pSCache = NULL;
f_memset( State, 0, sizeof( State));
// The StateInfo structs may have pointer to this object,
// but we only need one instance, so do the new here, rather
// than inside the loop where we initialize the StateInfo structs.
if( (pNodeVerifier = f_new F_NodeVerifier) == NULL)
{
rc = RC_SET( NE_XFLM_MEM);
goto Exit;
}
// szTmpIoPath is the directory where the result sets containing
// node data will be stored.
if (RC_BAD( rc = gv_pXFlmDbSystem->getTempDir( szTmpIoPath)))
{
if (rc == NE_FLM_IO_PATH_NOT_FOUND ||
rc == NE_FLM_IO_INVALID_FILENAME)
{
if (RC_BAD( rc = gv_XFlmSysData.pFileSystem->pathReduce(
m_pDb->m_pDatabase->m_pszDbPath, szTmpIoPath, szBaseName)))
{
goto Exit;
}
}
else
{
goto Exit;
}
}
for (uiCurrLevel = 0; uiCurrLevel < BH_MAX_LEVELS; uiCurrLevel++)
{
bStateInitialized [uiCurrLevel] = FALSE;
}
if (*pbStartOverRV)
{
goto Exit;
}
uiSaveDictSeq = m_pDb->m_pDict->getDictSeq();
if (RC_BAD( rc = setupLfTable()))
{
goto Exit;
}
if (m_uiFlags & XFLM_DO_LOGICAL_CHECK)
{
if (RC_BAD( rc = setupIxInfo()))
{
goto Exit;
}
}
// Loop through all of the logical files in the database
// and perform a structural and logical check.
m_pIxd = NULL;
uiCurrLf = 0;
while (uiCurrLf < m_pDbInfo->m_uiNumLogicalFiles)
{
m_Progress.uiCurrLF = uiCurrLf + 1;
pLogicalFile = &m_pDbInfo->m_pLogicalFiles[uiCurrLf];
if (pLogicalFile->eLfType == XFLM_LF_COLLECTION)
{
if (RC_BAD( rc = m_pDb->m_pDict->getCollection( pLogicalFile->uiLfNum,
&m_pCollection, TRUE)))
{
goto Exit;
}
m_pLFile = &m_pCollection->lfInfo;
m_pIxd = NULL;
}
else
{
// If this is our first index, and we are doing a logical check,
// create the index key result set from all of the documents we
// have created.
if (!m_bPhysicalCorrupt &&
(m_uiFlags & XFLM_DO_LOGICAL_CHECK) &&
!bRSFinalized)
{
FLMUINT64 ui64NumRSKeys = 0;
if (RC_BAD( rc = buildIndexKeyList( &ui64NumRSKeys)))
{
if (rc == NE_XFLM_EOF_HIT && ui64NumRSKeys == 0)
{
rc = NE_XFLM_OK;
}
else
{
goto Exit;
}
}
// Reset uiNumKeys to reflect the number of keys
// in the result set now that all duplicates have
// been eliminated.
if (m_Progress.ui64NumKeys > ui64NumRSKeys)
{
m_Progress.ui64NumDuplicateKeys =
m_Progress.ui64NumKeys - ui64NumRSKeys;
}
m_Progress.ui64NumKeys = ui64NumRSKeys;
// Set bRSFinalized to TRUE so that subsequent passes will not
// attempt to finalize the result set again.
bRSFinalized = TRUE;
}
if (RC_BAD( rc = m_pDb->m_pDict->getIndex( pLogicalFile->uiLfNum,
&m_pLFile, &m_pIxd, TRUE)))
{
goto Exit;
}
m_pCollection = NULL;
}
pLogicalFile->uiRootBlk = m_pLFile->uiRootBlk;
flmAssert( m_pLFile->uiRootBlk);
// Allocate space to hold the keys, if not already allocated.
if (uiKeysAllocated < pLogicalFile->uiNumLevels)
{
if (RC_BAD( rc = f_realloc( pLogicalFile->uiNumLevels * XFLM_MAX_KEY_SIZE,
&pucKeyBuffer)))
{
goto Exit;
}
uiKeysAllocated = pLogicalFile->uiNumLevels;
}
// Setup XFLM_PROGRESS_CHECK_INFO structure
m_Progress.iCheckPhase = XFLM_CHECK_B_TREE;
m_Progress.bStartFlag = TRUE;
m_Progress.uiLfNumber = m_pLFile->uiLfNum;
m_Progress.uiLfType = m_pLFile->eLfType;
if (RC_BAD( rc = chkCallProgFunc()))
{
break;
}
m_Progress.bStartFlag = FALSE;
f_yieldCPU();
// Initialize the state information for each level of the B-TREE.
for (uiCurrLevel = 0;
uiCurrLevel < pLogicalFile->uiNumLevels;
uiCurrLevel++)
{
FLMUINT uiExpectedBlkType;
// If we are resetting to a particular key, save the statistics
// which were gathered so far.
if (uiResetKeyLen != ~(FLMUINT)0)
{
// Save the statistics which were gathered.
pLogicalFile->pLevelInfo [uiCurrLevel].ui64KeyCount =
State [uiCurrLevel].ui64KeyCount;
f_memcpy( &pLogicalFile->pLevelInfo [uiCurrLevel].BlockInfo,
&State [uiCurrLevel].BlkInfo,
sizeof( BLOCK_INFO));
}
if (m_pLFile->eLfType == XFLM_LF_INDEX)
{
if (uiCurrLevel == 0)
{
if (m_pIxd->uiNumDataComponents)
{
uiExpectedBlkType = BT_LEAF_DATA;
}
else
{
uiExpectedBlkType = BT_LEAF;
}
}
else if (m_pIxd->uiFlags & IXD_ABS_POS)
{
uiExpectedBlkType = BT_NON_LEAF_COUNTS;
}
else
{
uiExpectedBlkType = BT_NON_LEAF;
}
}
else // collection BTree...
{
if (uiCurrLevel == 0)
{
uiExpectedBlkType = BT_LEAF_DATA;
}
else
{
uiExpectedBlkType = BT_NON_LEAF;
}
}
flmInitReadState( &State [uiCurrLevel],
&bStateInitialized [uiCurrLevel],
(FLMUINT)m_pDb->
m_pDatabase->m_lastCommittedDbHdr.ui32DbVersion,
m_pDb,
pLogicalFile,
uiCurrLevel,
uiExpectedBlkType,
&pucKeyBuffer [uiCurrLevel * XFLM_MAX_KEY_SIZE]);
State[ uiCurrLevel].pCollection = m_pCollection;
State[ uiCurrLevel].uiRootLevel = pLogicalFile->uiNumLevels - 1;
State[ uiCurrLevel].uiCurrLf = uiCurrLf;
if (uiResetKeyLen == ~(FLMUINT)0)
{
State [uiCurrLevel].ui32LastChildAddr = 0;
State [uiCurrLevel].uiElmLastFlag = TRUE;
}
else
{
// Restore the statistics which were gathered so far.
State [uiCurrLevel].ui64KeyCount =
pLogicalFile->pLevelInfo [uiCurrLevel].ui64KeyCount;
f_memcpy( &State [uiCurrLevel].BlkInfo,
&pLogicalFile->pLevelInfo [uiCurrLevel].BlockInfo,
sizeof( BLOCK_INFO));
}
}
if (m_pLFile->eLfType == XFLM_LF_COLLECTION)
{
// Only leaf blocks of collections need a NodeVerifier object
State[0].pNodeVerifier = pNodeVerifier;
// If this is a collection BTree, create a result set to hold the pointer
// information from all the nodes in this btree
if (RC_BAD( rc = getBtResultSet( &State[ 0].pNodeRS)))
{
goto Exit;
}
// The nodeVerifier will setup the Node Result Set etc..
if (pNodeVerifier)
{
pNodeVerifier->setupNodeRS( State[ 0].pNodeRS);
}
}
if ((m_uiFlags & XFLM_DO_LOGICAL_CHECK) &&
State[ 0].pXRefRS == NULL)
{
if (m_pXRefRS == NULL)
{
if (RC_BAD( rc = getBtResultSet( &m_pXRefRS)))
{
goto Exit;
}
}
State[ 0].pXRefRS = m_pXRefRS;
// The nodeVerifier will setup the Node Result Set etc..
if (pNodeVerifier)
{
pNodeVerifier->setupXRefRS( State[ 0].pXRefRS);
}
}
// Call verifySubTree to check the B-TREE starting at the
// root block.
Reset:
rc = verifySubTree( NULL,
&State [pLogicalFile->uiNumLevels - 1],
m_pLFile->uiRootBlk,
&pucResetKey,
uiResetKeyLen,
ui64ResetNodeId);
if (rc == NE_XFLM_RESET_NEEDED || rc == NE_XFLM_OLD_VIEW)
{
FLMUINT uiNumLevels;
if (rc == NE_XFLM_RESET_NEEDED)
{
m_LastStatusRc = NE_XFLM_OK;
}
// If it is a read transaction, reset.
if (m_pDb->getTransType() == XFLM_READ_TRANS)
{
// Free the KrefCntrl
m_pDb->krefCntrlFree();
// Abort the read transaction
if (RC_BAD( rc = m_pDb->transAbort()))
{
goto Exit;
}
// Try to start a new read transaction
if (RC_BAD( rc = m_pDb->transBegin( XFLM_READ_TRANS,
FLM_NO_TIMEOUT,
XFLM_DONT_POISON_CACHE)))
{
goto Exit;
}
}
// If we already have a reset key buffer, we need to free it.
// We will start by repositioning to the root level key we were
// last on.
if (pucResetKey)
{
f_free( &pucResetKey);
}
uiResetKeyLen = State[ pLogicalFile->uiNumLevels - 1].uiElmKeyLen;
ui64ResetNodeId = State[ pLogicalFile->uiNumLevels - 1].ui64ElmNodeId;
if (RC_BAD( rc = f_calloc( uiResetKeyLen + 1, &pucResetKey)))
{
goto Exit;
}
f_memcpy( pucResetKey,
State[ pLogicalFile->uiNumLevels - 1].pucElmKey,
uiResetKeyLen);
// On Reset, we may need to reget the LFILE and IXD.
uiNumLevels = pLogicalFile->uiNumLevels;
if (pLogicalFile->eLfType == XFLM_LF_COLLECTION)
{
if (RC_BAD( rc = m_pDb->m_pDict->getCollection(
pLogicalFile->uiLfNum, &m_pCollection, TRUE)))
{
goto Exit;
}
m_pIxd = NULL;
m_pLFile = &m_pCollection->lfInfo;
}
else
{
if (RC_BAD( rc = m_pDb->m_pDict->getIndex(
pLogicalFile->uiLfNum,
&m_pLFile, &m_pIxd, TRUE)))
{
goto Exit;
}
m_pCollection = NULL;
}
if (RC_BAD( rc = getLfInfo( pLogicalFile, m_pLFile)))
{
goto Exit;
}
if (uiNumLevels != pLogicalFile->uiNumLevels)
{
// Since the block structure of the BTree has been changed, we have
// to begin our scan again from the top. We need to gather stats too.
uiResetKeyLen = ~(FLMUINT)0;
if (pucResetKey)
{
f_free( &pucResetKey);
}
ui64ResetNodeId = 0;
// Initialize the state information for each level of the B-TREE.
for (uiCurrLevel = 0;
uiCurrLevel < pLogicalFile->uiNumLevels;
uiCurrLevel++)
{
FLMUINT uiExpectedBlkType;
// If we are resetting to a particular key, save the statistics
// which were gathered so far.
if (uiResetKeyLen != ~(FLMUINT)0)
{
// Save the statistics which were gathered.
pLogicalFile->pLevelInfo [uiCurrLevel].ui64KeyCount =
State [uiCurrLevel].ui64KeyCount;
f_memcpy( &pLogicalFile->pLevelInfo [uiCurrLevel].BlockInfo,
&State [uiCurrLevel].BlkInfo,
sizeof( BLOCK_INFO));
}
if (m_pLFile->eLfType == XFLM_LF_INDEX)
{
if (uiCurrLevel == 0)
{
if (m_pIxd->uiNumDataComponents)
{
uiExpectedBlkType = BT_LEAF_DATA;
}
else
{
uiExpectedBlkType = BT_LEAF;
}
}
else if (m_pIxd->uiFlags & IXD_ABS_POS)
{
uiExpectedBlkType = BT_NON_LEAF_COUNTS;
}
else
{
uiExpectedBlkType = BT_NON_LEAF;
}
}
else // collection BTree...
{
if (uiCurrLevel == 0)
{
uiExpectedBlkType = BT_LEAF_DATA;
}
else
{
uiExpectedBlkType = BT_NON_LEAF;
}
}
flmInitReadState( &State [uiCurrLevel],
&bStateInitialized [uiCurrLevel],
(FLMUINT)m_pDb->
m_pDatabase->m_lastCommittedDbHdr.ui32DbVersion,
m_pDb,
pLogicalFile,
uiCurrLevel,
uiExpectedBlkType,
&pucKeyBuffer [uiCurrLevel * XFLM_MAX_KEY_SIZE]);
State[ uiCurrLevel].pCollection = m_pCollection;
State[ uiCurrLevel].uiRootLevel = pLogicalFile->uiNumLevels - 1;
State[ uiCurrLevel].uiCurrLf = uiCurrLf;
if (uiResetKeyLen == ~(FLMUINT)0)
{
State [uiCurrLevel].ui32LastChildAddr = 0;
State [uiCurrLevel].uiElmLastFlag = TRUE;
}
else
{
// Restore the statistics which were gathered so far.
State [uiCurrLevel].ui64KeyCount =
pLogicalFile->pLevelInfo [uiCurrLevel].ui64KeyCount;
f_memcpy( &State [uiCurrLevel].BlkInfo,
&pLogicalFile->pLevelInfo [uiCurrLevel].BlockInfo,
sizeof( BLOCK_INFO));
}
}
}
goto Reset;
}
if (RC_BAD( rc))
{
goto Exit;
}
// If this was a collection, then go through the result set and verify
// all of the node pointers...
if (State[ 0].pNodeRS)
{
FLMINT iErrCode;
// Setup the current progress phase
m_Progress.iCheckPhase = XFLM_CHECK_DOM_LINKS;
m_Progress.bStartFlag = TRUE;
m_Progress.uiLfNumber = m_pLFile->uiLfNum;
m_Progress.uiLfType = m_pLFile->eLfType;
if (RC_BAD( rc = chkCallProgFunc()))
{
break;
}
m_Progress.bStartFlag = FALSE;
f_yieldCPU();
m_LastStatusRc = verifyNodePointers( &State[ 0], &iErrCode);
if (iErrCode)
{
chkReportError( iErrCode,
XFLM_LOCALE_B_TREE,
m_Progress.uiLfNumber,
m_Progress.uiLfType,
State[ 0].uiLevel,
m_pLFile->uiBlkAddress,
0,
0,
0);
}
State[ 0].pNodeRS->Release();
State[ 0].pNodeRS = NULL;
if (pNodeVerifier)
{
// Resets the Node verifier RS to NULL.
pNodeVerifier->setupNodeRS( State[ 0].pNodeRS);
}
}
// Verify that all of the levels' next block address's are 0.
if (RC_OK( m_LastStatusRc))
{
for (uiCurrLevel = 0;
uiCurrLevel < pLogicalFile->uiNumLevels;
uiCurrLevel++)
{
// Save the statistics which were gathered.
pLogicalFile->pLevelInfo [uiCurrLevel].ui64KeyCount =
State [uiCurrLevel].ui64KeyCount;
f_memcpy( &pLogicalFile->pLevelInfo [uiCurrLevel].BlockInfo,
&State [uiCurrLevel].BlkInfo, sizeof( BLOCK_INFO));
// Make sure the last block had a NEXT block address of 0.
if (State [uiCurrLevel].ui32NextBlkAddr != 0xFFFFFFFF &&
State [uiCurrLevel].ui32NextBlkAddr != 0)
{
FLMINT iBlkErrCode;
// Verify our finding. Get the block in question and see
// if it realy has a problem.
if (RC_BAD( rc = blkRead( State[ uiCurrLevel].ui32BlkAddress,
&pBlkHdr, &pSCache, &iBlkErrCode)))
{
// Log the error.
if (iBlkErrCode)
{
chkReportError( iBlkErrCode, XFLM_LOCALE_LFH_LIST,
0, 0, 0xFF, State[ uiCurrLevel].ui32BlkAddress,
0, 0, 0);
}
goto Exit;
}
if (pBlkHdr->ui32NextBlkInChain != 0)
{
chkReportError( FLM_BAD_LAST_BLK_NEXT, XFLM_LOCALE_B_TREE,
m_Progress.uiLfNumber, m_Progress.uiLfType, uiCurrLevel,
0, 0, 0, 0);
}
ScaReleaseCache( pSCache, FALSE);
pSCache = NULL;
pBlkHdr = NULL;
}
}
}
if (RC_BAD( m_LastStatusRc))
{
break;
}
// If we are doing a logical index check, need to see if we used up
// all of the keys in the result set for this index.
if (pLogicalFile->eLfType == XFLM_LF_INDEX &&
!m_bPhysicalCorrupt &&
(m_uiFlags & XFLM_DO_LOGICAL_CHECK) &&
bRSFinalized)
{
for (;;)
{
if (RC_BAD( rc = chkGetNextRSKey()))
{
if (rc == NE_XFLM_EOF_HIT || rc == NE_XFLM_NOT_FOUND)
{
rc = NE_XFLM_OK;
break;
}
goto Exit;
}
else
{
// Updated statistics
m_Progress.ui64NumKeysExamined++;
if (RC_BAD( rc = resolveIXMissingKey( &(State[ 0]))))
{
goto Exit;
}
}
}
}
uiCurrLf++;
if (pucResetKey)
{
f_free( &pucResetKey);
}
pucResetKey = NULL;
uiResetKeyLen = ~(FLMUINT)0;
ui64ResetNodeId = 0;
}
Exit:
if (pSCache)
{
ScaReleaseCache( pSCache, FALSE);
}
else if (pBlkHdr)
{
f_free( &pBlkHdr);
}
if (pucKeyBuffer)
{
f_free( &pucKeyBuffer);
}
if (pucResetKey)
{
f_free( &pucResetKey);
}
if (State[ 0].pNodeRS)
{
State[ 0].pNodeRS->Release();
State[ 0].pNodeRS = NULL;
if (pNodeVerifier)
{
pNodeVerifier->setupNodeRS( NULL);
}
}
// Clean up the NodeVerifier instance...
if (pNodeVerifier)
{
pNodeVerifier->Release();
}
// Cleanup any temporary index check files
if (m_pIxRSet)
{
m_pIxRSet->Release();
m_pIxRSet = NULL;
}
if (m_puiIxArray)
{
f_free( &m_puiIxArray);
}
if (RC_OK( rc) && RC_BAD( m_LastStatusRc))
{
rc = m_LastStatusRc;
}
return( rc);
}
/***************************************************************************
Desc: This routine allocates and initializes the LF table (array of
LF_HDR structures).
*****************************************************************************/
RCODE F_DbCheck::setupLfTable()
{
RCODE rc = NE_XFLM_OK;
FLMUINT uiLfHdrOffset;
IXD * pIxd;
F_COLLECTION * pCollection;
F_Dict * pDict = m_pDb->m_pDict;
FLMUINT uiIndexNum;
FLMUINT uiCollectionNum;
// Set up the table such that the dictionary is checked first,
// followed by data containers, and then indexes. This is
// necessary for the logical (index) check to work. The
// data records must be extracted before the indexes are
// checked so that the temporary result set, used during
// the logical check, can be built.
m_pDbInfo->freeLogicalFiles();
m_Progress.uiNumLFs = 0;
if (pDict)
{
// Count the indexes.
uiIndexNum = 0;
for (;;)
{
if ((pIxd = pDict->getNextIndex( uiIndexNum, TRUE)) == NULL)
{
break;
}
uiIndexNum = pIxd->uiIndexNum;
m_pDbInfo->m_uiNumIndexes++;
}
// Count the collections
uiCollectionNum = 0;
for (;;)
{
if ((pCollection = pDict->getNextCollection( uiCollectionNum,
TRUE)) == NULL)
{
break;
}
uiCollectionNum = pCollection->lfInfo.uiLfNum;
m_pDbInfo->m_uiNumCollections++;
}
m_pDbInfo->m_uiNumLogicalFiles = m_pDbInfo->m_uiNumIndexes +
m_pDbInfo->m_uiNumCollections;
m_Progress.uiNumLFs = m_pDbInfo->m_uiNumLogicalFiles;
// Allocate memory for each collection and index, then set up each
// collection and index
if (RC_BAD( rc = f_calloc( (FLMUINT)(sizeof( LF_HDR) *
m_pDbInfo->m_uiNumLogicalFiles),
(void **)&m_pDbInfo->m_pLogicalFiles)))
{
goto Exit;
}
uiLfHdrOffset = 0;
// Add in our dictionary collection first. Do field
// definitions first, then collection definitions, then index
// definitions.
if (RC_BAD( rc = pDict->getCollection( XFLM_DICT_COLLECTION, &pCollection)))
{
goto Exit;
}
if (RC_BAD( rc = getLfInfo( &m_pDbInfo->m_pLogicalFiles [uiLfHdrOffset],
&pCollection->lfInfo)))
{
goto Exit;
}
uiLfHdrOffset++;
// Add in default data collection
if (RC_BAD( rc = pDict->getCollection( XFLM_DATA_COLLECTION, &pCollection)))
{
goto Exit;
}
if (RC_BAD( rc = getLfInfo( &m_pDbInfo->m_pLogicalFiles [uiLfHdrOffset],
&pCollection->lfInfo)))
{
goto Exit;
}
uiLfHdrOffset++;
// Add in the maintenance collection
if (RC_BAD( rc = pDict->getCollection( XFLM_MAINT_COLLECTION, &pCollection)))
{
goto Exit;
}
if (RC_BAD( rc = getLfInfo( &m_pDbInfo->m_pLogicalFiles [uiLfHdrOffset],
&pCollection->lfInfo)))
{
goto Exit;
}
uiLfHdrOffset++;
// Add in user defined collections
uiCollectionNum = 0;
for (;;)
{
if ((pCollection = pDict->getNextCollection( uiCollectionNum,
FALSE)) == NULL)
{
break;
}
uiCollectionNum = pCollection->lfInfo.uiLfNum;
if (RC_BAD( rc = getLfInfo( &m_pDbInfo->m_pLogicalFiles [uiLfHdrOffset],
&pCollection->lfInfo)))
{
goto Exit;
}
uiLfHdrOffset++;
}
// Indexes need to be in order from lowest to highest
// because the result set is ordered that way.
uiIndexNum = 0;
for (;;)
{
if ((pIxd = pDict->getNextIndex( uiIndexNum, TRUE)) == NULL)
{
break;
}
uiIndexNum = pIxd->uiIndexNum;
if (RC_BAD( rc = getLfInfo( &m_pDbInfo->m_pLogicalFiles [uiLfHdrOffset],
&pIxd->lfInfo)))
{
goto Exit;
}
uiLfHdrOffset++;
}
}
Exit:
return( rc);
}
/***************************************************************************
Desc: Initializes index checking information.
*****************************************************************************/
RCODE F_DbCheck::setupIxInfo( void)
{
RCODE rc = NE_XFLM_OK;
LF_HDR * pLogicalFile;
FLMUINT uiLoop;
FLMUINT uiIxCount;
// Initialize the result set. The result set will be used
// to build an ordered list of keys for comparison to
// the database's indexes.
if (RC_BAD( rc = getBtResultSet( &m_pIxRSet)))
{
goto Exit;
}
// Build list of all indexes
if (m_pDbInfo->m_uiNumIndexes)
{
// Allocate memory to save each index number
if (RC_BAD( rc = f_alloc(
(FLMUINT)(sizeof( FLMUINT) * m_pDbInfo->m_uiNumIndexes),
&m_puiIxArray)))
{
goto Exit;
}
// Save the index numbers into the array.
uiIxCount = 0;
pLogicalFile = m_pDbInfo->m_pLogicalFiles;
for( uiLoop = 0;
uiLoop < m_pDbInfo->m_uiNumLogicalFiles;
uiLoop++, pLogicalFile++)
{
if (pLogicalFile->eLfType == XFLM_LF_INDEX)
{
m_puiIxArray[ uiIxCount] = pLogicalFile->uiLfNum;
uiIxCount++;
}
}
}
m_bGetNextRSKey = TRUE;
Exit:
// Clean up any memory on error exit.
if (RC_BAD( rc))
{
if (m_pIxRSet)
{
m_pIxRSet->Release();
m_pIxRSet = NULL;
}
if (m_puiIxArray)
{
f_free( &m_puiIxArray);
}
}
return( rc);
}
/***************************************************************************
Desc: This routine reads the LFH areas from disk to make sure they are up
to date in memory.
*****************************************************************************/
RCODE F_DbCheck::getLfInfo(
LF_HDR * pLogicalFile,
LFILE * pLFile
)
{
RCODE rc = NE_XFLM_OK;
F_CachedBlock * pSCache = NULL;
F_BLK_HDR * pBlkHdr = NULL;
FLMUINT uiSaveLevel;
FLMINT iBlkErrCode;
pLogicalFile->eLfType = pLFile->eLfType;
pLogicalFile->uiLfNum = pLFile->uiLfNum;
pLogicalFile->uiRootBlk = pLFile->uiRootBlk;
// Read in the block containing the logical file header.
if (RC_BAD( rc = blkRead( pLFile->uiBlkAddress,
&pBlkHdr, &pSCache, &iBlkErrCode)))
{
// Log the error.
if (iBlkErrCode)
{
chkReportError( iBlkErrCode,
XFLM_LOCALE_LFH_LIST,
0,
0,
0xFF,
pLFile->uiBlkAddress,
0,
0,
0);
}
goto Exit;
}
uiSaveLevel = pLogicalFile->uiNumLevels;
// Read root block to get the number of levels in the B-TREE
flmAssert( pLFile->uiRootBlk);
if (RC_BAD( rc = blkRead( pLFile->uiRootBlk,
&pBlkHdr,
&pSCache,
&iBlkErrCode)))
{
if (iBlkErrCode)
{
chkReportError( iBlkErrCode,
XFLM_LOCALE_B_TREE,
pLFile->uiLfNum,
pLFile->eLfType,
0xFF,
pLFile->uiRootBlk,
0,
0,
0);
}
goto Exit;
}
pLogicalFile->uiNumLevels =
(FLMUINT)(((F_BTREE_BLK_HDR *)pBlkHdr)->ui8BlkLevel) + 1;
// Need to make sure that the level extracted from
// the block is valid.
if (((F_BTREE_BLK_HDR *)pBlkHdr)->ui8BlkLevel >= BH_MAX_LEVELS)
{
chkReportError( FLM_BAD_BLK_HDR_LEVEL,
XFLM_LOCALE_B_TREE,
pLFile->uiLfNum,
pLFile->eLfType,
(FLMUINT)(((F_BTREE_BLK_HDR *)pBlkHdr)->ui8BlkLevel),
pLFile->uiRootBlk,
0,
0,
0);
// Force pLogicalFile->uiNumLevels to 1 so that we don't crash
pLogicalFile->uiNumLevels = 1;
}
// If the number of levels changed, reset the level information
// on this logical file.
if (uiSaveLevel != pLogicalFile->uiNumLevels &&
pLogicalFile->uiNumLevels)
{
if (pLogicalFile->uiNumLevels > uiSaveLevel)
{
if ( pLogicalFile->pLevelInfo)
{
f_free( &pLogicalFile->pLevelInfo);
}
if (RC_BAD( rc = f_calloc(
(sizeof( LEVEL_INFO) * pLogicalFile->uiNumLevels),
(void **)&pLogicalFile->pLevelInfo)))
{
goto Exit;
}
}
}
Exit:
if (pSCache)
{
ScaReleaseCache( pSCache, FALSE);
}
else if (pBlkHdr)
{
f_free( &pBlkHdr);
}
return( rc);
}
/***************************************************************************
Desc: Goes throught the (finalized) result set and validates that all the
node pointers point to the right nodes
*****************************************************************************/
RCODE F_DbCheck::verifyNodePointers(
STATE_INFO * pStateInfo,
FLMINT * piErrCode
)
{
RCODE rc = NE_XFLM_OK;
NODE_RS_ENTRY * pRSEntry = NULL;
NODE_RS_ENTRY * pTmpRSEntry = NULL;
F_BtResultSet * pResult = pStateInfo->pNodeRS;
FLMUINT uiRSEntrySize = sizeof( NODE_RS_ENTRY);
FLMBOOL bFirst = TRUE;
FLMBYTE pucKey[ XFLM_MAX_KEY_SIZE];
FLMUINT uiKeyLength = XFLM_MAX_KEY_SIZE;
F_Btree * pBTree = NULL;
FLMINT iErrCode = 0;
*piErrCode = 0;
if (RC_BAD( rc = f_calloc( sizeof( NODE_RS_ENTRY), &pRSEntry)))
{
goto Exit;
}
if (RC_BAD( rc = f_calloc( sizeof( NODE_RS_ENTRY), &pTmpRSEntry)))
{
goto Exit;
}
for (;;)
{
m_Progress.ui64NumDomNodes++;;
if (bFirst)
{
if (RC_BAD( rc = pResult->getBTree( NULL, NULL, &pBTree)))
{
goto Exit;
}
if (RC_BAD( rc = pResult->getFirst( NULL, NULL, pBTree,
pucKey,
XFLM_MAX_KEY_SIZE,
&uiKeyLength,
(FLMBYTE *)pRSEntry,
sizeof( NODE_RS_ENTRY),
&uiRSEntrySize)))
{
if (rc == NE_XFLM_EOF_HIT || rc == NE_XFLM_BOF_HIT)
{
rc = NE_XFLM_OK;
break;
}
goto Exit;
}
}
else
{
if (RC_BAD( rc = pResult->getNext( NULL, NULL, pBTree,
pucKey,
XFLM_MAX_KEY_SIZE,
&uiKeyLength,
(FLMBYTE *)pRSEntry,
sizeof( NODE_RS_ENTRY),
&uiRSEntrySize)))
{
if (rc == NE_XFLM_EOF_HIT || rc == NE_XFLM_BOF_HIT)
{
rc = NE_XFLM_OK;
break;
}
goto Exit;
}
}
bFirst = FALSE;
if (RC_BAD( rc = verifyRootLink(
pRSEntry, uiRSEntrySize, pTmpRSEntry, pResult, &iErrCode)))
{
goto Exit;
}
if (iErrCode)
{
chkReportError( iErrCode, XFLM_LOCALE_B_TREE, m_Progress.uiLfNumber,
m_Progress.uiLfType, 0, 0, 0, (FLMUINT)~(0),
pRSEntry->hdr.ui64NodeId);
iErrCode = 0;
m_Progress.ui64NumBrokenDomLinks++;
}
else
{
m_Progress.ui64NumDomLinksVerified++;
}
if (RC_BAD( rc = verifyParentLink(
pRSEntry, pTmpRSEntry, pResult, &iErrCode)))
{
goto Exit;
}
if (iErrCode)
{
chkReportError( iErrCode, XFLM_LOCALE_B_TREE, m_Progress.uiLfNumber,
m_Progress.uiLfType, 0, 0, 0, (FLMUINT)~(0),
pRSEntry->hdr.ui64NodeId);
iErrCode = 0;
m_Progress.ui64NumBrokenDomLinks++;
}
else
{
m_Progress.ui64NumDomLinksVerified++;
}
if( RC_BAD( rc = verifyFirstChildLink( pRSEntry, pTmpRSEntry,
pResult, &iErrCode)))
{
goto Exit;
}
if( iErrCode)
{
chkReportError( iErrCode, XFLM_LOCALE_B_TREE, m_Progress.uiLfNumber,
m_Progress.uiLfType, 0, 0, 0, (FLMUINT)~(0),
pRSEntry->hdr.ui64NodeId);
iErrCode = 0;
m_Progress.ui64NumBrokenDomLinks++;
}
else
{
m_Progress.ui64NumDomLinksVerified++;
}
if (RC_BAD( rc = verifyLastChildLink(
pRSEntry, pTmpRSEntry, pResult, &iErrCode)))
{
goto Exit;
}
if (iErrCode)
{
chkReportError( iErrCode, XFLM_LOCALE_B_TREE, m_Progress.uiLfNumber,
m_Progress.uiLfType, 0, 0, 0, (FLMUINT)~(0),
pRSEntry->hdr.ui64NodeId);
iErrCode = 0;
m_Progress.ui64NumBrokenDomLinks++;
}
else
{
m_Progress.ui64NumDomLinksVerified++;
}
if (RC_BAD( rc = verifyPrevSiblingLink(
pRSEntry, pTmpRSEntry, pResult, &iErrCode)))
{
goto Exit;
}
if (iErrCode)
{
chkReportError( iErrCode, XFLM_LOCALE_B_TREE, m_Progress.uiLfNumber,
m_Progress.uiLfType, 0, 0, 0, (FLMUINT)~(0),
pRSEntry->hdr.ui64NodeId);
iErrCode = 0;
m_Progress.ui64NumBrokenDomLinks++;
}
else
{
m_Progress.ui64NumDomLinksVerified++;
}
if (RC_BAD( rc = verifyNextSiblingLink(
pRSEntry, pTmpRSEntry, pResult, &iErrCode)))
{
goto Exit;
}
if (iErrCode)
{
chkReportError( iErrCode, XFLM_LOCALE_B_TREE, m_Progress.uiLfNumber,
m_Progress.uiLfType, 0, 0, 0, (FLMUINT)~(0),
pRSEntry->hdr.ui64NodeId);
iErrCode = 0;
m_Progress.ui64NumBrokenDomLinks++;
}
else
{
m_Progress.ui64NumDomLinksVerified++;
}
if (RC_BAD( rc = verifyAnnotationLink(
pRSEntry, pTmpRSEntry, pResult, &iErrCode)))
{
goto Exit;
}
if (iErrCode)
{
chkReportError( iErrCode, XFLM_LOCALE_B_TREE, m_Progress.uiLfNumber,
m_Progress.uiLfType, 0, 0, 0, (FLMUINT)~(0),
pRSEntry->hdr.ui64NodeId);
iErrCode = 0;
m_Progress.ui64NumBrokenDomLinks++;
}
else
{
m_Progress.ui64NumDomLinksVerified++;
}
if (RC_BAD( rc = chkCallProgFunc()))
{
break;
}
f_yieldCPU();
}
Exit:
if (pBTree)
{
pResult->freeBTree( &pBTree);
}
if (pRSEntry)
{
f_free( &pRSEntry);
}
if (pTmpRSEntry)
{
f_free( &pTmpRSEntry);
}
return rc;
}
/********************************************************************
Desc: Verify that the Root Id field points to a valid node.
********************************************************************/
FSTATIC RCODE verifyRootLink(
NODE_RS_ENTRY * pRSEntry,
FLMUINT uiRSEntrySize,
NODE_RS_ENTRY * pTmpRSEntry,
F_BtResultSet * pResult,
FLMINT * piErrCode
)
{
RCODE rc = NE_XFLM_OK;
FLMUINT64 ui64RootId = getLinkVal( CHK_BM_ROOT_ID, pRSEntry);
FLMUINT uiTmpRSEntrySize;
FLMUINT uiKeySize = sizeof( FLMUINT64);
f_memset( pTmpRSEntry, 0, sizeof(NODE_RS_ENTRY));
if (!ui64RootId)
{
// Returns NE_XFLM_OK
goto Exit;
}
if( ui64RootId != pRSEntry->hdr.ui64NodeId)
{
pTmpRSEntry->hdr.ui64NodeId = ui64RootId;
if (RC_BAD( rc = pResult->findEntry( NULL, NULL,
(FLMBYTE *)&(pTmpRSEntry->hdr.ui64NodeId), sizeof( FLMUINT64),
&uiKeySize, (FLMBYTE *)pTmpRSEntry, sizeof( NODE_RS_ENTRY),
&uiTmpRSEntrySize)))
{
*piErrCode = FLM_BAD_ROOT_LINK;
goto Exit;
}
// Found it!
// Make sure this Root is a "True" root node.
// Cannot have a parent node
if (pTmpRSEntry->hdr.ui16BitMap & CHK_BM_PARENT_ID)
{
*piErrCode = FLM_BAD_ROOT_PARENT;
goto Exit;
}
// Cannot be another node child, or attribute or annotation node.
if (pTmpRSEntry->hdr.ui16Flags & CHK_FIRST_CHILD_VERIFIED ||
pTmpRSEntry->hdr.ui16Flags & CHK_LAST_CHILD_VERIFIED ||
pTmpRSEntry->hdr.ui16Flags & CHK_ANNOTATION_VERIFIED)
{
*piErrCode = FLM_BAD_ROOT_LINK;
goto Exit;
}
pTmpRSEntry->hdr.ui16Flags |= CHK_ROOT_VERIFIED;
if (RC_BAD( rc = pResult->modifyEntry( NULL, NULL,
(FLMBYTE *)&(pTmpRSEntry->hdr.ui64NodeId), sizeof( FLMUINT64),
(FLMBYTE *)pTmpRSEntry, uiTmpRSEntrySize)))
{
goto Exit;
}
}
else
{
// If the node we were passed IS the root node...
// Cannot have a parent node
if (pRSEntry->hdr.ui16BitMap & CHK_BM_PARENT_ID)
{
*piErrCode = FLM_BAD_ROOT_LINK;
goto Exit;
}
// Cannot be another node child, or attribute or annotation node.
if (pRSEntry->hdr.ui16Flags & CHK_FIRST_CHILD_VERIFIED ||
pRSEntry->hdr.ui16Flags & CHK_LAST_CHILD_VERIFIED ||
pRSEntry->hdr.ui16Flags & CHK_ANNOTATION_VERIFIED)
{
*piErrCode = FLM_BAD_ROOT_LINK;
goto Exit;
}
pRSEntry->hdr.ui16Flags |= CHK_ROOT_VERIFIED;
if (RC_BAD( rc = pResult->modifyEntry( NULL, NULL,
(FLMBYTE *)&(pRSEntry->hdr.ui64NodeId), sizeof( FLMUINT64),
(FLMBYTE *)pRSEntry, uiRSEntrySize)))
{
goto Exit;
}
}
Exit:
return rc;
}
/********************************************************************
Desc: Verify that the parent Id field points to a valid node.
********************************************************************/
FSTATIC RCODE verifyParentLink(
NODE_RS_ENTRY * pRSEntry,
NODE_RS_ENTRY * pTmpRSEntry,
F_BtResultSet * pResult,
FLMINT * piErrCode
)
{
RCODE rc = NE_XFLM_OK;
FLMUINT64 ui64ParentId = getLinkVal( CHK_BM_PARENT_ID, pRSEntry);
FLMUINT64 ui64RootId = getLinkVal( CHK_BM_ROOT_ID, pRSEntry);
FLMUINT uiTmpRSEntrySize;
FLMUINT64 ui64TmpRootId;
FLMUINT uiKeySize = sizeof( FLMUINT64);
f_memset( pTmpRSEntry, 0, sizeof(NODE_RS_ENTRY));
if (!ui64ParentId)
{
// With no parent node, this node cannot have any child
// or attribute or annotation nodes.
if (pRSEntry->hdr.ui16Flags & CHK_FIRST_CHILD_VERIFIED ||
pRSEntry->hdr.ui16Flags & CHK_LAST_CHILD_VERIFIED ||
pRSEntry->hdr.ui16Flags & CHK_ANNOTATION_VERIFIED)
{
*piErrCode = FLM_BAD_PARENT_LINK;
}
goto Exit;
}
if (ui64ParentId == pRSEntry->hdr.ui64NodeId)
{
*piErrCode = FLM_BAD_PARENT_LINK;
goto Exit;
}
pTmpRSEntry->hdr.ui64NodeId = ui64ParentId;
if (RC_BAD( rc = pResult->findEntry( NULL, NULL,
(FLMBYTE *)&(pTmpRSEntry->hdr.ui64NodeId), sizeof( FLMUINT64),
&uiKeySize, (FLMBYTE *)pTmpRSEntry, sizeof( NODE_RS_ENTRY),
&uiTmpRSEntrySize)))
{
*piErrCode = FLM_BAD_PARENT_LINK;
goto Exit;
}
// Verify that they are in the same document...
ui64TmpRootId = getLinkVal( CHK_BM_ROOT_ID, pTmpRSEntry);
if (ui64TmpRootId)
{
if (ui64RootId != ui64TmpRootId)
{
*piErrCode = FLM_BAD_PARENT_LINK;
goto Exit;
}
}
else
{
if (ui64ParentId != ui64RootId)
{
*piErrCode = FLM_BAD_PARENT_LINK;
goto Exit;
}
}
pTmpRSEntry->hdr.ui16Flags |= CHK_PARENT_VERIFIED;
if( RC_BAD( rc = pResult->modifyEntry( NULL, NULL,
(FLMBYTE *)&(pTmpRSEntry->hdr.ui64NodeId), sizeof( FLMUINT64),
(FLMBYTE *)pTmpRSEntry, uiTmpRSEntrySize)))
{
goto Exit;
}
Exit:
return( rc);
}
/********************************************************************
Desc: Verify that the First Child link points to a vaild node.
********************************************************************/
FSTATIC RCODE verifyFirstChildLink(
NODE_RS_ENTRY * pRSEntry,
NODE_RS_ENTRY * pTmpRSEntry,
F_BtResultSet * pResult,
FLMINT * piErrCode)
{
RCODE rc = NE_XFLM_OK;
FLMUINT64 ui64FirstChildId = getLinkVal( CHK_BM_FIRST_CHILD, pRSEntry);
FLMUINT64 ui64RootId = getLinkVal( CHK_BM_ROOT_ID, pRSEntry);
FLMUINT uiTmpRSEntrySize;
FLMUINT64 ui64TmpRootId;
FLMUINT uiKeySize = sizeof( FLMUINT64);
f_memset( pTmpRSEntry, 0, sizeof(NODE_RS_ENTRY));
if (!ui64FirstChildId)
{
// Better not have a last child either.
if (getLinkVal( CHK_BM_LAST_CHILD, pRSEntry))
{
*piErrCode = FLM_BAD_FIRST_CHILD_LINK;
}
if (pRSEntry->hdr.ui16Flags & CHK_PARENT_VERIFIED)
{
if( !getLinkVal( CHK_BM_ANNOTATION, pRSEntry))
{
*piErrCode = FLM_BAD_FIRST_CHILD_LINK;
}
}
goto Exit;
}
if (ui64FirstChildId == pRSEntry->hdr.ui64NodeId)
{
*piErrCode = FLM_BAD_FIRST_CHILD_LINK;
goto Exit;
}
pTmpRSEntry->hdr.ui64NodeId = ui64FirstChildId;
if (RC_BAD( rc = pResult->findEntry( NULL, NULL,
(FLMBYTE *)&(pTmpRSEntry->hdr.ui64NodeId), sizeof( FLMUINT64),
&uiKeySize, (FLMBYTE *)pTmpRSEntry, sizeof( NODE_RS_ENTRY),
&uiTmpRSEntrySize)))
{
*piErrCode = FLM_BAD_FIRST_CHILD_LINK;
goto Exit;
}
// Must belong to the same document / root
ui64TmpRootId = getLinkVal( CHK_BM_ROOT_ID, pTmpRSEntry);
if (ui64RootId)
{
if (ui64RootId != ui64TmpRootId)
{
*piErrCode = FLM_BAD_FIRST_CHILD_LINK;
goto Exit;
}
}
else
{
if (ui64TmpRootId != pRSEntry->hdr.ui64NodeId)
{
*piErrCode = FLM_BAD_FIRST_CHILD_LINK;
goto Exit;
}
}
// Make sure this child has not been visited as a first child already.
if (pTmpRSEntry->hdr.ui16Flags & CHK_FIRST_CHILD_VERIFIED)
{
*piErrCode = FLM_BAD_FIRST_CHILD_LINK;
goto Exit;
}
// Does this child reference the correct parent?
if (getLinkVal( CHK_BM_PARENT_ID, pTmpRSEntry) != pRSEntry->hdr.ui64NodeId)
{
*piErrCode = FLM_BAD_FIRST_CHILD_LINK;
goto Exit;
}
// Mark the child as visited as a first child.
pTmpRSEntry->hdr.ui16Flags |= CHK_FIRST_CHILD_VERIFIED;
if (RC_BAD( rc = pResult->modifyEntry( NULL, NULL,
(FLMBYTE *)&(pTmpRSEntry->hdr.ui64NodeId),
sizeof( FLMUINT64),
(FLMBYTE *)pTmpRSEntry,
uiTmpRSEntrySize)))
{
goto Exit;
}
Exit:
return rc;
}
/********************************************************************
Desc: Verify that the Last Child link points to a vaild node.
********************************************************************/
FSTATIC RCODE verifyLastChildLink(
NODE_RS_ENTRY * pRSEntry,
NODE_RS_ENTRY * pTmpRSEntry,
F_BtResultSet * pResult,
FLMINT * piErrCode
)
{
RCODE rc = NE_XFLM_OK;
FLMUINT64 ui64LastChildId = getLinkVal( CHK_BM_LAST_CHILD, pRSEntry);
FLMUINT64 ui64RootId = getLinkVal( CHK_BM_ROOT_ID, pRSEntry);
FLMUINT uiTmpRSEntrySize;
FLMUINT64 ui64TmpRootId;
FLMUINT uiKeySize = sizeof( FLMUINT64);
f_memset( pTmpRSEntry, 0, sizeof(NODE_RS_ENTRY));
if (!ui64LastChildId)
{
// Better not have a first child either.
if (getLinkVal( CHK_BM_FIRST_CHILD, pRSEntry))
{
*piErrCode = FLM_BAD_LAST_CHILD_LINK;
}
if (pRSEntry->hdr.ui16Flags & CHK_PARENT_VERIFIED)
{
if( !getLinkVal( CHK_BM_ANNOTATION, pRSEntry))
{
*piErrCode = FLM_BAD_FIRST_CHILD_LINK;
}
}
goto Exit;
}
if (ui64LastChildId == pRSEntry->hdr.ui64NodeId)
{
*piErrCode = FLM_BAD_LAST_CHILD_LINK;
goto Exit;
}
pTmpRSEntry->hdr.ui64NodeId = ui64LastChildId;
if( RC_BAD( rc = pResult->findEntry( NULL, NULL,
(FLMBYTE *)&(pTmpRSEntry->hdr.ui64NodeId), sizeof( FLMUINT64),
&uiKeySize, (FLMBYTE *)pTmpRSEntry, sizeof( NODE_RS_ENTRY),
&uiTmpRSEntrySize)))
{
*piErrCode = FLM_BAD_LAST_CHILD_LINK;
goto Exit;
}
// Must belong to the same document / root
ui64TmpRootId = getLinkVal( CHK_BM_ROOT_ID, pTmpRSEntry);
if (ui64RootId)
{
if (ui64RootId != ui64TmpRootId)
{
*piErrCode = FLM_BAD_LAST_CHILD_LINK;
goto Exit;
}
}
else
{
if (ui64TmpRootId != pRSEntry->hdr.ui64NodeId)
{
*piErrCode = FLM_BAD_LAST_CHILD_LINK;
goto Exit;
}
}
// Make sure this child has not been visited as a last child already.
if (pTmpRSEntry->hdr.ui16Flags & CHK_LAST_CHILD_VERIFIED)
{
*piErrCode = FLM_BAD_LAST_CHILD_LINK;
goto Exit;
}
// Does this child reference the correct parent?
if (getLinkVal( CHK_BM_PARENT_ID, pTmpRSEntry) != pRSEntry->hdr.ui64NodeId)
{
*piErrCode = FLM_BAD_LAST_CHILD_LINK;
goto Exit;
}
// Mark the child as visited as a last child.
pTmpRSEntry->hdr.ui16Flags |= CHK_LAST_CHILD_VERIFIED;
if (RC_BAD( rc = pResult->modifyEntry( NULL, NULL,
(FLMBYTE *)&(pTmpRSEntry->hdr.ui64NodeId),
sizeof( FLMUINT64),
(FLMBYTE *)pTmpRSEntry,
uiTmpRSEntrySize)))
{
goto Exit;
}
Exit:
return rc;
}
/********************************************************************
Desc: Verify that the Prev Sibling link points to a valid node.
********************************************************************/
FSTATIC RCODE verifyPrevSiblingLink(
NODE_RS_ENTRY * pRSEntry,
NODE_RS_ENTRY * pTmpRSEntry,
F_BtResultSet * pResult,
FLMINT * piErrCode
)
{
RCODE rc = NE_XFLM_OK;
FLMUINT64 ui64PrevSibling = getLinkVal( CHK_BM_PREV_SIBLING, pRSEntry);
FLMUINT64 ui64RootId = getLinkVal( CHK_BM_ROOT_ID, pRSEntry);
FLMUINT64 ui64ParentId = getLinkVal( CHK_BM_PARENT_ID, pRSEntry);
FLMUINT uiTmpRSEntrySize;
FLMUINT uiKeySize = sizeof( FLMUINT64);
f_memset( pTmpRSEntry, 0, sizeof(NODE_RS_ENTRY));
if (!ui64PrevSibling)
{
// Should not be a Next Sibling to anyone.
if (pRSEntry->hdr.ui16Flags & CHK_NEXT_SIBLING_VERIFIED)
{
*piErrCode = FLM_BAD_PREV_SIBLING_LINK;
}
// Must also verify that this node is the first child of the parent node
// - if there is a parent.
if (ui64ParentId)
{
FLMUINT64 ui64FirstChild;
pTmpRSEntry->hdr.ui64NodeId = ui64ParentId;
if( RC_BAD( rc = pResult->findEntry( NULL, NULL,
(FLMBYTE *)&(pTmpRSEntry->hdr.ui64NodeId), sizeof( FLMUINT64),
&uiKeySize, (FLMBYTE *)pTmpRSEntry, sizeof( NODE_RS_ENTRY),
&uiTmpRSEntrySize)))
{
*piErrCode = FLM_BAD_PARENT_LINK;
goto Exit;
}
ui64FirstChild = getLinkVal( CHK_BM_FIRST_CHILD, pTmpRSEntry);
if (ui64FirstChild != pRSEntry->hdr.ui64NodeId)
{
FLMUINT64 ui64Annot;
// It may be an annotation Node.
ui64Annot = getLinkVal( CHK_BM_ANNOTATION, pTmpRSEntry);
if (ui64Annot != pRSEntry->hdr.ui64NodeId)
{
*piErrCode = FLM_BAD_PREV_SIBLING_LINK;
goto Exit;
}
}
}
goto Exit;
}
pTmpRSEntry->hdr.ui64NodeId = ui64PrevSibling;
if( RC_BAD( rc = pResult->findEntry( NULL, NULL,
(FLMBYTE *)&(pTmpRSEntry->hdr.ui64NodeId), sizeof( FLMUINT64),
&uiKeySize, (FLMBYTE *)pTmpRSEntry, sizeof( NODE_RS_ENTRY),
&uiTmpRSEntrySize)))
{
*piErrCode = FLM_BAD_PREV_SIBLING_LINK;
goto Exit;
}
// Must belong to the same document unless both nodes are
// document roots
if( ui64RootId != getLinkVal( CHK_BM_ROOT_ID, pTmpRSEntry))
{
if( ui64ParentId || getLinkVal( CHK_BM_PARENT_ID, pTmpRSEntry))
{
*piErrCode = FLM_BAD_PREV_SIBLING_LINK;
goto Exit;
}
}
// The previous sibling should not have been visited as a previous
// sibling before now.
if (pTmpRSEntry->hdr.ui16Flags & CHK_PREV_SIBLING_VERIFIED)
{
*piErrCode = FLM_BAD_PREV_SIBLING_LINK;
goto Exit;
}
// Should point to "this" node.
if (getLinkVal( CHK_BM_NEXT_SIBLING, pTmpRSEntry) != pRSEntry->hdr.ui64NodeId)
{
*piErrCode = FLM_BAD_PREV_SIBLING_LINK;
goto Exit;
}
// Mark the previous sibling as being visited as such.
pTmpRSEntry->hdr.ui16Flags |= CHK_PREV_SIBLING_VERIFIED;
if (RC_BAD( rc = pResult->modifyEntry( NULL, NULL,
(FLMBYTE *)&(pTmpRSEntry->hdr.ui64NodeId),
sizeof( FLMUINT64),
(FLMBYTE *)pTmpRSEntry,
uiTmpRSEntrySize)))
{
goto Exit;
}
Exit:
return rc;
}
/********************************************************************
Desc: Verify that the Next Sibling link points to a valid node.
********************************************************************/
FSTATIC RCODE verifyNextSiblingLink(
NODE_RS_ENTRY * pRSEntry,
NODE_RS_ENTRY * pTmpRSEntry,
F_BtResultSet * pResult,
FLMINT * piErrCode
)
{
RCODE rc = NE_XFLM_OK;
FLMUINT64 ui64NextSibling = getLinkVal( CHK_BM_NEXT_SIBLING, pRSEntry);
FLMUINT64 ui64RootId = getLinkVal( CHK_BM_ROOT_ID, pRSEntry);
FLMUINT64 ui64ParentId = getLinkVal( CHK_BM_PARENT_ID, pRSEntry);
FLMUINT uiTmpRSEntrySize;
FLMUINT uiKeySize = sizeof( FLMUINT64);
f_memset( pTmpRSEntry, 0, sizeof(NODE_RS_ENTRY));
if (!ui64NextSibling)
{
// Should not be a Prev Sibling to anyone.
if (pRSEntry->hdr.ui16Flags & CHK_PREV_SIBLING_VERIFIED)
{
*piErrCode = FLM_BAD_NEXT_SIBLING_LINK;
}
// Must also verify that this node is the last child of the parent node
// - if there is a parent.
if (ui64ParentId)
{
FLMUINT64 ui64LastChild;
pTmpRSEntry->hdr.ui64NodeId = ui64ParentId;
if( RC_BAD( rc = pResult->findEntry( NULL, NULL,
(FLMBYTE *)&(pTmpRSEntry->hdr.ui64NodeId), sizeof( FLMUINT64),
&uiKeySize, (FLMBYTE *)pTmpRSEntry, sizeof( NODE_RS_ENTRY),
&uiTmpRSEntrySize)))
{
*piErrCode = FLM_BAD_PARENT_LINK;
goto Exit;
}
ui64LastChild = getLinkVal( CHK_BM_LAST_CHILD, pTmpRSEntry);
if (ui64LastChild != pRSEntry->hdr.ui64NodeId)
{
FLMUINT64 ui64Annot;
// It may be an annotation Node.
ui64Annot = getLinkVal( CHK_BM_ANNOTATION, pTmpRSEntry);
if (ui64Annot != pRSEntry->hdr.ui64NodeId)
{
*piErrCode = FLM_BAD_NEXT_SIBLING_LINK;
goto Exit;
}
}
}
goto Exit;
}
pTmpRSEntry->hdr.ui64NodeId = ui64NextSibling;
if( RC_BAD( rc = pResult->findEntry( NULL, NULL,
(FLMBYTE *)&(pTmpRSEntry->hdr.ui64NodeId), sizeof( FLMUINT64),
&uiKeySize, (FLMBYTE *)pTmpRSEntry, sizeof( NODE_RS_ENTRY),
&uiTmpRSEntrySize)))
{
*piErrCode = FLM_BAD_NEXT_SIBLING_LINK;
goto Exit;
}
// Must belong to the same document unless both nodes are
// document roots
if( ui64RootId != getLinkVal( CHK_BM_ROOT_ID, pTmpRSEntry))
{
if( ui64ParentId || getLinkVal( CHK_BM_PARENT_ID, pTmpRSEntry))
{
*piErrCode = FLM_BAD_NEXT_SIBLING_LINK;
goto Exit;
}
}
// The next sibling should not have been visited as a next
// sibling before now.
if( pTmpRSEntry->hdr.ui16Flags & CHK_NEXT_SIBLING_VERIFIED)
{
*piErrCode = FLM_BAD_NEXT_SIBLING_LINK;
goto Exit;
}
// Should point to "this" node.
if( getLinkVal( CHK_BM_PREV_SIBLING, pTmpRSEntry) != pRSEntry->hdr.ui64NodeId)
{
*piErrCode = FLM_BAD_NEXT_SIBLING_LINK;
goto Exit;
}
// Mark the previous sibling as being visited as such.
pTmpRSEntry->hdr.ui16Flags |= CHK_NEXT_SIBLING_VERIFIED;
if( RC_BAD( rc = pResult->modifyEntry( NULL, NULL,
(FLMBYTE *)&(pTmpRSEntry->hdr.ui64NodeId),
sizeof( FLMUINT64), (FLMBYTE *)pTmpRSEntry, uiTmpRSEntrySize)))
{
goto Exit;
}
Exit:
return( rc);
}
/********************************************************************
Desc:
********************************************************************/
FSTATIC RCODE verifyAnnotationLink(
NODE_RS_ENTRY * pRSEntry,
NODE_RS_ENTRY * pTmpRSEntry,
F_BtResultSet * pResult,
FLMINT * piErrCode
)
{
RCODE rc = NE_XFLM_OK;
FLMUINT64 ui64Annotation = getLinkVal( CHK_BM_ANNOTATION, pRSEntry);
FLMUINT64 ui64RootId = getLinkVal( CHK_BM_ROOT_ID, pRSEntry);
FLMUINT uiTmpRSEntrySize;
FLMUINT64 ui64TmpRootId;
FLMUINT uiKeySize = sizeof( FLMUINT64);
f_memset( pTmpRSEntry, 0, sizeof(NODE_RS_ENTRY));
if (!ui64Annotation)
{
goto Exit;
}
pTmpRSEntry->hdr.ui64NodeId = ui64Annotation;
if( RC_BAD( rc = pResult->findEntry( NULL, NULL,
(FLMBYTE *)&(pTmpRSEntry->hdr.ui64NodeId), sizeof( FLMUINT64),
&uiKeySize, (FLMBYTE *)pTmpRSEntry, sizeof( NODE_RS_ENTRY),
&uiTmpRSEntrySize)))
{
*piErrCode = FLM_BAD_ANNOTATION_LINK;
goto Exit;
}
// Must belong to the same document / root
// Annotations may belong to a node that does not have a Root Id. If
// that is the case, the parent and root of the annotaion should match and
// the parent should point to the source node.
ui64TmpRootId = getLinkVal( CHK_BM_ROOT_ID, pTmpRSEntry);
if( ui64RootId)
{
if (ui64RootId != ui64TmpRootId)
{
*piErrCode = FLM_BAD_ANNOTATION_LINK;
goto Exit;
}
}
else
{
// With no root, the temporary root must point to "this" node.
if (ui64TmpRootId != pRSEntry->hdr.ui64NodeId)
{
*piErrCode = FLM_BAD_ANNOTATION_LINK;
goto Exit;
}
}
// The annotation should not have been visited as such before now.
if (pTmpRSEntry->hdr.ui16Flags & CHK_ANNOTATION_VERIFIED)
{
*piErrCode = FLM_BAD_ANNOTATION_LINK;
goto Exit;
}
// Parent should point to "this" node.
if (getLinkVal( CHK_BM_PARENT_ID, pTmpRSEntry) != pRSEntry->hdr.ui64NodeId)
{
*piErrCode = FLM_BAD_ANNOTATION_LINK;
goto Exit;
}
// Mark the last attr as being visited as such.
pTmpRSEntry->hdr.ui16Flags |= CHK_ANNOTATION_VERIFIED;
if (RC_BAD( rc = pResult->modifyEntry( NULL, NULL,
(FLMBYTE *)&(pTmpRSEntry->hdr.ui64NodeId),
sizeof( FLMUINT64),
(FLMBYTE *)pTmpRSEntry,
uiTmpRSEntrySize)))
{
goto Exit;
}
Exit:
return rc;
}
/***************************************************************************
Desc: This routine does for chains of data-only blocks what verifySubTree
does for the other blocks. Basically, it's going to read in each
block in the chain, perform some basic verification on the header
and add the data to the NodeVerifier object.
*****************************************************************************/
RCODE F_DbCheck::verifyDOChain(
STATE_INFO * pParentState,
FLMUINT uiBlkAddr,
FLMINT * piElmErrCode)
{
RCODE rc = NE_XFLM_OK;
F_NodeVerifier * pNodeVerifier = pParentState->pNodeVerifier;
F_CachedBlock * pSCache = NULL;
F_BLK_HDR * pBlkHdr = NULL;
FLMUINT uiParentBlkAddr = pParentState->pBlkHdr->ui32BlkAddr;
FLMUINT uiPrevNextBlkAddr; // The ui32NextBlkInChain field from the previous block
FLMUINT uiNumErrors = 0;
FLMUINT uiNumBlksRead = 0;
FLMUINT uiBlockSize = m_pDb->m_pDatabase->getBlockSize();
FLMBYTE * pucData = NULL;
FLMUINT uiDataLen = 0;
BLOCK_INFO * pBlkInfo = &pParentState->BlkInfo;
STATE_INFO StateInfo;
// All leaf nodes are level 0, and only leaf nodes can point
// to data only blocks...
if (pParentState->uiLevel != 0)
{
*piElmErrCode = FLM_BAD_ELM_INVALID_LEVEL;
rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE);
goto Exit;
}
//Initialize the StateInfo struct
f_memset( &StateInfo, 0, sizeof( STATE_INFO));
StateInfo.pCollection = pParentState->pCollection;
StateInfo.pDb = pParentState->pDb;
StateInfo.ui64ElmNodeId = pParentState->ui64ElmNodeId;
// Initialize the NodeVerifier object
if (pNodeVerifier)
{
pNodeVerifier->Reset( pParentState);
}
// Now, loop through every block in the chain...
uiPrevNextBlkAddr = 0;
while (uiBlkAddr)
{
// Read in the next block in the chain
f_yieldCPU();
if (RC_BAD( rc = blkRead( uiBlkAddr,
&pBlkHdr,
&pSCache,
piElmErrCode)))
{
if (*piElmErrCode)
{
uiNumErrors++;
chkReportError( *piElmErrCode,
XFLM_LOCALE_B_TREE,
m_Progress.uiLfNumber,
m_Progress.uiLfType,
StateInfo.uiLevel,
uiBlkAddr,
uiParentBlkAddr,
0,
0);
}
else if (rc == NE_XFLM_OLD_VIEW)
{
// We're going to have to reset. Unfortunately,
// we don't know how to position ourselves into the middle
// of a record, so we'll have to reset back to the beginning
// of the record. We should still be able to finish processing.
// We only need to see enough of the DOM node to build the header.
// It's the header that gives us the DOM link information.
if (pNodeVerifier)
{
pNodeVerifier->Reset( pParentState);
}
m_Progress.ui64BytesExamined -=
(FLMUINT64)(uiBlockSize*uiNumBlksRead);
chkCallProgFunc();
}
goto Exit;
}
// Chains of data only blocks should always have at least 2 blocks...
if ((uiNumBlksRead == 0) && (pBlkHdr->ui32NextBlkInChain == 0))
{
*piElmErrCode = FLM_BAD_DATA_BLOCK_COUNT;
rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE);
goto Exit;
}
// Record the progress that we're making
uiNumBlksRead++;
m_Progress.ui64BytesExamined += (FLMUINT64)uiBlockSize;
chkCallProgFunc();
// Check the block header.
StateInfo.pBlkHdr = pBlkHdr;
StateInfo.uiBlkType = BT_DATA_ONLY;
StateInfo.ui32BlkAddress = (FLMUINT32)uiBlkAddr;
*piElmErrCode = flmVerifyBlockHeader( &StateInfo, pBlkInfo, uiBlockSize,
0xFFFFFFFF, (uiNumBlksRead > 1) ?
uiPrevNextBlkAddr : 0, TRUE);
if (*piElmErrCode != 0)
{
uiNumErrors++;
chkReportError( *piElmErrCode, XFLM_LOCALE_B_TREE, m_Progress.uiLfNumber,
m_Progress.uiLfType, StateInfo.uiLevel, uiBlkAddr,
uiParentBlkAddr, 0, 0);
}
// Verify that the ui16BlkBytesAvail is a reasonable size...
if( (pBlkHdr->ui32NextBlkInChain != 0) &&
(pBlkHdr->ui16BlkBytesAvail != 0))
{
*piElmErrCode = FLM_BAD_AVAIL_SIZE;
rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE);
goto Exit;
}
// Add the current data to the verifier....
// We really only should need to do this if this is the first block
// in the chain.
if( !pBlkHdr->ui32PrevBlkInChain)
{
FLMBYTE * pucPtr = (FLMBYTE *)pBlkHdr + sizeof( F_BLK_HDR);
FLMUINT uiKeySize = (FLMUINT)FB2UW( pucPtr);
pucData = pucPtr + uiKeySize + 2;
uiDataLen = uiBlockSize - sizeof( F_BLK_HDR) -
pBlkHdr->ui16BlkBytesAvail - uiKeySize - 2;
if (pNodeVerifier)
{
if (RC_BAD( rc = pNodeVerifier->AddData(
StateInfo.ui64ElmNodeId, pucData, uiDataLen)))
{
goto Exit;
}
}
}
uiPrevNextBlkAddr = uiBlkAddr;
uiBlkAddr = pBlkHdr->ui32NextBlkInChain;
} // end of while (uiNextBlkAddress)
Exit:
if (pSCache)
{
ScaReleaseCache( pSCache, FALSE);
}
else if (pBlkHdr)
{
// The reason for the else is that pBlkHdr will normally be
// pointing into pSCache. Only if the call to getBlock()
// inside of blkRead() fails will memory be allocated
// explicitly for pBlkHdr
f_free( &pBlkHdr);
}
return rc;
}
/***************************************************************************
Desc: This routine checks all of the blocks/links in a sub-tree of a
B-TREE. It calls itself recursively whenever it descends a level
in the tree.
*****************************************************************************/
RCODE F_DbCheck::verifySubTree(
STATE_INFO * pParentState,
STATE_INFO * pStateInfo,
FLMUINT uiBlkAddress,
FLMBYTE ** ppucResetKey,
FLMUINT uiResetKeyLen,
FLMUINT64 ui64ResetNodeId)
{
RCODE rc = NE_XFLM_OK;
F_CachedBlock * pSCache = NULL;
F_BLK_HDR * pBlkHdr = NULL;
FLMUINT uiLevel = pStateInfo->uiLevel;
FLMUINT uiBlkType = pStateInfo->uiBlkType;
FLMUINT uiLfType = m_pLFile->eLfType;
FLMUINT uiBlockSize = m_pDb->m_pDatabase->m_uiBlockSize;
FLMUINT uiParentBlkAddress;
FLMUINT uiChildBlkAddress;
FLMUINT uiPrevNextBlkAddress;
FLMINT iElmErrCode;
FLMINT iBlkErrCode = 0;
FLMINT iLastErrCode = 0;
FLMUINT uiNumErrors = 0;
FLMUINT64 ui64SaveKeyCount = 0;
FLMUINT64 ui64SaveKeyRefs = 0;
BLOCK_INFO SaveBlkInfo;
BLOCK_INFO * pBlkInfo;
FLMBOOL bProcessElm;
FLMBOOL bCountElm;
FLMBOOL bDescendToChildBlocks;
FLMINT iCompareStatus;
FLMINT iHdrErrCode;
F_NodeVerifier * pNodeVerifier = pStateInfo->pNodeVerifier;
STATE_INFO * pChildStateInfo = NULL;
F_CachedBlock * pTmpSCache = NULL;
F_BLK_HDR * pTmpBlkHdr = NULL;
// Setup the state information.
pStateInfo->pBlkHdr = NULL;
pStateInfo->ui32BlkAddress = (FLMUINT32)uiBlkAddress;
uiPrevNextBlkAddress = pStateInfo->ui32NextBlkAddr;
uiParentBlkAddress = (pParentState)
? pParentState->ui32BlkAddress
: 0xFFFFFFFF;
// Read the sub-tree block into memory
bDescendToChildBlocks = TRUE;
if (RC_BAD( rc = blkRead( uiBlkAddress, &pBlkHdr, &pSCache, &iBlkErrCode)))
{
if (iBlkErrCode)
{
uiNumErrors++;
iLastErrCode = iBlkErrCode;
chkReportError( iBlkErrCode, XFLM_LOCALE_B_TREE,
m_Progress.uiLfNumber, m_Progress.uiLfType, uiLevel, uiBlkAddress,
uiParentBlkAddress, 0, 0);
if( iBlkErrCode == FLM_BAD_BLK_CHECKSUM)
{
bDescendToChildBlocks = FALSE;
// Allow to continue the check, but if this is a non-leaf block
// a non-zero iBlkErrCode will prevent us from descending to
// child blocks. Set rc to SUCCESS so we won't goto Exit below.
rc = NE_XFLM_OK;
}
else if (iBlkErrCode == FLM_COULD_NOT_SYNC_BLK)
{
iLastErrCode = iBlkErrCode;
// Need the goto here, because rc is changed to SUCCESS,
// and the goto below would get skipped.
rc = NE_XFLM_OK;
goto fix_state;
}
}
// If rc was not changed to SUCCESS above, goto Exit.
if (RC_BAD( rc))
{
goto Exit;
}
}
pStateInfo->pBlkHdr = pBlkHdr;
// Verify the block header
// Don't recount the block if we are resetting.
if (uiResetKeyLen == ~(FLMUINT)0)
{
m_Progress.ui64BytesExamined += (FLMUINT64)uiBlockSize;
pBlkInfo = &pStateInfo->BlkInfo;
}
else
{
pBlkInfo = NULL;
}
chkCallProgFunc();
// Check the block header.
if ((iHdrErrCode =
flmVerifyBlockHeader( pStateInfo, pBlkInfo, uiBlockSize,
(pParentState == NULL)
? 0
: 0xFFFFFFFF,
(pParentState == NULL)
? 0
: pParentState->ui32LastChildAddr,
TRUE)) == 0)
{
// Verify the previous block's next block address -- it should
// equal the current block's address.
if (uiPrevNextBlkAddress != 0xFFFFFFFF &&
uiPrevNextBlkAddress != uiBlkAddress &&
(uiResetKeyLen == ~(FLMUINT)0))
{
iHdrErrCode = FLM_BAD_PREV_BLK_NEXT;
}
}
if (iHdrErrCode != 0)
{
// Check to see if the previous block is still valid.
// It may be that the block has gone away, and so is no longer valid.
if (iHdrErrCode == FLM_BAD_BLK_HDR_PREV)
{
flmAssert( pParentState);
if (RC_BAD( rc = blkRead( pParentState->ui32LastChildAddr,
&pTmpBlkHdr, &pTmpSCache, &iBlkErrCode)))
{
iLastErrCode = iBlkErrCode;
uiNumErrors++;
chkReportError( iBlkErrCode, XFLM_LOCALE_B_TREE,
m_Progress.uiLfNumber, m_Progress.uiLfType, uiLevel,
uiBlkAddress, uiParentBlkAddress, 0, 0);
}
else
{
// If the block is free, it means that somewhere in our check, we
// deleted entries that resulted in this block being emptied,
// thus freed.
if (pTmpBlkHdr->ui8BlkType == BT_FREE)
{
iHdrErrCode = 0;
}
else
{
iLastErrCode = iHdrErrCode;
uiNumErrors++;
chkReportError( iHdrErrCode, XFLM_LOCALE_B_TREE,
m_Progress.uiLfNumber, m_Progress.uiLfType, uiLevel,
uiBlkAddress, uiParentBlkAddress, 0, 0);
}
}
}
else
{
iLastErrCode = iHdrErrCode;
uiNumErrors++;
chkReportError( iHdrErrCode, XFLM_LOCALE_B_TREE, m_Progress.uiLfNumber,
m_Progress.uiLfType, uiLevel, uiBlkAddress, uiParentBlkAddress,
0, 0);
}
if (pTmpSCache)
{
ScaReleaseCache( pTmpSCache, FALSE);
pTmpSCache = NULL;
pTmpBlkHdr = NULL;
}
}
// Verify the structure of the block
if( RC_BAD( rc = verifyBlockStructure( uiBlockSize,
(F_BTREE_BLK_HDR *)pBlkHdr)))
{
if (rc == NE_XFLM_BTREE_ERROR)
{
iBlkErrCode = FLM_BAD_BLOCK_STRUCTURE;
rc = NE_XFLM_OK;
goto fix_state;
}
else
{
goto Exit;
}
}
// Go through the elements in the block.
for ( pStateInfo->uiElmOffset=0;
(pStateInfo->uiElmOffset <
((F_BTREE_BLK_HDR *)pBlkHdr)->ui16NumKeys &&
RC_OK( m_LastStatusRc));
pStateInfo->uiElmOffset++)
{
// If we are resetting, save any statistical information so we
// can back it out if we need to.
if (uiResetKeyLen != ~(FLMUINT)0)
{
ui64SaveKeyCount = pStateInfo->ui64KeyCount;
ui64SaveKeyRefs = pStateInfo->ui64KeyRefs;
f_memcpy( &SaveBlkInfo, &pStateInfo->BlkInfo, sizeof( BLOCK_INFO));
bCountElm = FALSE;
bProcessElm = FALSE;
}
else
{
bCountElm = TRUE;
bProcessElm = TRUE;
}
// Verify the element first, then check if we are restting...
m_LastStatusRc = flmVerifyElement( pStateInfo, m_pLFile, m_pIxd,
&iElmErrCode);
if (iElmErrCode)
{
// Report any errors in the element.
iLastErrCode = iElmErrCode;
uiNumErrors++;
if (RC_BAD( rc = chkReportError( iElmErrCode, XFLM_LOCALE_B_TREE,
m_Progress.uiLfNumber, m_Progress.uiLfType, uiLevel, uiBlkAddress,
uiParentBlkAddress, pStateInfo->uiElmOffset,
pStateInfo->ui64ElmNodeId)))
{
break;
}
}
// See if we are resetting
iCompareStatus = 0;
if ((uiResetKeyLen != ~(FLMUINT)0) &&
pStateInfo->bValidKey &&
(!pStateInfo->uiElmKeyLen ||
((iCompareStatus = f_memcmp(
pStateInfo->pucElmKey, *ppucResetKey,
pStateInfo->uiElmKeyLen < uiResetKeyLen
? pStateInfo->uiElmKeyLen
: uiResetKeyLen)) >= 0)))
{
// A key length of 0 is valid. It refers to the LEM. All keys are
// less than the LEM if their length is > 0.
if ((uiResetKeyLen && pStateInfo->uiElmKeyLen ||
!pStateInfo->uiElmKeyLen))
{
// If we passed the target key, or we are on the last element
// then count it.
bProcessElm = TRUE;
if ((iCompareStatus > 0) || !(pStateInfo->uiElmKeyLen))
{
if ( (uiBlkType == BT_LEAF_DATA) ||
(uiBlkType == BT_LEAF) )
{
bCountElm = TRUE;
uiResetKeyLen = ~(FLMUINT)0;
if (*ppucResetKey)
{
f_free( ppucResetKey);
}
*ppucResetKey = NULL;
ui64ResetNodeId = 0;
}
}
else if ( uiLfType == XFLM_LF_INDEX)
{
bCountElm = TRUE;
}
}
}
if (bCountElm)
{
pStateInfo->BlkInfo.ui64ElementCount++;
}
// Check for index keys that can be verified on leaf level blocks.
if (uiResetKeyLen == ~(FLMUINT)0)
{
if ( isIndexBlk( (F_BTREE_BLK_HDR *)pStateInfo->pBlkHdr) &&
(pStateInfo->pBlkHdr->ui8BlkType == BT_LEAF ||
pStateInfo->pBlkHdr->ui8BlkType == BT_LEAF_DATA) &&
pStateInfo->uiElmKeyLen)
{
FLMUINT64 ui64CurrTransId = pStateInfo->pDb->m_ui64CurrTransID;
if (RC_BAD( rc = verifyIXRefs( pStateInfo, ui64ResetNodeId)))
{
goto Exit;
}
// Make sure we resynchronize when we make changes to
// blocks we are looking at.
if (pStateInfo->pDb->m_ui64CurrTransID != ui64CurrTransId)
{
if (m_bPhysicalCorrupt)
{
m_bPhysicalCorrupt = FALSE;
m_uiFlags |= XFLM_DO_LOGICAL_CHECK;
}
rc = m_LastStatusRc = RC_SET( NE_XFLM_RESET_NEEDED);
goto Exit;
}
}
if (RC_BAD( m_LastStatusRc))
{
break;
}
// Keep track of the number of continuation elements.
if( (uiBlkType == BT_LEAF_DATA) &&
((*pStateInfo->pucElm & BTE_FLAG_FIRST_ELEMENT) == 0))
{
pStateInfo->BlkInfo.ui64ContElementCount++;
pStateInfo->BlkInfo.ui64ContElmBytes += pStateInfo->uiElmLen;
}
}
// Do some further checking.
if (iElmErrCode == 0)
{
if (bProcessElm &&
(uiBlkType == BT_LEAF_DATA ||
uiBlkType == BT_LEAF) &&
uiLfType == XFLM_LF_COLLECTION)
{
// No need to process LEM element
if ((pStateInfo->uiElmKeyLen != 0) && (pStateInfo->bValidKey))
{
// Is this record stored in a chain of DO blocks...?
if ((*pStateInfo->pucElm & BTE_FLAG_DATA_BLOCK) != 0)
{
flmAssert( pStateInfo->uiElmDataLen == 4);
if( RC_BAD( rc = verifyDOChain( pStateInfo,
FB2UD( pStateInfo->pucElmData), &iElmErrCode)))
{
goto Exit;
}
}
else
{
// Since DOM nodes may be spread across multiple entries
// in the Btree block, it may be impractical to read in
// the entire node all at once, we need a way of doing the
// verification a little at a time. The NodeVerifier
// object handles this. We pass in the data as we get it,
// it appends the data to any data it had left over from
// a previous call, and then verifies as much as it can.
// Any "left over" data is saved for the next call.
// If the "first" flag is set on this element, twe need
// to reset the verifier
if( *pStateInfo->pucElm & BTE_FLAG_FIRST_ELEMENT &&
pNodeVerifier)
{
pNodeVerifier->Reset( pStateInfo);
}
// Add the current data to the verifier....
flmAssert( pStateInfo->ui64ElmNodeId);
if (pNodeVerifier)
{
if (RC_BAD( rc = pNodeVerifier->AddData(
pStateInfo->ui64ElmNodeId, pStateInfo->pucElmData,
pStateInfo->uiElmDataLen)))
{
goto Exit;
}
}
}
// If this is the last entry for this element, then we can
// finalize the node verifier. This entails assembling
// the DOM node, and possibly adding required information
// to a result set for later DOM link verification. We also
// add the node Id to the index result set if we are
// checking/verifying indexes.
if( *pStateInfo->pucElm & BTE_FLAG_LAST_ELEMENT)
{
if( pNodeVerifier)
{
if( RC_BAD( rc = pNodeVerifier->finalize(
m_pDb, m_pDb->m_pDict,
pStateInfo->pCollection->lfInfo.uiLfNum,
pStateInfo->ui64ElmNodeId, m_bSkipDOMLinkCheck,
&iElmErrCode)))
{
goto Exit;
}
}
}
}
if (bProcessElm)
{
if (iElmErrCode != 0)
{
// Report any errors in the element.
iLastErrCode = iElmErrCode;
uiNumErrors++;
chkReportError( iElmErrCode, XFLM_LOCALE_B_TREE,
m_Progress.uiLfNumber, m_Progress.uiLfType, uiLevel,
uiBlkAddress, uiParentBlkAddress, pStateInfo->uiElmOffset,
pStateInfo->ui64ElmNodeId);
if (RC_BAD( m_LastStatusRc))
{
break;
}
}
}
}
else if (uiBlkType != BT_LEAF_DATA && uiBlkType != BT_LEAF)
{
flmAssert( uiBlkType != BT_DATA_ONLY);
uiChildBlkAddress = (FLMUINT)FB2UD(pStateInfo->pucElm);
// Check the child sub-tree -- NOTE, this is a recursive call.
if (bProcessElm)
{
if (!bDescendToChildBlocks)
{
rc = NE_XFLM_OK;
}
else
{
pChildStateInfo = (pStateInfo - 1);
if (pChildStateInfo->uiElmKeyLen &&
(uiResetKeyLen != ~(FLMUINT)0))
{
uiResetKeyLen = pChildStateInfo->uiElmKeyLen;
ui64ResetNodeId = pChildStateInfo->ui64ElmNodeId;
if (*ppucResetKey)
{
f_free( ppucResetKey);
}
if (RC_BAD( rc = f_calloc( uiResetKeyLen + 1,
ppucResetKey)))
{
goto Exit;
}
f_memcpy( *ppucResetKey, pChildStateInfo->pucElmKey,
uiResetKeyLen);
}
rc = verifySubTree( pStateInfo, (pStateInfo - 1),
uiChildBlkAddress, ppucResetKey, uiResetKeyLen,
ui64ResetNodeId);
}
if (RC_BAD( rc) || RC_BAD( m_LastStatusRc))
{
goto Exit;
}
// Once we reach the key, set it to an empty to key so that
// we will always descend to the child block after this point.
uiResetKeyLen = ~(FLMUINT)0;
if (*ppucResetKey)
{
f_free( ppucResetKey);
}
*ppucResetKey = NULL;
ui64ResetNodeId = 0;
}
// Save the child block address in the level information
pStateInfo->ui32LastChildAddr = (FLMUINT32)uiChildBlkAddress;
}
}
// If we were resetting on this element, restore the statistics to what
// they were before.
if( !bCountElm)
{
pStateInfo->ui64KeyCount = ui64SaveKeyCount;
pStateInfo->ui64KeyRefs = ui64SaveKeyRefs;
f_memcpy( &pStateInfo->BlkInfo, &SaveBlkInfo, sizeof( BLOCK_INFO));
}
if (RC_BAD( rc = chkCallProgFunc()))
{
break;
}
}
// Verify that the last key in the block matches the parent's key.
if (iLastErrCode == 0 && pParentState && RC_OK( m_LastStatusRc))
{
if (pStateInfo->bValidKey && pParentState->bValidKey &&
f_memcmp( pStateInfo->pucElmKey,
pParentState->pucElmKey,
pStateInfo->uiElmKeyLen < pParentState->uiElmKeyLen
? pStateInfo->uiElmKeyLen
: pParentState->uiElmKeyLen) != 0)
{
iLastErrCode = FLM_BAD_PARENT_KEY;
uiNumErrors++;
chkReportError( iLastErrCode, XFLM_LOCALE_B_TREE,
m_Progress.uiLfNumber, m_Progress.uiLfType, uiLevel, uiBlkAddress,
uiParentBlkAddress, 0, 0);
}
}
fix_state:
// If the block could not be verified, set the level's next block
// address and last child address to zero to indicate that we really
// aren't sure we're at the right place in this level in the B-TREE.
if (iLastErrCode != 0)
{
pStateInfo->BlkInfo.iErrCode = iLastErrCode;
pStateInfo->BlkInfo.uiNumErrors += uiNumErrors;
// Reset all child block states.
for (;;)
{
pStateInfo->ui32NextBlkAddr = 0xFFFFFFFF;
pStateInfo->ui32LastChildAddr = 0xFFFFFFFF;
pStateInfo->bValidKey = FALSE;
pStateInfo->uiElmLastFlag = 0xFF;
// Quit when the leaf level has been reached.
if (pStateInfo->uiLevel == 0)
{
break;
}
pStateInfo--;
}
}
Exit:
if (pSCache)
{
ScaReleaseCache( pSCache, FALSE);
}
else if (pBlkHdr)
{
f_free( &pBlkHdr);
}
pStateInfo->pBlkHdr = NULL;
return( rc);
}
/********************************************************************
Desc:
*********************************************************************/
FSTATIC FLMBYTE * getEntryEnd(
FLMBYTE * pucEntry,
FLMUINT uiBlkType)
{
FLMBYTE * pucEnd = pucEntry;
switch (uiBlkType)
{
case BT_LEAF:
{
FLMUINT uiKL = (FLMUINT)FB2UW( pucEnd);
pucEnd += (uiKL + 2);
break;
}
case BT_LEAF_DATA:
{
FLMUINT uiKL;
FLMUINT uiDL;
FLMUINT ucFlags = *pucEnd;
pucEnd++;
if (ucFlags & BTE_FLAG_KEY_LEN)
{
uiKL = (FLMUINT)FB2UW( pucEnd);
pucEnd += 2;
}
else
{
uiKL = *pucEnd;
pucEnd++;
}
if (ucFlags & BTE_FLAG_DATA_LEN)
{
uiDL = FB2UW( pucEnd);
pucEnd += 2;
}
else
{
uiDL = *pucEnd;
pucEnd++;
}
if (ucFlags & BTE_FLAG_OA_DATA_LEN)
{
pucEnd += 4;
}
pucEnd += (uiKL + uiDL);
break;
}
case BT_NON_LEAF:
{
FLMUINT uiKL;
pucEnd += 4;
uiKL = (FLMUINT)FB2UW( pucEnd);
pucEnd += (uiKL + 2);
break;
}
case BT_NON_LEAF_COUNTS:
{
FLMUINT uiKL;
pucEnd += 8;
uiKL = (FLMUINT)FB2UW( pucEnd);
pucEnd += (uiKL + 2);
break;
}
}
return( pucEnd);
}
/***************************************************************************
Desc: Compare two cache blocks during a sort to determine which
one has lower address.
*****************************************************************************/
FINLINE FLMINT FLMAPI blkSortCompare(
void * pvBuffer,
FLMUINT uiPos1,
FLMUINT uiPos2)
{
BlkStruct * pBlk1 = ((BlkStruct *)pvBuffer) + uiPos1;
BlkStruct * pBlk2 = ((BlkStruct *)pvBuffer) + uiPos2;
if (pBlk1->uiStartOfEntry < pBlk2->uiStartOfEntry)
{
return( -1);
}
else if (pBlk1->uiStartOfEntry > pBlk2->uiStartOfEntry)
{
return( 1);
}
else
{
return( 0);
}
}
/***************************************************************************
Desc: Swap two entries in cache table during sort.
*****************************************************************************/
FINLINE void FLMAPI blkSortSwap(
void * pvBuffer,
FLMUINT uiPos1,
FLMUINT uiPos2)
{
BlkStruct * pBlkEntryTable = (BlkStruct *)pvBuffer;
BlkStruct TmpEntry = pBlkEntryTable [uiPos1];
pBlkEntryTable[ uiPos1] = pBlkEntryTable[ uiPos2];
pBlkEntryTable[ uiPos2] = TmpEntry;
}
/********************************************************************
Desc:
*********************************************************************/
RCODE F_DbCheck::verifyBlockStructure(
FLMUINT uiBlockSize,
F_BTREE_BLK_HDR * pBlkHdr)
{
RCODE rc = NE_XFLM_OK;
FLMUINT uiIndex;
FLMBYTE * pucEntry;
FLMBYTE * pucEnd;
BlkStruct * pCurBlk;
FLMUINT uiNumEntries;
// No need to check anything in the block if there are less than 2 entries.
if ((uiNumEntries = pBlkHdr->ui16NumKeys) < 2)
{
goto Exit;
}
if (uiNumEntries > m_uiBlkEntryArraySize)
{
FLMUINT uiNewEntryArraySize = uiNumEntries + 200;
if (RC_BAD( rc = f_realloc( sizeof( BlkStruct) * uiNewEntryArraySize,
&m_pBlkEntries)))
{
goto Exit;
}
f_memset( &m_pBlkEntries [m_uiBlkEntryArraySize], 0,
sizeof( BlkStruct) * (uiNewEntryArraySize - m_uiBlkEntryArraySize));
m_uiBlkEntryArraySize = uiNewEntryArraySize;
}
for (uiIndex = 0, pCurBlk = m_pBlkEntries;
uiIndex < uiNumEntries;
uiIndex++, pCurBlk++)
{
pucEntry = BtEntry( (FLMBYTE *)pBlkHdr, uiIndex);
if( pucEntry > (FLMBYTE *)pBlkHdr + uiBlockSize)
{
rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE);
goto Exit;
}
pucEnd = getEntryEnd( pucEntry, pBlkHdr->stdBlkHdr.ui8BlkType);
pCurBlk->uiStartOfEntry = (FLMUINT)pucEntry;
pCurBlk->uiEndOfEntry = (FLMUINT)pucEnd;
}
// Now sort the entries and check the offsets.
f_qsort( m_pBlkEntries, 0, uiNumEntries - 1, blkSortCompare, blkSortSwap);
for (uiIndex = 1; uiIndex < uiNumEntries; uiIndex++)
{
if (m_pBlkEntries [uiIndex - 1].uiEndOfEntry >
m_pBlkEntries [uiIndex].uiStartOfEntry)
{
rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR);
goto Exit;
}
}
Exit:
return( rc);
}
/********************************************************************
Desc: Verify the reference set for a key
*********************************************************************/
RCODE F_DbCheck::verifyIXRefs(
STATE_INFO * pStateInfo,
FLMUINT64 ui64ResetNodeId
)
{
RCODE rc = NE_XFLM_OK;
FLMUINT64 ui64NodeId;
// Get the NodeId for the element.
ui64NodeId = pStateInfo->ui64ElmNodeId;
if (ui64NodeId <= ui64ResetNodeId)
{
ui64ResetNodeId = 0;
}
if (!ui64ResetNodeId && !m_bPhysicalCorrupt)
{
if (RC_BAD( rc = verifyIXRSet( pStateInfo)))
{
goto Exit;
}
}
pStateInfo->ui64KeyRefs++;
Exit:
return( rc);
}
/********************************************************************
Desc: Initialize the STATE_INFO in preparation to do checking.
*********************************************************************/
void flmInitReadState(
STATE_INFO * pStateInfo,
FLMBOOL * pbStateInitialized,
FLMUINT, //uiVersionNum,
F_Db * pDb, // May be NULL.
LF_HDR *, //pLogicalFile,
FLMUINT uiLevel,
FLMUINT uiExpectedBlkType,
FLMBYTE * pucKeyBuffer)
{
f_memset( pStateInfo, 0, sizeof( STATE_INFO));
*pbStateInitialized = TRUE;
pStateInfo->pDb = pDb;
pStateInfo->uiLevel = uiLevel;
pStateInfo->uiBlkType = uiExpectedBlkType;
pStateInfo->pucElmKey = pucKeyBuffer;
pStateInfo->uiElmLastFlag = 0xFF;
pStateInfo->ui32NextBlkAddr = 0xFFFFFFFF;
pStateInfo->ui32LastChildAddr = 0xFFFFFFFF;
}
/******************************************************************************
Desc: Constructor
******************************************************************************/
F_NodeVerifier::F_NodeVerifier()
{
m_pucBuf = &m_ucBuf [0];
m_uiBufSize = sizeof( m_ucBuf);
m_uiBytesInBuf = 0;
m_pXRefRS = NULL;
m_bFinalizeCalled = FALSE;
m_pRS = NULL;
f_memset( &m_nodeInfo, 0, sizeof( F_NODE_INFO));
}
/******************************************************************************
Desc: Destructor
******************************************************************************/
F_NodeVerifier::~F_NodeVerifier()
{
if (m_pRS)
{
m_pRS->Release();
}
if (m_pucBuf != &m_ucBuf [0])
{
f_free( &m_pucBuf);
}
}
/******************************************************************************
Desc: Reset
******************************************************************************/
void F_NodeVerifier::Reset(
STATE_INFO * pStateInfo)
{
m_uiBytesInBuf = 0;
m_bFinalizeCalled = FALSE;
f_memset( m_pucBuf, 0, m_uiBufSize);
f_memset( &m_nodeInfo, 0, sizeof( F_NODE_INFO));
if( *pStateInfo->pucElm & BTE_FLAG_OA_DATA_LEN)
{
m_uiOverallLength = pStateInfo->uiElmOADataLen;
}
else
{
m_uiOverallLength = pStateInfo->uiElmDataLen;
}
}
/******************************************************************************
Desc:
******************************************************************************/
RCODE F_NodeVerifier::AddData(
FLMUINT64 ui64NodeId,
void * pucData,
FLMUINT uiDataLen)
{
RCODE rc = NE_XFLM_OK;
// Verify the node Id or Save it (first time)
if( m_nodeInfo.ui64NodeId)
{
if (m_nodeInfo.ui64NodeId != ui64NodeId)
{
rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE);
goto Exit;
}
}
else
{
m_nodeInfo.ui64NodeId = ui64NodeId;
}
// Copy the entry data into the local buffer. At most,
// we only need MAX_DOM_HEADER_SIZE bytes to make a
// complete DOM node header - unless we have an
// element node, in which case we want to get all of
// the attribute nodes.
if (uiDataLen + m_uiBytesInBuf > m_uiBufSize)
{
eDomNodeType eNodeType;
// Node type should be in the first byte of data we have for
// the node.
if (!m_uiBytesInBuf)
{
eNodeType = (eDomNodeType)((*((FLMBYTE *)pucData)) & 0x0F);
}
else
{
eNodeType = (eDomNodeType)((*m_pucBuf) & 0x0F);
}
if (eNodeType != ELEMENT_NODE)
{
flmAssert( m_uiBufSize >= MAX_DOM_HEADER_SIZE);
// Only copy enough to fill up the current buffer - don't
// really need any more than the header for non-element nodes.
uiDataLen = m_uiBufSize - m_uiBytesInBuf;
}
else
{
// Must get everything for element nodes
if (m_pucBuf != &m_ucBuf [0])
{
if (RC_BAD( rc = f_realloc( uiDataLen + m_uiBytesInBuf, &m_pucBuf)))
{
goto Exit;
}
}
else
{
FLMBYTE * pucNew;
if (RC_BAD( rc = f_alloc( uiDataLen + m_uiBytesInBuf, &pucNew)))
{
goto Exit;
}
if (m_uiBytesInBuf)
{
f_memcpy( pucNew, m_pucBuf, m_uiBytesInBuf);
}
m_pucBuf = pucNew;
}
m_uiBufSize = uiDataLen + m_uiBytesInBuf;
}
}
f_memcpy( &m_pucBuf[m_uiBytesInBuf], pucData, uiDataLen);
m_uiBytesInBuf += uiDataLen;
Exit:
return rc;
}
/********************************************************************
Desc: Do the final setup to store the node information in the Node
Result Set.
*********************************************************************/
RCODE F_NodeVerifier::finalize(
F_Db * pDb,
F_Dict * pDict,
FLMUINT uiCollection,
FLMUINT64 ui64NodeId,
FLMBOOL bSkipDOMLinkCheck,
FLMINT * piElmErrCodeRV)
{
RCODE rc = NE_XFLM_OK;
NODE_RS_ENTRY * pRSEntry = NULL;
FLMUINT uiRSBufIndex;
FLMUINT uiStorageFlags;
F_NameTable * pNameTable = pDict->getNameTable();
IF_BufferIStream * pBufferStream = NULL;
*piElmErrCodeRV = 0;
if( m_bFinalizeCalled)
{
flmAssert( 0);
goto Exit;
}
f_memset( &m_nodeInfo, 0, sizeof( F_NODE_INFO));
if( RC_BAD( rc = FlmAllocBufferIStream( &pBufferStream)))
{
goto Exit;
}
if( RC_BAD( rc = pBufferStream->open(
(const char *)m_pucBuf, m_uiBytesInBuf)))
{
goto Exit;
}
if( RC_BAD( rc = flmReadNodeInfo( uiCollection, ui64NodeId,
pBufferStream, m_uiOverallLength, FALSE, &m_nodeInfo, &uiStorageFlags)))
{
goto Exit;
}
// See if child element count is set.
if( uiStorageFlags & NSF_HAVE_CELM_LIST_BIT)
{
// This bit should only be set for elements.
if( m_nodeInfo.eNodeType != ELEMENT_NODE)
{
*piElmErrCodeRV = FLM_BAD_NODE_TYPE;
goto Exit;
}
// If the count > 0, the NSF_HAVE_CHILDREN_BIT better also be set.
if( m_nodeInfo.uiChildElmCount)
{
if( !(uiStorageFlags & NSF_HAVE_CHILDREN_BIT))
{
*piElmErrCodeRV = FLM_BAD_CHILD_ELM_COUNT;
goto Exit;
}
}
}
// Verify the Name and Prefix Ids.
if( RC_BAD( rc = verifyNameId( pDb, m_nodeInfo.eNodeType,
m_nodeInfo.uiNameId, pNameTable, piElmErrCodeRV)))
{
goto Exit;
}
if( *piElmErrCodeRV)
{
goto Exit;
}
if( RC_BAD( rc = verifyPrefixId( pDb,
m_nodeInfo.uiPrefixId, pNameTable, piElmErrCodeRV)))
{
goto Exit;
}
if( *piElmErrCodeRV)
{
goto Exit;
}
if( !bSkipDOMLinkCheck)
{
FLMUINT16 ui16BitMap = 0;
// Build a buffer to set into the Result Set for later verification...
if( RC_BAD( rc = f_calloc( sizeof( NODE_RS_ENTRY), &pRSEntry)))
{
goto Exit;
}
uiRSBufIndex = 0;
if( m_nodeInfo.ui64DocumentId)
{
pRSEntry->ui64FieldArray[ uiRSBufIndex++] = m_nodeInfo.ui64DocumentId;
ui16BitMap |= CHK_BM_ROOT_ID;
}
if( m_nodeInfo.ui64ParentId)
{
pRSEntry->ui64FieldArray[ uiRSBufIndex++] = m_nodeInfo.ui64ParentId;
ui16BitMap |= CHK_BM_PARENT_ID;
}
if( m_nodeInfo.ui64PrevSibId)
{
pRSEntry->ui64FieldArray[ uiRSBufIndex++] = m_nodeInfo.ui64PrevSibId;
ui16BitMap |= CHK_BM_PREV_SIBLING;
}
if( m_nodeInfo.ui64NextSibId)
{
pRSEntry->ui64FieldArray[ uiRSBufIndex++] = m_nodeInfo.ui64NextSibId;
ui16BitMap |= CHK_BM_NEXT_SIBLING;
}
if( m_nodeInfo.ui64FirstChildId)
{
pRSEntry->ui64FieldArray[ uiRSBufIndex++] = m_nodeInfo.ui64FirstChildId;
ui16BitMap |= CHK_BM_FIRST_CHILD;
}
if( m_nodeInfo.ui64LastChildId)
{
pRSEntry->ui64FieldArray[ uiRSBufIndex++] = m_nodeInfo.ui64LastChildId;
ui16BitMap |= CHK_BM_LAST_CHILD;
}
if( m_nodeInfo.ui64AnnotationId)
{
pRSEntry->ui64FieldArray[ uiRSBufIndex++] = m_nodeInfo.ui64AnnotationId;
ui16BitMap |= CHK_BM_ANNOTATION;
}
pRSEntry->hdr.ui64NodeId = m_nodeInfo.ui64NodeId;
pRSEntry->hdr.ui16BitMap = ui16BitMap;
m_bFinalizeCalled = TRUE;
// Finally add the result to the result set for later evaluation.
if( RC_BAD( rc = m_pRS->addEntry( NULL, NULL,
(FLMBYTE *)&(pRSEntry->hdr.ui64NodeId),
sizeof( FLMUINT64), (FLMBYTE *)pRSEntry,
sizeof( NODE_RS_HDR) + (uiRSBufIndex * sizeof( FLMUINT64)))))
{
*piElmErrCodeRV = -1;
goto Exit;
}
}
if( m_pXRefRS)
{
if (RC_BAD( rc = checkForIndexes( pDb, pDict, uiCollection)))
{
goto Exit;
}
}
Exit:
if( pBufferStream)
{
pBufferStream->Release();
}
if( pRSEntry)
{
f_free( &pRSEntry);
}
return( rc);
}
/******************************************************************************
Desc: Method to add any indexes to the document list that qualify.
******************************************************************************/
RCODE F_NodeVerifier::checkForIndexes(
F_Db * pDb,
F_Dict * pDict,
FLMUINT uiCollection)
{
RCODE rc = NE_XFLM_OK;
FLMUINT64 ui64DocumentId;
DOC_IXD_XREF DocXRef;
ICD * pIcd;
F_AttrElmInfo defInfo;
// Determine if there are any indexs that qualify to be included in the
// index list.
//
// If there is no document id, then this node is the the root node?
ui64DocumentId = m_nodeInfo.ui64DocumentId;
if( !ui64DocumentId)
{
if( !m_nodeInfo.ui64ParentId)
{
ui64DocumentId = m_nodeInfo.ui64NodeId;
}
else
{
rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE);
}
}
switch( m_nodeInfo.eNodeType)
{
case DOCUMENT_NODE:
case ELEMENT_NODE:
case DATA_NODE:
case COMMENT_NODE:
case CDATA_SECTION_NODE:
case ANNOTATION_NODE:
{
if( RC_BAD( rc = pDict->getElement( pDb,
m_nodeInfo.uiNameId, &defInfo)))
{
goto Exit;
}
break;
}
case ATTRIBUTE_NODE:
{
if (RC_BAD( rc = pDict->getAttribute( pDb, m_nodeInfo.uiNameId,
&defInfo)))
{
goto Exit;
}
break;
}
default:
{
goto Exit;
}
}
pIcd = defInfo.m_pFirstIcd;
// Do we have any qualifying indexes?
while (pIcd)
{
// Only process if the target collection matches.
if( pIcd->pIxd->uiCollectionNum == uiCollection)
{
if( pIcd->uiFlags & (ICD_REQUIRED_PIECE | ICD_REQUIRED_IN_SET))
{
FLMBYTE ucDummy = 0;
// Get the document list entry for this node.
// Build a buffer to set into the Result Set for later verification...
DocXRef.uiIndexNum = pIcd->pIxd->uiIndexNum;
DocXRef.ui64DocId = m_nodeInfo.ui64DocumentId;
DocXRef.uiCollection = uiCollection;
if (RC_BAD( rc = m_pXRefRS->addEntry( NULL, NULL,
(FLMBYTE *)&DocXRef,
sizeof( DOC_IXD_XREF),
&ucDummy, 1)))
{
// It's okay if we get a duplicate entry, only one will be saved in
// the result set.
if (rc == NE_XFLM_NOT_UNIQUE)
{
rc = NE_XFLM_OK;
}
else
{
goto Exit;
}
}
}
}
pIcd = pIcd->pNextInChain;
}
Exit:
return rc;
}
/******************************************************************************
Desc:
******************************************************************************/
FLMUINT64 getLinkVal(
FLMUINT uiBMId,
NODE_RS_ENTRY * pRSEntry
)
{
FLMUINT64 * pui64Ptr = pRSEntry->ui64FieldArray;
FLMUINT16 ui16BitMap = pRSEntry->hdr.ui16BitMap;
FLMUINT64 ui64Link = 0;
if (ui16BitMap & CHK_BM_ROOT_ID)
{
if (uiBMId == CHK_BM_ROOT_ID)
{
ui64Link = *pui64Ptr;
goto Exit;
}
pui64Ptr++;
}
if (ui16BitMap & CHK_BM_PARENT_ID)
{
if (uiBMId == CHK_BM_PARENT_ID)
{
ui64Link = *pui64Ptr;
goto Exit;
}
pui64Ptr++;
}
if (ui16BitMap & CHK_BM_PREV_SIBLING)
{
if (uiBMId == CHK_BM_PREV_SIBLING)
{
ui64Link = *pui64Ptr;
goto Exit;
}
pui64Ptr++;
}
if (ui16BitMap & CHK_BM_NEXT_SIBLING)
{
if (uiBMId == CHK_BM_NEXT_SIBLING)
{
ui64Link = *pui64Ptr;
goto Exit;
}
pui64Ptr++;
}
if (ui16BitMap & CHK_BM_FIRST_CHILD)
{
if (uiBMId == CHK_BM_FIRST_CHILD)
{
ui64Link = *pui64Ptr;
goto Exit;
}
pui64Ptr++;
}
if (ui16BitMap & CHK_BM_LAST_CHILD)
{
if (uiBMId == CHK_BM_LAST_CHILD)
{
ui64Link = *pui64Ptr;
goto Exit;
}
pui64Ptr++;
}
if (ui16BitMap & CHK_BM_ANNOTATION)
{
if (uiBMId == CHK_BM_ANNOTATION)
{
ui64Link = *pui64Ptr;
goto Exit;
}
}
Exit:
return ui64Link;
}
/******************************************************************************
Desc: Verify that the nameId is in the dictionary.
******************************************************************************/
RCODE F_NodeVerifier::verifyNameId(
F_Db * pDb,
eDomNodeType eNodeType,
FLMUINT uiNameId,
F_NameTable * pNameTable,
FLMINT * piErrCode)
{
RCODE rc = NE_XFLM_OK;
FLMUINT uiType;
FLMUINT uiLen;
if( !uiNameId)
{
goto Exit;
}
switch (eNodeType)
{
case DOCUMENT_NODE:
case ELEMENT_NODE:
case DATA_NODE:
case COMMENT_NODE:
case CDATA_SECTION_NODE:
case ANNOTATION_NODE:
{
uiType = ELM_ELEMENT_TAG;
break;
}
case ATTRIBUTE_NODE:
{
uiType = ELM_ATTRIBUTE_TAG;
break;
}
default:
{
flmAssert( 0);
*piErrCode = FLM_UNSUPPORTED_NODE_TYPE;
goto Exit;
}
}
if (RC_BAD( rc = pNameTable->getFromTagTypeAndNum( pDb, uiType,
uiNameId, NULL, NULL, &uiLen, NULL, NULL, NULL, NULL, TRUE)))
{
*piErrCode = FLM_BAD_INVALID_NAME_ID;
goto Exit;
}
Exit:
return rc;
}
/******************************************************************************
Desc: Verify that the prefixId is in the dictionary.
******************************************************************************/
RCODE F_NodeVerifier::verifyPrefixId(
F_Db * pDb,
FLMUINT uiPrefixId,
F_NameTable * pNameTable,
FLMINT * piErrCode
)
{
RCODE rc = NE_XFLM_OK;
FLMUINT uiLen;
if (!uiPrefixId)
{
goto Exit; // Ok.
}
if (RC_BAD( rc = pNameTable->getFromTagTypeAndNum(
pDb, ELM_PREFIX_TAG, uiPrefixId, NULL, NULL, &uiLen)))
{
*piErrCode = FLM_BAD_INVALID_PREFIX_ID;
goto Exit;
}
Exit:
return rc;
}
/******************************************************************************
Desc: Add a key to the result set.
******************************************************************************/
RCODE F_KeyCollector::addKey(
F_Db * pDb,
IXD * pIxd,
KREF_ENTRY * pKref
)
{
RCODE rc = NE_XFLM_OK;
FLMBYTE * pucKey = (FLMBYTE *)&pKref[1];
FLMBYTE * pucData = pucKey + pKref->ui16KeyLen;
FLMUINT uiDataLen;
flmAssert( pKref->ui16KeyLen <= XFLM_MAX_KEY_SIZE);
// Can't store an entry with zero length data.
if ((uiDataLen = pKref->uiDataLen) == 0)
{
*pucData = 0;
uiDataLen = 1;
}
// Save the key in the result set.
if (RC_BAD( rc = m_pDbCheck->m_pIxRSet->addEntry( pDb, pIxd, pucKey,
(FLMUINT)pKref->ui16KeyLen,
pucData, uiDataLen)))
{
if (rc == NE_XFLM_NOT_UNIQUE)
{
rc = NE_XFLM_OK;
}
goto Exit;
}
m_ui64TotalKeys++;
Exit:
return( rc);
}