Files
mars-flaim/flaim/src/flchktr.cpp
ahodgkinson e2005fa114 Changes for HP/UX.
git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@183 0109f412-320b-0410-ab79-c3e0c5ffbbe6
2006-03-16 22:55:50 +00:00

2350 lines
54 KiB
C++

//-------------------------------------------------------------------------
// Desc: Check database b-trees for physical corruptions.
// Tabs: 3
//
// Copyright (c) 1991-1992,1995-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: flchktr.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $
//-------------------------------------------------------------------------
#include "flaimsys.h"
FSTATIC RCODE chkReadBlkFromDisk(
FILE_HDR * pFileHdr,
F_SuperFileHdl * pSFileHdl,
FLMUINT uiFilePos,
FLMUINT uiBlkAddress,
LFILE * pLFile,
FFILE * pFile,
FLMBYTE * pBlk);
FSTATIC RCODE chkVerifyElmFields(
STATE_INFO * pStateInfo,
DB_INFO * pDbInfo,
IX_CHK_INFO * pIxChkInfo,
POOL * pTmpPool,
FLMUINT * puiErrElmRecOffsetRV,
eCorruptionType * peElmErrCorruptCode);
FSTATIC RCODE chkVerifySubTree(
DB_INFO * pDbInfo,
IX_CHK_INFO * pIxChkInfo,
STATE_INFO * ParentState,
STATE_INFO * pStateInfo,
FLMUINT uiBlkAddress,
POOL * pTmpPool,
FLMBYTE * pucResetKey,
FLMUINT uiResetKeyLen,
FLMUINT uiResetDrn);
FSTATIC RCODE chkGetLfInfo(
DB_INFO * pDbInfo,
POOL * pPool,
LF_STATS * pLfStats,
LFILE * pLFile,
LF_STATS * pCurrLfStats,
FLMBOOL * pbCurrLfLevelChangedRV);
FSTATIC RCODE chkSetupLfTable(
DB_INFO * pDbInfo,
POOL * pPool);
FSTATIC RCODE chkSetupIxInfo(
DB_INFO * pDbInfo,
IX_CHK_INFO * pIxInfoRV);
FSTATIC RCODE chkOutputIndexKeys(
STATE_INFO * pStateInfo,
IX_CHK_INFO * pIxChkInfo,
IXD * pIxd,
REC_KEY * pKeyList);
/****************************************************************************
Desc: Frees memory allocated to a result set and deletes any temporary files
that may have been created.
****************************************************************************/
FINLINE void chkRSFree(
void ** ppRSetRV)
{
FResultSet * pRSet;
pRSet = *((FResultSet **)ppRSetRV);
pRSet->Release();
*ppRSetRV = (void *)0;
}
/****************************************************************************
Desc: Adds a variable-length entry to a result set.
****************************************************************************/
FINLINE RCODE chkRSAddEntry(
void * pRSet,
FLMBYTE * pEntry,
FLMUINT uiEntryLength)
{
return( ((FResultSet *)pRSet)->AddEntry( pEntry, uiEntryLength));
}
/****************************************************************************
Desc: This routine counts the number of fields in an object table.
****************************************************************************/
FINLINE void chkCountFields(
FDICT * pDict,
FLMUINT * puiNumFieldsRV)
{
FLMUINT uiTblSize = pDict->uiIttCnt;
ITT * pItt = pDict->pIttTbl;
FLMUINT uiCount = 0;
FLMUINT uiCurrObj;
for (uiCurrObj = 0; uiCurrObj < uiTblSize; uiCurrObj++, pItt++)
{
if( ITT_IS_FIELD( pItt))
{
uiCount++;
}
}
(*puiNumFieldsRV) += uiCount;
}
/****************************************************************************
Desc: Frees memory allocated to an IX_CHK_INFO structure
****************************************************************************/
FINLINE RCODE chkFreeIxInfo(
IX_CHK_INFO * pIxInfoRV)
{
GedPoolFree( &(pIxInfoRV->pool));
chkRSFree( &(pIxInfoRV->pRSet));
f_free( &(pIxInfoRV->puiIxArray));
f_memset( pIxInfoRV, 0, sizeof( IX_CHK_INFO));
return FERR_OK;
}
/********************************************************************
Desc:
*********************************************************************/
RCODE chkBlkRead(
DB_INFO * pDbInfo,
FLMUINT uiBlkAddress,
LFILE * pLFile,
FLMBYTE ** ppBlk,
SCACHE ** ppSCache,
eCorruptionType * peCorruption
)
{
FDB * pDb = pDbInfo->pDb;
FILE_HDR * pFileHdr = &pDb->pFile->FileHdr;
RCODE rc = FERR_OK;
if( *ppSCache)
{
ScaReleaseCache( *ppSCache, FALSE);
*ppSCache = NULL;
*ppBlk = NULL;
}
else if( *ppBlk)
{
f_free( ppBlk);
*ppBlk = NULL;
}
if( pDb->uiKilledTime)
{
rc = RC_SET( FERR_OLD_VIEW);
goto Exit;
}
/*
Get the block from cache.
*/
if( RC_OK( rc = ScaGetBlock( pDb, pLFile, 0,
uiBlkAddress, NULL, ppSCache)))
{
*ppBlk = (*ppSCache)->pucBlk;
}
else
{
/*
Try to read the block directly from disk.
*/
FLMUINT uiBlkLen = pFileHdr->uiBlockSize;
FLMUINT uiTransID;
FLMBYTE * pucBlk;
FLMUINT uiLastReadTransID;
FLMUINT uiPrevBlkAddr;
FLMUINT uiFilePos;
/*
If we didn't get a corruption error, jump to exit.
*/
if( !FlmErrorIsFileCorrupt( rc))
{
goto Exit;
}
/*
Allocate memory for the block.
*/
if( RC_BAD( rc = f_calloc( uiBlkLen, ppBlk)))
{
goto Exit;
}
pucBlk = *ppBlk;
uiFilePos = uiBlkAddress;
uiTransID = pDb->LogHdr.uiCurrTransID;
uiLastReadTransID = 0xFFFFFFFF;
// Follow version chain until we find version we need.
for (;;)
{
if( RC_BAD( rc = chkReadBlkFromDisk( pFileHdr, pDbInfo->pSFileHdl,
uiFilePos, uiBlkAddress, pLFile, pDb->pFile, pucBlk)))
{
goto Exit;
}
/*
See if we can use the current version of the block, or if we
must go get a previous version.
*/
if( (FLMUINT)FB2UD( &pucBlk [BH_TRANS_ID]) <= uiTransID)
{
break;
}
// If the transaction ID is greater than or equal to the last
// one we read, we have a corruption. This test will keep us
// from looping around forever.
if ((FLMUINT)FB2UD( &pucBlk [BH_TRANS_ID]) >= uiLastReadTransID)
{
rc = RC_SET( FERR_DATA_ERROR);
goto Exit;
}
uiLastReadTransID = (FLMUINT)FB2UD( &pucBlk [BH_TRANS_ID]);
// Block is too new, go for next older version.
// If previous block address is same as current file position or
// zero, we have a problem.
uiPrevBlkAddr = (FLMUINT)FB2UD( &pucBlk [BH_PREV_BLK_ADDR]);
if ((uiPrevBlkAddr == uiFilePos) || (!uiPrevBlkAddr))
{
rc = (pDb->uiKilledTime)
? RC_SET( FERR_OLD_VIEW)
: RC_SET( FERR_DATA_ERROR);
goto Exit;
}
uiFilePos = uiPrevBlkAddr;
}
/* See if we even got the block we thought we wanted. */
if (GET_BH_ADDR( pucBlk) != uiBlkAddress)
{
rc = RC_SET( FERR_DATA_ERROR);
goto Exit;
}
}
Exit:
*peCorruption = FLM_NO_CORRUPTION;
if (RC_BAD( rc))
{
switch (rc)
{
case FERR_DATA_ERROR:
*peCorruption = FLM_COULD_NOT_SYNC_BLK;
break;
case FERR_BLOCK_CHECKSUM:
*peCorruption = FLM_BAD_BLK_CHECKSUM;
break;
default:
break;
}
}
return( rc);
}
/************************************************************************
Desc:
*************************************************************************/
FSTATIC RCODE chkReadBlkFromDisk(
FILE_HDR * pFileHdr,
F_SuperFileHdl * pSFileHdl,
FLMUINT uiFilePos,
FLMUINT uiBlkAddress,
LFILE * pLFile,
FFILE * pFile,
FLMBYTE * pBlk)
{
RCODE rc = FERR_OK;
FLMUINT uiBytesRead;
FLMUINT uiBlkLen = pFileHdr->uiBlockSize;
if (RC_BAD( rc = pSFileHdl->ReadBlock( uiFilePos,
uiBlkLen, pBlk, &uiBytesRead)))
{
if (rc == FERR_IO_END_OF_FILE)
{
rc = RC_SET( FERR_DATA_ERROR);
}
goto Exit;
}
if( uiBytesRead < uiBlkLen)
{
rc = RC_SET( FERR_DATA_ERROR);
goto Exit;
}
/* Verify the block checksum BEFORE decrypting or using any data. */
if( RC_BAD( rc = BlkCheckSum( pBlk, CHECKSUM_CHECK,
uiBlkAddress, uiBlkLen)))
{
goto Exit;
}
// If this is an index block it may be encrypted, we
// need to decrypt it before we can use it.
// The function ScaDecryptBlock will check if the index
// is encrypted first. If not, it will return.
if (pLFile && pLFile->uiLfType == LF_INDEX)
{
if (RC_BAD( rc = ScaDecryptBlock( pFile, pBlk)))
{
goto Exit;
}
}
Exit:
return( rc);
}
/********************************************************************
Desc:
*********************************************************************/
FSTATIC RCODE chkVerifyElmFields(
STATE_INFO * pStateInfo,
DB_INFO * pDbInfo,
IX_CHK_INFO * pIxChkInfo,
POOL * pTmpPool,
FLMUINT * puiErrElmRecOffsetRV,
eCorruptionType * peElmCorruptCode)
{
FLMBYTE * pValue = pStateInfo->pValue;
FLMBYTE * pData = pStateInfo->pData;
FLMBYTE * pTempValue;
FlmRecord * pRecord = pStateInfo->pRecord;
FLMBOOL bKRefAbortRec = FALSE;
FLMUINT uiSaveElmRecOffset = 0;
void * pDbPoolMark = NULL;
void * pKeyMark = NULL;
FLMBOOL bResetDbPool = FALSE;
RCODE rc = FERR_OK;
void * pvField = pStateInfo->pvField;
REC_KEY * pKeyList = NULL;
REC_KEY * pTmpKey;
*peElmCorruptCode = FLM_NO_CORRUPTION;
pTempValue = (pValue)
? (FLMBYTE *)&pValue [pStateInfo->uiFieldProcessedLen]
: (FLMBYTE *)NULL;
while ((*peElmCorruptCode == FLM_NO_CORRUPTION) &&
(pStateInfo->uiElmRecOffset < pStateInfo->uiElmRecLen))
{
uiSaveElmRecOffset = pStateInfo->uiElmRecOffset;
if ((*peElmCorruptCode = flmVerifyElmFOP( pStateInfo)) != FLM_NO_CORRUPTION)
{
break;
}
if (!pStateInfo->bElmRecOK)
{
pValue = pTempValue = NULL;
if (pRecord)
{
pRecord->clear();
}
continue;
}
switch (pStateInfo->uiFOPType)
{
case FLM_FOP_CONT_DATA:
if ((pTempValue != NULL) && (pStateInfo->uiFOPDataLen))
{
f_memcpy( pTempValue,
pStateInfo->pFOPData, pStateInfo->uiFOPDataLen);
pTempValue += pStateInfo->uiFOPDataLen;
}
break;
case FLM_FOP_STANDARD:
case FLM_FOP_OPEN:
case FLM_FOP_TAGGED:
case FLM_FOP_NO_VALUE:
if( pValue)
{
pValue = pTempValue = NULL;
}
if (pvField)
{
pvField = NULL;
}
if (!pRecord)
{
if( (pRecord = f_new FlmRecord) == NULL)
{
rc = RC_SET( FERR_MEM);
goto Exit;
}
}
if (RC_BAD( rc = pRecord->insertLast( pStateInfo->uiFieldLevel,
pStateInfo->uiFieldNum,
pStateInfo->uiFieldType, &pvField)))
{
goto Exit;
}
if( pStateInfo->uiFieldLen)
{
if (RC_BAD( rc = pRecord->allocStorageSpace( pvField,
pStateInfo->uiFieldType,
pStateInfo->uiFieldLen,
0,
0,
0,
&pValue,
NULL)))
{
goto Exit;
}
pTempValue = pValue;
}
if ((pTempValue) && (pStateInfo->uiFOPDataLen))
{
f_memcpy( pTempValue, pStateInfo->pFOPData,
pStateInfo->uiFOPDataLen);
pTempValue += pStateInfo->uiFOPDataLen;
}
break;
case FLM_FOP_ENCRYPTED:
{
if( pValue)
{
pValue = pTempValue = NULL;
}
if( pData)
{
pData = NULL;
}
if (pvField)
{
pvField = NULL;
}
if (!pRecord)
{
if( (pRecord = f_new FlmRecord) == NULL)
{
rc = RC_SET( FERR_MEM);
goto Exit;
}
}
if (RC_BAD( rc = pRecord->insertLast( pStateInfo->uiFieldLevel,
pStateInfo->uiFieldNum,
pStateInfo->uiFieldType, &pvField)))
{
goto Exit;
}
if( pStateInfo->uiFieldLen)
{
if (RC_BAD( rc = pRecord->allocStorageSpace( pvField,
pStateInfo->uiFieldType,
pStateInfo->uiFieldLen,
pStateInfo->uiEncFieldLen,
pStateInfo->uiEncId,
FLD_HAVE_ENCRYPTED_DATA,
&pData,
&pValue)))
{
goto Exit;
}
pTempValue = pValue;
}
if ((pTempValue) && (pStateInfo->uiFOPDataLen))
{
f_memcpy( pTempValue, pStateInfo->pFOPData,
pStateInfo->uiFOPDataLen);
pTempValue += pStateInfo->uiFOPDataLen;
}
break;
}
case FLM_FOP_JUMP_LEVEL:
break;
}
if ((!pStateInfo->uiEncId && pStateInfo->uiFieldProcessedLen == pStateInfo->uiFieldLen) ||
(pStateInfo->uiEncId && pStateInfo->uiFieldProcessedLen == pStateInfo->uiEncFieldLen))
{
/*
The whole field has been retrieved. Verify the field and
graft it into the record being built.
*/
if (pValue && (pDbInfo->uiFlags & FLM_CHK_FIELDS))
{
if (pStateInfo->uiFieldType == 0xFF)
{
// Hit Rec Info object - don't care what's in it - must not
// assert, because this would kill our ability to check
// older versions of the database which have REC_INFO
// data in them.
*peElmCorruptCode = FLM_NO_CORRUPTION;
}
else
{
if (!pStateInfo->uiEncId)
{
*peElmCorruptCode = flmVerifyField( pValue,
pStateInfo->uiFieldLen, pStateInfo->uiFieldType);
}
else
{
// Decrypt the field and store the decrypted data.
if (!pStateInfo->pDb->pFile->bInLimitedMode)
{
if (RC_BAD( rc = flmDecryptField( pStateInfo->pDb->pDict,
pRecord, pvField, pStateInfo->uiEncId, pTmpPool)))
{
goto Exit;
}
*peElmCorruptCode = flmVerifyField( pData,
pStateInfo->uiFieldLen, pStateInfo->uiFieldType);
}
else
{
// If we can't decrypt the field, then just pass
// it for now.
*peElmCorruptCode = FLM_NO_CORRUPTION;
}
}
}
}
else
{
*peElmCorruptCode = FLM_NO_CORRUPTION;
}
pValue = pTempValue = NULL;
}
// If this is the last element of the record, verify the
// record's keys.
if( BBE_IS_LAST( pStateInfo->pElm) &&
(pStateInfo->uiElmRecOffset == pStateInfo->uiElmRecLen))
{
pValue = pTempValue = NULL;
if( !pDbInfo->pProgress->bPhysicalCorrupt && (pDbInfo->uiFlags & FLM_CHK_INDEX_REFERENCING))
{
FLMUINT uiLoop;
IXD * pIxd;
if( pStateInfo->pLogicalFile->pLFile->uiLfType == LF_CONTAINER)
{
// Need to mark the DB's temporary pool. The index code
// allocates memory for new CDL entries from the DB pool. If
// the pool is not reset, it grows during the check and
// becomes VERY large.
pDbPoolMark = GedPoolMark( &(pDbInfo->pDb->TempPool));
bResetDbPool = TRUE;
/*
Set up the KRef table so that flmGetRecKeys
will work correctly.
*/
if( RC_BAD( rc = KrefCntrlCheck( pStateInfo->pDb)))
{
goto Exit;
}
bKRefAbortRec = TRUE;
for( uiLoop = 0; uiLoop < pIxChkInfo->uiIxCount; uiLoop++)
{
if( RC_BAD( rc = fdictGetIndex(
pStateInfo->pDb->pDict,
pStateInfo->pDb->pFile->bInLimitedMode,
pIxChkInfo->puiIxArray[ uiLoop], NULL, &pIxd, TRUE)))
{
goto Exit;
}
if( pIxd->uiFlags & IXD_OFFLINE)
{
continue;
}
if( pIxd->uiContainerNum ==
pStateInfo->pLogicalFile->pLFile->uiLfNum ||
!pIxd->uiContainerNum)
{
/*
Mark the field pool so that it can be reset
after the record keys have been generated
and output.
*/
pKeyMark = GedPoolMark( pTmpPool);
/*
Build the record keys for the current index.
Do not remove duplicate keys. The result set
will remove any duplicates.
*/
if (RC_BAD( rc = flmGetRecKeys(
pStateInfo->pDb, pIxd, pRecord,
pStateInfo->pLogicalFile->pLFile->uiLfNum,
FALSE, pTmpPool, &pKeyList)))
{
goto Exit;
}
/*
If the record generated keys for the current
index, output the keys to the result set.
*/
if( pKeyList)
{
if( RC_BAD( rc = chkOutputIndexKeys(
pStateInfo, pIxChkInfo, pIxd, pKeyList)))
{
goto Exit;
}
pTmpKey = pKeyList;
while( pTmpKey)
{
pTmpKey->pKey->Release();
pTmpKey->pKey = NULL;
pTmpKey = pTmpKey->pNextKey;
}
pKeyList = NULL;
}
/* Reset the field pool */
GedPoolReset( pTmpPool, pKeyMark);
}
}
/*
Clean up any keys that may have been added to the
KRef table.
*/
KYAbortCurrentRecord( pStateInfo->pDb);
bKRefAbortRec = FALSE;
/* Reset the DB's temporary pool */
(void)GedPoolReset( &(pDbInfo->pDb->TempPool), pDbPoolMark);
bResetDbPool = FALSE;
}
}
if (pRecord)
{
pRecord->clear();
}
pValue = pTempValue = NULL;
GedPoolReset( pTmpPool, NULL);
}
if( *peElmCorruptCode != FLM_NO_CORRUPTION)
{
pStateInfo->bElmRecOK = FALSE;
}
}
Exit:
/*
Clean up any keys that may have been added to the
KRef table. This is a fail-safe case to clean up the
KRef in case KYKeysCommit didn't get called above.
*/
if (bKRefAbortRec)
{
KYAbortCurrentRecord( pStateInfo->pDb);
}
/*
Free any keys in the key list
*/
if( pKeyList)
{
pTmpKey = pKeyList;
while( pTmpKey)
{
pTmpKey->pKey->Release();
pTmpKey->pKey = NULL;
pTmpKey = pTmpKey->pNextKey;
}
}
/* Reset the DB's temporary pool */
if( bResetDbPool)
{
(void)GedPoolReset( &(pDbInfo->pDb->TempPool), pDbPoolMark);
}
if( *peElmCorruptCode != FLM_NO_CORRUPTION || RC_BAD( rc))
{
pValue = pTempValue = NULL;
GedPoolReset( pTmpPool, NULL);
if (pRecord)
{
pRecord->clear();
}
}
pStateInfo->pValue = pValue;
pStateInfo->pData = pData;
pStateInfo->pvField = pvField;
pStateInfo->pRecord = pRecord;
if( *peElmCorruptCode != FLM_NO_CORRUPTION)
{
*puiErrElmRecOffsetRV = uiSaveElmRecOffset;
}
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.
*****************************************************************************/
FSTATIC RCODE chkVerifySubTree(
DB_INFO * pDbInfo,
IX_CHK_INFO * pIxChkInfo,
STATE_INFO * pParentState,
STATE_INFO * pStateInfo,
FLMUINT uiBlkAddress,
POOL * pTmpPool,
FLMBYTE * pucResetKey,
FLMUINT uiResetKeyLen,
FLMUINT uiResetDrn)
{
RCODE rc = FERR_OK;
SCACHE * pSCache = NULL;
FLMBYTE * pBlk = NULL;
FLMUINT uiLevel = pStateInfo->uiLevel;
FLMUINT uiBlkType = pStateInfo->uiBlkType;
FLMUINT uiLfType = pStateInfo->pLogicalFile->pLFile->uiLfType;
FLMUINT uiBlockSize =
pDbInfo->pDb->pFile->FileHdr.uiBlockSize;
FLMUINT uiParentBlkAddress;
FLMBYTE * pChildBlkAddr;
FLMUINT uiChildBlkAddress;
FLMUINT uiPrevNextBlkAddress;
eCorruptionType eElmCorruptCode;
eCorruptionType eBlkCorruptionCode = FLM_NO_CORRUPTION;
eCorruptionType eLastCorruptCode = FLM_NO_CORRUPTION;
FLMUINT uiNumErrors = 0;
FLMUINT uiErrElmRecOffset = 0;
FLMUINT64 ui64SaveKeyCount = 0;
FLMUINT64 ui64SaveKeyRefs = 0;
BLOCK_INFO SaveBlkInfo;
BLOCK_INFO * pBlkInfo;
FLMBOOL bProcessElm;
FLMBOOL bCountElm;
FLMBOOL bDescendToChildBlocks;
FLMINT iCompareStatus;
eCorruptionType eHdrCorruptCode;
/* Setup the state information. */
pStateInfo->pBlk = NULL;
pStateInfo->uiBlkAddress = uiBlkAddress;
uiPrevNextBlkAddress = pStateInfo->uiNextBlkAddr;
uiParentBlkAddress = (pParentState)
? pParentState->uiBlkAddress
: BT_END;
f_yieldCPU();
/* Read the sub-tree root block into memory. */
bDescendToChildBlocks = TRUE;
if (RC_BAD( rc = chkBlkRead( pDbInfo,
uiBlkAddress,
pStateInfo->pLogicalFile ? pStateInfo->pLogicalFile->pLFile : NULL,
&pBlk, &pSCache,&eBlkCorruptionCode)))
{
if (eBlkCorruptionCode != FLM_NO_CORRUPTION)
{
uiNumErrors++;
eLastCorruptCode = eBlkCorruptionCode;
chkReportError( pDbInfo, eBlkCorruptionCode, LOCALE_B_TREE,
pDbInfo->pProgress->uiLfNumber,
pDbInfo->pProgress->uiLfType,
uiLevel, uiBlkAddress,
uiParentBlkAddress, 0, 0, 0xFFFF, 0, pBlk);
if (eBlkCorruptionCode == FLM_BAD_BLK_CHECKSUM)
{
bDescendToChildBlocks = FALSE;
/*
Allow to continue the check, but if this is a non-leaf block
a non-zero eBlkCorruptionCode will prevent us from descending to
child blocks. Set rc to SUCCESS so we won't goto Exit below.
*/
rc = FERR_OK;
}
else if (eBlkCorruptionCode == FLM_COULD_NOT_SYNC_BLK)
{
eLastCorruptCode = eBlkCorruptionCode;
/*
Need the goto here, because rc is changed to SUCCESS,
and the goto below would get skipped.
*/
rc = FERR_OK;
goto fix_state;
}
}
else if (rc == FERR_OLD_VIEW)
{
pDbInfo->bReposition = TRUE;
}
/* If rc was not changed to SUCCESS above, goto Exit. */
if (RC_BAD( rc))
goto Exit;
}
pStateInfo->pBlk = pBlk;
/* Verify the block header */
/* Don't recount the block if we are resetting. */
if (!uiResetKeyLen)
{
pDbInfo->pProgress->ui64BytesExamined += (FLMUINT64)uiBlockSize;
pBlkInfo = &pStateInfo->BlkInfo;
}
else
{
pBlkInfo = NULL;
}
chkCallProgFunc( pDbInfo);
/* Check the block header. */
if ((eHdrCorruptCode =
flmVerifyBlockHeader( pStateInfo, pBlkInfo, uiBlockSize,
(pParentState == NULL)
? BT_END
: 0,
(pParentState == NULL)
? BT_END
: pParentState->uiLastChildAddr,
TRUE, TRUE)) == FLM_NO_CORRUPTION)
{
/*
Verify the previous block's next block address -- it should
equal the current block's address.
*/
if ((uiPrevNextBlkAddress) &&
(uiPrevNextBlkAddress != uiBlkAddress))
{
eHdrCorruptCode = FLM_BAD_PREV_BLK_NEXT;
}
}
if (eHdrCorruptCode != FLM_NO_CORRUPTION)
{
eLastCorruptCode = eHdrCorruptCode;
uiNumErrors++;
chkReportError( pDbInfo, eHdrCorruptCode, LOCALE_B_TREE,
pDbInfo->pProgress->uiLfNumber,
pDbInfo->pProgress->uiLfType,
uiLevel, uiBlkAddress,
uiParentBlkAddress, 0, 0, 0xFFFF, 0,
pBlk);
}
/* Go through the elements in the block. */
pStateInfo->uiElmOffset = BH_OVHD;
while ((pStateInfo->uiElmOffset < pStateInfo->uiEndOfBlock) &&
(RC_OK( pDbInfo->LastStatusRc)))
{
/*
If we are resetting, save any statistical information so we
can back it out if we need to.
*/
if (uiResetKeyLen)
{
ui64SaveKeyCount = pStateInfo->ui64KeyCount;
ui64SaveKeyRefs = pStateInfo->ui64KeyRefs;
f_memcpy( &SaveBlkInfo, &pStateInfo->BlkInfo, sizeof( BLOCK_INFO));
bCountElm = FALSE;
bProcessElm = FALSE;
}
else
{
bCountElm = TRUE;
bProcessElm = TRUE;
}
pStateInfo->BlkInfo.ui64ElementCount++;
if ((eElmCorruptCode = flmVerifyElement( pStateInfo, pDbInfo->uiFlags)) != FLM_NO_CORRUPTION)
{
/* Report any errors in the element. */
eLastCorruptCode = eElmCorruptCode;
uiNumErrors++;
if (RC_BAD( rc = chkReportError( pDbInfo, eElmCorruptCode,
LOCALE_B_TREE,
pDbInfo->pProgress->uiLfNumber,
pDbInfo->pProgress->uiLfType,
uiLevel, uiBlkAddress,
uiParentBlkAddress,
pStateInfo->uiElmOffset, pStateInfo->uiElmDrn,
0xFFFF, 0, pBlk)))
{
break;
}
}
/* Keep track of the number of continuation elements. */
if ((uiBlkType == BHT_LEAF) &&
(!BBE_IS_FIRST( pStateInfo->pElm)) &&
(pStateInfo->uiElmLen != BBE_LEM_LEN))
{
pStateInfo->BlkInfo.ui64ContElementCount++;
pStateInfo->BlkInfo.ui64ContElmBytes += pStateInfo->uiElmLen;
}
/* Do some further checking. */
if (eElmCorruptCode != FLM_NO_CORRUPTION)
{
pStateInfo->bElmRecOK = FALSE;
}
else
{
/* See if we are resetting */
iCompareStatus = 0;
if ((uiResetKeyLen) &&
(pStateInfo->bValidKey) &&
((!pStateInfo->uiCurKeyLen) ||
((iCompareStatus = flmCompareKeys( pStateInfo->pCurKey,
pStateInfo->uiCurKeyLen,
pucResetKey, uiResetKeyLen)) >= 0)))
{
if (iCompareStatus > 0)
{
if (uiBlkType == BHT_LEAF)
{
uiResetKeyLen = 0;
pucResetKey = NULL;
uiResetDrn = 0;
bCountElm = TRUE;
}
bProcessElm = TRUE;
}
else if( uiLfType == LF_INDEX)
{
FLMBYTE * pTmpElm = pStateInfo->pElm;
FLMUINT uiLowestDrn = FSGetDomain( &pTmpElm,
pStateInfo->uiElmOvhd);
if( uiResetDrn >= uiLowestDrn)
{
bProcessElm = TRUE;
bCountElm = TRUE;
}
}
else
{
/* Processing a container */
bProcessElm = TRUE;
}
}
if (uiBlkType == BHT_LEAF)
{
/* No need to parse LEM element. */
if ((pStateInfo->uiCurKeyLen != 0) && (pStateInfo->bValidKey))
{
if (uiLfType == LF_CONTAINER)
{
if (pStateInfo->uiElmDrn != DRN_LAST_MARKER)
{
if( RC_BAD( rc = chkVerifyElmFields( pStateInfo,
pDbInfo, pIxChkInfo, pTmpPool,
&uiErrElmRecOffset, &eElmCorruptCode)))
{
goto Exit;
}
}
}
else if( bProcessElm)
{
uiErrElmRecOffset = 0xFFFF;
if( !pDbInfo->pProgress->bPhysicalCorrupt && (pDbInfo->uiFlags & FLM_CHK_INDEX_REFERENCING))
{
if(( RC_BAD( rc = flmVerifyIXRefs( pStateInfo, pIxChkInfo,
uiResetDrn, &eElmCorruptCode))) ||
(pDbInfo->bReposition))
{
goto Exit;
}
}
else
{
if(( RC_BAD( rc = flmVerifyIXRefs( pStateInfo, NULL,
uiResetDrn, &eElmCorruptCode))) ||
(pDbInfo->bReposition))
{
goto Exit;
}
}
}
}
if( bProcessElm)
{
uiResetKeyLen = 0;
pucResetKey = NULL;
uiResetDrn = 0;
if (eElmCorruptCode != FLM_NO_CORRUPTION)
{
/* Report any errors in the element. */
eLastCorruptCode = eElmCorruptCode;
uiNumErrors++;
chkReportError( pDbInfo, eElmCorruptCode,
LOCALE_B_TREE,
pDbInfo->pProgress->uiLfNumber,
pDbInfo->pProgress->uiLfType,
uiLevel, uiBlkAddress,
uiParentBlkAddress,
pStateInfo->uiElmOffset,
pStateInfo->uiElmDrn,
uiErrElmRecOffset,
pStateInfo->uiFieldNum,
pBlk);
if (RC_BAD( pDbInfo->LastStatusRc))
{
break;
}
}
}
}
else
{
if (uiBlkType == BHT_NON_LEAF_DATA)
{
pChildBlkAddr = &pStateInfo->pElm [BNE_DATA_CHILD_BLOCK];
}
else
{
pChildBlkAddr = &pStateInfo->pElm [BNE_CHILD_BLOCK];
}
uiChildBlkAddress = (FLMUINT)FB2UD( pChildBlkAddr );
/*
Check the child sub-tree -- NOTE, this is a recursive call.
First see if we have a pucResetKey that we want to position
to. If so, make sure we are positioned to it before
descending to the child block.
*/
if (bProcessElm)
{
if (!bDescendToChildBlocks)
{
rc = FERR_OK;
}
else
{
rc = chkVerifySubTree( pDbInfo, pIxChkInfo, pStateInfo,
(pStateInfo - 1), uiChildBlkAddress, pTmpPool,
pucResetKey, uiResetKeyLen, uiResetDrn);
}
if ((RC_BAD( rc)) ||
(RC_BAD( pDbInfo->LastStatusRc)) ||
(pDbInfo->bReposition))
{
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 = 0;
pucResetKey = NULL;
uiResetDrn = 0;
}
/* Save the child block address in the level information. */
pStateInfo->uiLastChildAddr = 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));
}
/* Go to the next element. */
pStateInfo->uiElmOffset += pStateInfo->uiElmLen;
}
/* Verify that we ended exactly on the end of the block. */
if ((eLastCorruptCode == FLM_NO_CORRUPTION) &&
(pStateInfo->uiEndOfBlock >= BH_OVHD) &&
(pStateInfo->uiEndOfBlock <= uiBlockSize) &&
(pStateInfo->uiElmOffset > pStateInfo->uiEndOfBlock))
{
eLastCorruptCode = FLM_BAD_ELM_END;
uiNumErrors++;
chkReportError( pDbInfo, eLastCorruptCode, LOCALE_B_TREE,
pDbInfo->pProgress->uiLfNumber,
pDbInfo->pProgress->uiLfType,
uiLevel, uiBlkAddress,
uiParentBlkAddress,
pStateInfo->uiElmOffset, 0, 0xFFFF, 0,
pBlk);
}
/* Verify that the last key in the block matches the parent's key. */
if ((eLastCorruptCode == FLM_NO_CORRUPTION) && (pParentState) &&
(RC_OK( pDbInfo->LastStatusRc)))
{
if ((pStateInfo->bValidKey) && (pParentState->bValidKey) &&
(flmCompareKeys( pStateInfo->pCurKey, pStateInfo->uiCurKeyLen,
pParentState->pCurKey,
pParentState->uiCurKeyLen) != 0))
{
eLastCorruptCode = FLM_BAD_PARENT_KEY;
uiNumErrors++;
chkReportError( pDbInfo, eLastCorruptCode, LOCALE_B_TREE,
pDbInfo->pProgress->uiLfNumber,
pDbInfo->pProgress->uiLfType,
uiLevel, uiBlkAddress,
uiParentBlkAddress,
0, 0, 0xFFFF, 0, pBlk);
}
}
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 (eLastCorruptCode != FLM_NO_CORRUPTION)
{
pStateInfo->BlkInfo.eCorruption = eLastCorruptCode;
pStateInfo->BlkInfo.uiNumErrors += uiNumErrors;
/*
Reset all child block states.
*/
for( ;;)
{
pStateInfo->uiNextBlkAddr = 0;
pStateInfo->uiLastChildAddr = 0;
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( pBlk)
{
f_free( &pBlk);
}
pStateInfo->pBlk = NULL;
return( rc);
}
/***************************************************************************
Desc: This routine reads the LFH areas from disk to make sure they are up
to date in memory.
*****************************************************************************/
FSTATIC RCODE chkGetLfInfo(
DB_INFO * pDbInfo,
POOL * pPool,
LF_STATS * pLfStats,
LFILE * pLFile,
LF_STATS * pCurrLfStats,
FLMBOOL * pbCurrLfLevelChangedRV)
{
SCACHE * pSCache = NULL;
FLMBYTE * pBlk = NULL;
FLMUINT uiSaveLevel;
eCorruptionType eBlkCorruptionCode;
RCODE rc = FERR_OK;
/* Read in the block containing the logical file header. */
if (RC_BAD( rc = chkBlkRead( pDbInfo,
pLFile->uiBlkAddress, pLFile, &pBlk, &pSCache,
&eBlkCorruptionCode)))
{
/* Log the error. */
if (eBlkCorruptionCode != FLM_NO_CORRUPTION)
{
//Bug #22003
chkReportError( pDbInfo, eBlkCorruptionCode, LOCALE_LFH_LIST,
0, 0, 0xFF,
pLFile->uiBlkAddress, 0, 0, 0,
0xFFFF, 0, pBlk);
}
goto Exit;
}
/* Copy the LFH from the block to the LFILE. */
uiSaveLevel = pLfStats->uiNumLevels;
if (RC_BAD( rc = flmBufferToLFile(
&pBlk[ pLFile->uiOffsetInBlk], pLFile,
pLFile->uiBlkAddress,
pLFile->uiOffsetInBlk)))
{
goto Exit;
}
/*
Read root block to get the number of levels in the B-TREE
*/
if (pLFile->uiRootBlk == BT_END)
{
pLfStats->uiNumLevels = 0;
}
else
{
if (RC_BAD( rc = chkBlkRead( pDbInfo,
pLFile->uiRootBlk, pLFile, &pBlk, &pSCache,
&eBlkCorruptionCode)))
{
if (eBlkCorruptionCode != FLM_NO_CORRUPTION)
{
//Bug #22003
chkReportError( pDbInfo, eBlkCorruptionCode, LOCALE_B_TREE,
pLFile->uiLfNum, pLFile->uiLfType, 0xFF,
pLFile->uiRootBlk, 0, 0, 0,
0xFFFF, 0, pBlk);
}
goto Exit;
}
pLfStats->uiNumLevels = (FLMUINT)(pBlk [BH_LEVEL]) + 1;
/*
GW Bug 55264: Need to make sure that the level extracted from
the block is valid.
*/
if( pBlk [BH_LEVEL] >= BH_MAX_LEVELS)
{
chkReportError( pDbInfo, FLM_BAD_BLK_HDR_LEVEL, LOCALE_B_TREE,
pLFile->uiLfNum, pLFile->uiLfType,
(FLMUINT)(pBlk [BH_LEVEL]),
pLFile->uiRootBlk, 0, 0, 0,
0xFFFF, 0, pBlk);
/*
Force pLfStats->uiNumLevels to 1 so that we don't crash
*/
pLfStats->uiNumLevels = 1;
}
}
/*
If the number of levels changed, reset the level information
on this logical file.
*/
if (uiSaveLevel != pLfStats->uiNumLevels && pLfStats->uiNumLevels)
{
if (pLfStats->uiNumLevels > uiSaveLevel)
{
if ((pLfStats->pLevelInfo =
(LEVEL_INFO *)GedPoolCalloc( pPool,
(FLMUINT)(sizeof( LEVEL_INFO) * pLfStats->uiNumLevels))) == NULL)
{
rc = RC_SET( FERR_MEM);
goto Exit;
}
}
if (pCurrLfStats == pLfStats)
{
*pbCurrLfLevelChangedRV = TRUE;
}
}
Exit:
if( pSCache)
{
ScaReleaseCache( pSCache, FALSE);
}
else if( pBlk)
{
f_free( &pBlk);
}
return( rc);
}
/***************************************************************************
Desc: This routine allocates and initializes the LF table (array of
LF_HDR structures).
*****************************************************************************/
FSTATIC RCODE chkSetupLfTable(
DB_INFO * pDbInfo,
POOL * pPool)
{
FLMUINT uiCnt;
FLMUINT uiNumIndexes = 0;
FLMUINT uiIxStart;
FLMUINT uiNumDataCont = 0;
FLMUINT uiNumDictCont = 0;
FLMUINT uiIxOffset;
FLMUINT uiDataOffset;
FLMUINT uiDictOffset;
FDB * pDb = pDbInfo->pDb;
LFILE * pLFile;
LFILE * pTmpLFile;
LF_HDR * pLogicalFile;
LF_STATS * pLfStats;
RCODE rc = FERR_OK;
/*
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.
*/
pDbInfo->pProgress->uiNumFields =
pDbInfo->pProgress->uiNumIndexes =
pDbInfo->pProgress->uiNumContainers = 0;
pDbInfo->pProgress->uiNumLogicalFiles =
(FLMUINT)((pDb->pDict)
? (FLMUINT)pDb->pDict->uiLFileCnt
: (FLMUINT)0);
/* Determine the number of fields. */
if (pDb->pDict)
{
chkCountFields( pDb->pDict, &pDbInfo->pProgress->uiNumFields);
for (uiCnt = 0, pLFile = (LFILE *)pDb->pDict->pLFileTbl;
uiCnt < pDb->pDict->uiLFileCnt;
uiCnt++, pLFile++)
{
if (pLFile->uiLfType == LF_INDEX)
{
pDbInfo->pProgress->uiNumIndexes++;
uiNumIndexes++;
}
else
{
pDbInfo->pProgress->uiNumContainers++;
if( pLFile->uiLfNum == FLM_DICT_CONTAINER)
{
uiNumDictCont++;
}
else
{
uiNumDataCont++;
}
}
}
}
/* Allocate memory for each LFILE, then set up each LFILE. */
if (!pDbInfo->pProgress->uiNumLogicalFiles)
{
pDbInfo->pLogicalFiles = NULL;
pDbInfo->pProgress->pLfStats = NULL;
}
else
{
if ((pDbInfo->pLogicalFiles =
(LF_HDR *)GedPoolCalloc( pPool,
(FLMUINT)(sizeof( LF_HDR) *
pDbInfo->pProgress->uiNumLogicalFiles))) == NULL)
{
rc = RC_SET( FERR_MEM);
goto Exit;
}
if ((pDbInfo->pProgress->pLfStats =
(LF_STATS *)GedPoolCalloc( pPool,
(FLMUINT)(sizeof( LF_STATS) *
pDbInfo->pProgress->uiNumLogicalFiles))) == NULL)
{
rc = RC_SET( FERR_MEM);
goto Exit;
}
uiDictOffset = 0;
uiDataOffset = uiNumDictCont;
uiIxOffset = uiDataOffset + uiNumDataCont;
uiIxStart = uiIxOffset;
for (uiCnt = 0, pTmpLFile = (LFILE *)pDb->pDict->pLFileTbl;
uiCnt < pDbInfo->pProgress->uiNumLogicalFiles;
uiCnt++, pTmpLFile++)
{
if( pTmpLFile->uiLfType == LF_INDEX)
{
FLMUINT uiTmpIxOffset = uiIxOffset;
// Indexes need to be in order from lowest to highest
// because the result set is sorted that way.
while (uiTmpIxOffset > uiIxStart)
{
if (pDbInfo->pLogicalFiles [uiTmpIxOffset - 1].pLFile->uiLfNum <
pTmpLFile->uiLfNum)
{
break;
}
f_memcpy( &pDbInfo->pLogicalFiles [uiTmpIxOffset],
&pDbInfo->pLogicalFiles [uiTmpIxOffset - 1],
sizeof( LF_HDR));
f_memcpy( &pDbInfo->pProgress->pLfStats [uiTmpIxOffset],
&pDbInfo->pProgress->pLfStats [uiTmpIxOffset - 1],
sizeof( LF_STATS));
uiTmpIxOffset--;
}
pLogicalFile = &(pDbInfo->pLogicalFiles[ uiTmpIxOffset]);
pLfStats = &(pDbInfo->pProgress->pLfStats[ uiTmpIxOffset]);
uiIxOffset++;
}
else
{
if( pTmpLFile->uiLfNum == FLM_DICT_CONTAINER)
{
pLogicalFile = &(pDbInfo->pLogicalFiles[ uiDictOffset]);
pLfStats = &(pDbInfo->pProgress->pLfStats[ uiDictOffset]);
uiDictOffset++;
}
else
{
pLogicalFile = &(pDbInfo->pLogicalFiles[ uiDataOffset]);
pLfStats = &(pDbInfo->pProgress->pLfStats[ uiDataOffset]);
uiDataOffset++;
}
}
pLogicalFile->pLfStats = pLfStats;
/*
Copy the LFILE information - so we can return the
information even after the database has been closed.
*/
if ((pLogicalFile->pLFile = pLFile =
(LFILE *)GedPoolAlloc( pPool,
(FLMUINT)sizeof( LFILE))) == NULL)
{
rc = RC_SET( FERR_MEM);
goto Exit;
}
/*
Copy the LFILE structure so we can get enough information
to read them from disk, then read them from disk so we have
a read-consistent view of them.
*/
f_memcpy( pLFile, pTmpLFile, sizeof( LFILE));
if (RC_BAD( rc = flmLFileRead( pDb, pLFile)))
{
goto Exit;
}
pLfStats->uiLfType = pLFile->uiLfType;
if (pLFile->uiLfType == LF_INDEX)
{
pLfStats->uiIndexNum = pLFile->uiLfNum;
pLfStats->uiContainerNum = 0;
}
else
{
pLfStats->uiIndexNum = 0;
pLfStats->uiContainerNum = pLFile->uiLfNum;
}
/*
If the logical file is an index, get pointers to the index
definition and its field definitions.
*/
if (pLFile->uiLfType == LF_INDEX)
{
IXD * pTmpIxd;
IFD_p pTmpIfd;
if (RC_BAD( rc = fdictGetIndex(
pDb->pDict,
pDb->pFile->bInLimitedMode,
pLFile->uiLfNum,
NULL, &pTmpIxd, TRUE)))
{
if (rc == FERR_BAD_IX)
{
chkReportError( pDbInfo, FLM_BAD_PCODE_IXD_TBL,
LOCALE_IXD_TBL,
pLFile->uiLfNum, pLFile->uiLfType,
0xFF, 0,
0, 0, 0, 0xFFFF, 0,
NULL);
rc = RC_SET( FERR_PCODE_ERROR);
}
goto Exit;
}
pTmpIfd = pTmpIxd->pFirstIfd;
/*
Copy the IXD and IFD information - so we can return the
information even after the database has been closed.
*/
if ((pLogicalFile->pIxd = (IXD *)GedPoolAlloc( pPool,
(FLMUINT)(sizeof( IXD) +
sizeof( IFD) * pTmpIxd->uiNumFlds))) == NULL)
{
rc = RC_SET( FERR_MEM);
goto Exit;
}
pLogicalFile->pIfd = (IFD_p)(void *)(&pLogicalFile->pIxd [1]);
f_memcpy( pLogicalFile->pIxd, pTmpIxd, sizeof( IXD));
f_memcpy( pLogicalFile->pIfd, pTmpIfd,
sizeof( IFD) * pTmpIxd->uiNumFlds);
pLfStats->uiContainerNum = pLogicalFile->pIxd->uiContainerNum;
}
/*
Get the current number of levels in the logical file and
allocate an array of LEVEL_INFO structures for the levels.
*/
pLfStats->uiNumLevels = 0;
if (RC_BAD( rc = chkGetLfInfo( pDbInfo, pPool, pLfStats, pLFile,
NULL, NULL)))
{
goto Exit;
}
}
}
Exit:
return( rc);
}
/***************************************************************************
Desc: This routine checks all of the B-TREES in the database -- all
indexes and containers.
*****************************************************************************/
RCODE chkVerifyBTrees(
DB_INFO * pDbInfo,
POOL * pPool,
FLMBOOL * pbStartOverRV)
{
RCODE rc = FERR_OK;
FDB * pDb = pDbInfo->pDb;
FLMUINT uiCurrLf;
FLMUINT uiCurrLevel;
FLMBYTE * pKeyBuffer = NULL;
FLMUINT uiKeysAllocated = 0;
STATE_INFO State [BH_MAX_LEVELS];
FLMBOOL bStateInitialized [BH_MAX_LEVELS];
FLMBYTE ucResetKeyBuff [MAX_KEY_SIZ];
FLMBYTE * pucResetKey = NULL;
FLMUINT uiResetKeyLen = 0;
FLMUINT uiResetDrn = 0;
LF_HDR * pLogicalFile;
LF_STATS * pLfStats;
LFILE * pLFile;
FLMUINT uiSaveDictSeq;
FLMUINT uiTmpLf;
LF_STATS * pTmpLfStats;
POOL tmpPool;
FLMBOOL bRSFinalized = FALSE;
DB_CHECK_PROGRESS * pProgress = pDbInfo->pProgress;
IX_CHK_INFO IxChkInfo;
IX_CHK_INFO * pIxChkInfo = NULL;
void * pvPoolMark;
FILE_HDR * pFileHdr = &pDb->pFile->FileHdr;
for (uiCurrLevel = 0; uiCurrLevel < BH_MAX_LEVELS; uiCurrLevel++)
{
bStateInitialized [uiCurrLevel] = FALSE;
}
if (*pbStartOverRV)
{
goto Exit;
}
pvPoolMark = GedPoolMark( pPool);
uiSaveDictSeq = pDb->pDict->uiDictSeq;
if( RC_BAD( rc = chkSetupLfTable( pDbInfo, pPool)))
{
goto Exit;
}
if( pDbInfo->uiFlags & FLM_CHK_INDEX_REFERENCING)
{
if( RC_BAD( rc = chkSetupIxInfo( pDbInfo, &IxChkInfo)))
{
goto Exit;
}
pIxChkInfo = &IxChkInfo;
}
/*
Loop through all of the logical files in the database
and perform a structural and logical check.
*/
uiCurrLf = 0;
while (uiCurrLf < pDbInfo->pProgress->uiNumLogicalFiles)
{
pProgress->uiCurrLF = uiCurrLf + 1;
pLogicalFile = &pDbInfo->pLogicalFiles [uiCurrLf];
pLfStats = &pDbInfo->pProgress->pLfStats [uiCurrLf];
pLFile = pLogicalFile->pLFile;
if (pLFile->uiRootBlk == BT_END)
{
rc = FERR_OK;
uiCurrLf++;
uiResetKeyLen = 0;
pucResetKey = NULL;
uiResetDrn = 0;
continue;
}
/* Allocate space to hold the keys, if not already allocated. */
if (uiKeysAllocated < pLfStats->uiNumLevels)
{
/* If there is already a key allocated, deallocate it */
if (pKeyBuffer)
{
f_free( &pKeyBuffer);
uiKeysAllocated = 0;
}
if( RC_BAD( rc = f_alloc(
pLfStats->uiNumLevels * MAX_KEY_SIZ, &pKeyBuffer)))
{
goto Exit;
}
uiKeysAllocated = pLfStats->uiNumLevels;
}
/* Setup PROGRESS_CHECK_INFO structure */
pProgress->iCheckPhase = CHECK_B_TREE;
pProgress->bStartFlag = TRUE;
pProgress->uiLfNumber = pLFile->uiLfNum;
pProgress->uiLfType = pLFile->uiLfType;
if( pLFile->uiLfType == LF_INDEX)
{
pProgress->bUniqueIndex = (pLogicalFile->pIxd->uiFlags & IXD_UNIQUE) ? TRUE : FALSE;
}
if (RC_BAD( rc = chkCallProgFunc( pDbInfo)))
{
break;
}
pProgress->bStartFlag = FALSE;
f_yieldCPU();
/* Initialize the state information for each level of the B-TREE. */
for (uiCurrLevel = 0; uiCurrLevel < pLfStats->uiNumLevels; uiCurrLevel++)
{
/*
If we are resetting to a particular key, save the statistics
which were gathered so far.
*/
if (uiResetKeyLen)
{
/* Save the statistics which were gathered. */
pLfStats->pLevelInfo [uiCurrLevel].ui64KeyCount =
State [uiCurrLevel].ui64KeyCount;
f_memcpy( &pLfStats->pLevelInfo [uiCurrLevel].BlockInfo,
&State [uiCurrLevel].BlkInfo, sizeof( BLOCK_INFO));
}
flmInitReadState( &State [uiCurrLevel], &bStateInitialized [uiCurrLevel],
pFileHdr->uiVersionNum,
pDb, pLogicalFile, uiCurrLevel,
(FLMUINT)((!uiCurrLevel)
? (FLMUINT)BHT_LEAF
: (FLMUINT)BHT_NON_LEAF),
&pKeyBuffer [uiCurrLevel * MAX_KEY_SIZ]);
if (!uiResetKeyLen)
{
State [uiCurrLevel].uiLastChildAddr = BT_END;
State [uiCurrLevel].uiElmLastFlag = TRUE;
}
else
{
/* Restore the statistics which were gathered so far. */
State [uiCurrLevel].ui64KeyCount =
pLfStats->pLevelInfo [uiCurrLevel].ui64KeyCount;
f_memcpy( &State [uiCurrLevel].BlkInfo,
&pLfStats->pLevelInfo [uiCurrLevel].BlockInfo,
sizeof( BLOCK_INFO));
}
}
/*
Need to finalize the result set used by the logical
check. If the current logical file is an index and the
result set has not been finalized, call chkRSFinalize.
*/
if( !pDbInfo->pProgress->bPhysicalCorrupt &&
(pDbInfo->uiFlags & FLM_CHK_INDEX_REFERENCING) &&
bRSFinalized == FALSE && pLFile->uiLfType == LF_INDEX)
{
FLMUINT64 ui64NumRSKeys = 0;
/*
Finalize the result set.
*/
if( RC_BAD( rc = chkRSFinalize( pIxChkInfo, &ui64NumRSKeys)))
{
goto Exit;
}
/*
Reset uiNumKeys to reflect the number of keys
in the result set now that all duplicates have
been eliminated.
*/
if( pDbInfo->pProgress->ui64NumKeys > ui64NumRSKeys)
{
pDbInfo->pProgress->ui64NumDuplicateKeys =
pDbInfo->pProgress->ui64NumKeys - ui64NumRSKeys;
}
pDbInfo->pProgress->ui64NumKeys = ui64NumRSKeys;
/*
Set bRSFinalized to TRUE so that subsequent passes will not
attempt to finalize the result set again.
*/
bRSFinalized = TRUE;
}
/*
Call chkVerifySubTree to check the B-TREE starting at the
root block.
*/
GedPoolInit( &tmpPool, 512);
pDbInfo->bReposition = FALSE;
rc = chkVerifySubTree( pDbInfo, pIxChkInfo, NULL,
&State [pLfStats->uiNumLevels - 1],
pLFile->uiRootBlk, &tmpPool,
pucResetKey, uiResetKeyLen, uiResetDrn);
GedPoolFree( &tmpPool);
if (rc == FERR_OLD_VIEW)
{
// If it is a read transaction, reset.
if( flmGetDbTransType( pDb) == FLM_READ_TRANS)
{
// Free the KrefCntrl
KrefCntrlFree( pDb);
// Abort the read transaction
if( RC_BAD( rc = flmAbortDbTrans( pDb)))
{
goto Exit;
}
// Try to start a new read transaction
if( RC_BAD( rc = flmBeginDbTrans( pDb,
FLM_READ_TRANS, 0, FLM_DONT_POISON_CACHE)))
{
goto Exit;
}
}
rc = FERR_OK;
pDbInfo->bReposition = TRUE;
}
if (RC_BAD( rc))
{
goto Exit;
}
// We may get told to reposition if we had to repair
// an index or we got an old view error.
if (pDbInfo->bReposition)
{
/* If the dictionary has changed we must start all over. */
if (pDb->pDict->uiDictSeq != uiSaveDictSeq)
{
*pbStartOverRV = TRUE;
goto Exit;
}
/*
Save the current key at the bottom level of the B-Tree.
This is the point we want to try to reset to. Don't change
the reset key if the current key length is zero - this may
have occurred because of some error - we want to keep moving
forward in the file if at all possible.
*/
if (State [0].uiCurKeyLen)
{
uiResetKeyLen = State [0].uiCurKeyLen;
pucResetKey = &ucResetKeyBuff [0];
uiResetDrn = State [0].uiCurrIxRefDrn;
f_memcpy( pucResetKey, State [0].pCurKey, uiResetKeyLen);
}
// Re-read each logical file's LFH information.
pProgress->ui64DatabaseSize =
FSGetSizeInBytes( pDb->pFile->uiMaxFileSize,
pDb->LogHdr.uiLogicalEOF);
/*
Reread each of the LFH blocks and update the root block
address and other pertinent information for each logical
file.
*/
for (uiTmpLf = 0, pTmpLfStats = pDbInfo->pProgress->pLfStats;
uiTmpLf < pDbInfo->pProgress->uiNumLogicalFiles;
uiTmpLf++, pTmpLfStats++)
{
FLMBOOL bCurrLfLevelChanged = FALSE;
if (RC_BAD( rc = chkGetLfInfo( pDbInfo, pPool,
pTmpLfStats, pDbInfo->pLogicalFiles [uiTmpLf].pLFile,
pLfStats, &bCurrLfLevelChanged)))
{
goto Exit;
}
/*
If the number of levels for the current logical file
changed, reset things so we will recheck the entire logical
file.
*/
if (bCurrLfLevelChanged)
{
pucResetKey = NULL;
uiResetKeyLen = 0;
}
}
continue;
}
/*
Verify that all of the levels' next block address's
are BT_END.
*/
if (RC_OK( pDbInfo->LastStatusRc))
{
for (uiCurrLevel = 0; uiCurrLevel < pLfStats->uiNumLevels; uiCurrLevel++)
{
/* Save the statistics which were gathered. */
pLfStats->pLevelInfo [uiCurrLevel].ui64KeyCount =
State [uiCurrLevel].ui64KeyCount;
f_memcpy( &pLfStats->pLevelInfo [uiCurrLevel].BlockInfo,
&State [uiCurrLevel].BlkInfo, sizeof( BLOCK_INFO));
/*
Make sure the last block had a NEXT block address
of BT_END.
*/
if ((State [uiCurrLevel].uiNextBlkAddr) &&
(State [uiCurrLevel].uiNextBlkAddr != BT_END))
{
chkReportError( pDbInfo, FLM_BAD_LAST_BLK_NEXT,
LOCALE_B_TREE,
pDbInfo->pProgress->uiLfNumber,
pDbInfo->pProgress->uiLfType,
uiCurrLevel, 0, 0, 0, 0,
0xFFFF, 0, NULL);
}
}
}
if (RC_BAD( pDbInfo->LastStatusRc))
{
break;
}
uiCurrLf++;
pucResetKey = NULL;
uiResetKeyLen = 0;
uiResetDrn = 0;
}
/*
If index check was requested, no structural corruptions
were detected, and this is the last logical file, need to make
sure that the result set is empty.
*/
if( RC_OK( rc) && !pDbInfo->pProgress->bPhysicalCorrupt &&
(pDbInfo->uiFlags & FLM_CHK_INDEX_REFERENCING) &&
bRSFinalized == TRUE &&
uiCurrLf == pDbInfo->pProgress->uiNumLogicalFiles)
{
for( ;;)
{
if( RC_BAD( rc = chkGetNextRSKey( pIxChkInfo)))
{
if( rc == FERR_EOF_HIT || rc == FERR_NOT_FOUND)
{
rc = FERR_OK;
break;
}
goto Exit;
}
else
{
/* Updated statistics */
pIxChkInfo->pDbInfo->pProgress->ui64NumKeysExamined++;
if( RC_BAD( rc = chkResolveIXMissingKey(
&(State[ 0]), pIxChkInfo)))
{
goto Exit;
}
}
}
}
/* Clear the unique index flag */
pProgress->bUniqueIndex = FALSE;
Exit:
// Clear the pRecord for each level in the state array.
for (uiCurrLevel = 0; uiCurrLevel < BH_MAX_LEVELS; uiCurrLevel++)
{
if (bStateInitialized [uiCurrLevel] && State [uiCurrLevel].pRecord)
{
State [uiCurrLevel].pRecord->Release();
State [uiCurrLevel].pRecord = NULL;
}
}
/* Cleanup any temporary index check files */
if( pIxChkInfo != NULL)
{
chkFreeIxInfo( pIxChkInfo);
}
if (pKeyBuffer)
{
f_free( &pKeyBuffer);
}
if (RC_OK( rc) && RC_BAD( pDbInfo->LastStatusRc))
{
rc = pDbInfo->LastStatusRc;
}
return( rc);
}
/********************************************************************
Desc:
*********************************************************************/
RCODE chkReportError(
DB_INFO * pDbInfo,
eCorruptionType eCorruption,
eCorruptionLocale eErrLocale,
FLMUINT uiErrLfNumber,
FLMUINT uiErrLfType,
FLMUINT uiErrBTreeLevel,
FLMUINT uiErrBlkAddress,
FLMUINT uiErrParentBlkAddress,
FLMUINT uiErrElmOffset,
FLMUINT uiErrDrn,
FLMUINT uiErrElmRecOffset,
FLMUINT uiErrFieldNum,
FLMBYTE * pBlk)
{
CORRUPT_INFO CorruptInfo;
FLMBOOL bFixErr;
CorruptInfo.eCorruption = eCorruption;
CorruptInfo.eErrLocale = eErrLocale;
CorruptInfo.uiErrLfNumber = uiErrLfNumber;
CorruptInfo.uiErrLfType = uiErrLfType;
CorruptInfo.uiErrBTreeLevel = uiErrBTreeLevel;
CorruptInfo.uiErrBlkAddress = uiErrBlkAddress;
CorruptInfo.uiErrParentBlkAddress = uiErrParentBlkAddress;
CorruptInfo.uiErrElmOffset = uiErrElmOffset;
CorruptInfo.uiErrDrn = uiErrDrn;
CorruptInfo.uiErrElmRecOffset = uiErrElmRecOffset;
CorruptInfo.uiErrFieldNum = uiErrFieldNum;
CorruptInfo.pBlk = pBlk;
CorruptInfo.pErrIxKey = NULL;
CorruptInfo.pErrRecord = NULL;
CorruptInfo.pErrRecordKeyList = NULL;
if ((pDbInfo->fnStatusFunc) && (RC_OK( pDbInfo->LastStatusRc)))
{
bFixErr = FALSE;
pDbInfo->LastStatusRc = (*pDbInfo->fnStatusFunc)( FLM_PROBLEM_STATUS,
(void *)&CorruptInfo,
(void *)&bFixErr,
pDbInfo->pProgress->AppArg);
}
if (eCorruption != FLM_OLD_VIEW)
{
pDbInfo->pProgress->bPhysicalCorrupt = TRUE;
pDbInfo->uiFlags &= ~FLM_CHK_INDEX_REFERENCING;
}
return( pDbInfo->LastStatusRc);
}
/***************************************************************************
Desc: Initializes an IX_CHK_INFO structure
*****************************************************************************/
FSTATIC RCODE chkSetupIxInfo(
DB_INFO * pDbInfo,
IX_CHK_INFO * pIxInfoRV
)
{
RCODE rc = FERR_OK;
FLMUINT uiIxCount = 0;
FLMUINT uiIxNum = 0;
IXD * pIxd;
LFILE * pLFile;
char szTmpIoPath [F_PATH_MAX_SIZE];
char szBaseName [F_FILENAME_SIZE];
FDB * pDb = pDbInfo->pDb;
f_memset( pIxInfoRV, 0, sizeof( IX_CHK_INFO));
GedPoolInit( &(pIxInfoRV->pool), 512);
pIxInfoRV->pDbInfo = pDbInfo;
/* Set up the result set path */
if( RC_BAD( rc = flmGetTmpDir( szTmpIoPath)))
{
if( rc == FERR_IO_PATH_NOT_FOUND ||
rc == FERR_IO_INVALID_PATH)
{
if( RC_BAD( rc = f_pathReduce( pDb->pFile->pszDbPath,
szTmpIoPath, szBaseName)))
{
goto Exit;
}
}
else
{
goto Exit;
}
}
/*
Initialize the result set. The result set will be used
to build an ordered list of keys for comparision to
the database's indexes.
*/
if( RC_BAD( rc = chkRSInit( szTmpIoPath, &(pIxInfoRV->pRSet))))
{
goto Exit;
}
/* Build list of all indexes */
uiIxCount = 0;
if( pDb->pDict)
{
uiIxCount += pDb->pDict->uiIxdCnt;
}
/*
Allocate memory to save each index number and its associated
container number.
*/
if( RC_BAD( rc = f_alloc(
(FLMUINT)((sizeof( FLMUINT) * uiIxCount) + (sizeof( FLMBOOL) * uiIxCount)),
&(pIxInfoRV->puiIxArray))))
{
goto Exit;
}
/* Save the index numbers into the array. */
uiIxCount = 0;
if( pDb->pDict)
{
for( uiIxNum = 0, pIxd = (IXD *)pDb->pDict->pIxdTbl;
uiIxNum < pDb->pDict->uiIxdCnt;
uiIxNum++, pIxd++)
{
pIxInfoRV->puiIxArray[ uiIxCount] = pIxd->uiIndexNum;
uiIxCount++;
}
}
if( RC_OK( fdictGetIndex( pDb->pDict,
pDb->pFile->bInLimitedMode,
FLM_DICT_INDEX, &pLFile, NULL)))
{
pIxInfoRV->puiIxArray[ uiIxCount] = FLM_DICT_INDEX;
uiIxCount++;
}
pIxInfoRV->uiIxCount = uiIxCount;
pIxInfoRV->bGetNextRSKey = TRUE;
Exit:
/*
Clean up any memory on error exit.
*/
if( RC_BAD( rc))
{
GedPoolFree( &(pIxInfoRV->pool));
if( pIxInfoRV->puiIxArray)
{
f_free( &(pIxInfoRV->puiIxArray));
}
}
return( rc);
}
/********************************************************************
Desc: Outputs keys to the temporary result set
*********************************************************************/
FSTATIC RCODE chkOutputIndexKeys(
STATE_INFO * pStateInfo,
IX_CHK_INFO * pIxChkInfo,
IXD * pIxd,
REC_KEY * pKeyList
)
{
FLMUINT uiKeyLen;
REC_KEY * pKey;
FLMBYTE ucBuf[ MAX_KEY_SIZ + RS_KEY_OVERHEAD];
RCODE rc = FERR_OK;
pKey = pKeyList;
while( pKey)
{
/* Set the index and reference */
UW2FBA( (FLMUINT16)pIxd->uiIndexNum, &(ucBuf[ RS_IX_OFFSET]));
UD2FBA( (FLMUINT32)pStateInfo->uiElmDrn, &(ucBuf[ RS_REF_OFFSET]));
/* Convert the key tree to a collation key */
if( RC_BAD( rc = KYTreeToKey( pIxChkInfo->pDbInfo->pDb,
pIxd, pKey->pKey, pKey->pKey->getContainerID(),
&(ucBuf[ RS_KEY_OVERHEAD]), &uiKeyLen, 0)))
{
goto Exit;
}
/* Add the composite key (index, ref, key) to the result set */
if( RC_BAD( rc = chkRSAddEntry( pIxChkInfo->pRSet, ucBuf,
uiKeyLen + RS_KEY_OVERHEAD)))
{
goto Exit;
}
/*
Update statistics. Note that uiNumKeys will reflect the
total number of keys generated by records, including any
duplicate keys. This value is updated to reflect the
correct number of keys once the result set has been finalized.
*/
pIxChkInfo->pDbInfo->pProgress->ui64NumKeys++;
pKey = pKey->pNextKey;
}
Exit:
return( rc);
}