git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@547 0109f412-320b-0410-ab79-c3e0c5ffbbe6
4919 lines
108 KiB
C++
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);
|
|
}
|
|
|