git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@568 0109f412-320b-0410-ab79-c3e0c5ffbbe6
4799 lines
111 KiB
C++
4799 lines
111 KiB
C++
//-------------------------------------------------------------------------
|
|
// Desc: Check database for corruptions.
|
|
// 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: flchkdb.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $
|
|
//-------------------------------------------------------------------------
|
|
|
|
#include "flaimsys.h"
|
|
|
|
FSTATIC RCODE chkGetDictInfo(
|
|
DB_INFO * pDbInfo);
|
|
|
|
FSTATIC RCODE chkVerifyBlkChain(
|
|
DB_INFO * pDbInfo,
|
|
BLOCK_INFO * pBlkInfo,
|
|
eCorruptionLocale eLocale,
|
|
FLMUINT uiFirstBlkAddr,
|
|
FLMUINT uiBlkType,
|
|
FLMUINT * puiBlkCount,
|
|
FLMBOOL * pbStartOverRV);
|
|
|
|
FSTATIC RCODE chkVerifyLFHBlocks(
|
|
DB_INFO * pDbInfo,
|
|
FLMBOOL * pbStartOverRV);
|
|
|
|
FSTATIC RCODE chkVerifyAvailList(
|
|
DB_INFO * pDbInfo,
|
|
FLMBOOL * pbStartOverRV);
|
|
|
|
FSTATIC RCODE chkVerifyTrackerCounts(
|
|
STATE_INFO * pStateInfo,
|
|
IX_CHK_INFO * pIxChkInfo,
|
|
FLMUINT uiIndexNum,
|
|
FLMUINT uiKeyCount,
|
|
FLMUINT uiRefCount);
|
|
|
|
FSTATIC RCODE chkIsCountIndex(
|
|
STATE_INFO * pStateInfo,
|
|
FLMUINT uiIndexNum,
|
|
FLMBOOL * pbIsCountIndex);
|
|
|
|
FSTATIC RCODE chkResolveRSetMissingKey(
|
|
STATE_INFO * pStateInfo,
|
|
IX_CHK_INFO * pIxChkInfo,
|
|
FLMUINT uiIxRefDrn);
|
|
|
|
FSTATIC RCODE chkVerifyDelNonUniqueRec(
|
|
STATE_INFO * pStateInfo,
|
|
IX_CHK_INFO * pIxChkInfo,
|
|
FLMUINT uiIndex,
|
|
FLMBYTE * pucKey,
|
|
FLMUINT uiKeyLen,
|
|
FLMUINT uiRecDrn,
|
|
FLMUINT * puiRecContainerRV,
|
|
FLMBOOL * pbDelRecRV);
|
|
|
|
FSTATIC RCODE chkVerifyKeyExists(
|
|
FDB * pDb,
|
|
LFILE * pLFile,
|
|
FLMBYTE * pucKey,
|
|
FLMUINT uiKeyLen,
|
|
FLMUINT uiRefDrn,
|
|
FLMBOOL * pbFoundRV);
|
|
|
|
FSTATIC RCODE chkAddDelKeyRef(
|
|
STATE_INFO * pStateInfo,
|
|
IX_CHK_INFO * pIxChkInfo,
|
|
FLMUINT uiIndexNum,
|
|
FLMBYTE * pucKey,
|
|
FLMUINT uiKeyLen,
|
|
FLMUINT uiDrn,
|
|
FLMUINT uiFlags);
|
|
|
|
FSTATIC RCODE chkGetKeySource(
|
|
STATE_INFO * pStateInfo,
|
|
IX_CHK_INFO * pIxChkInfo,
|
|
FLMUINT uiIndex,
|
|
FLMBYTE * pucKey,
|
|
FLMUINT uiKeyLen,
|
|
FLMUINT uiDrn,
|
|
FLMUINT * puiRecordContainerRV,
|
|
FLMBOOL * pbKeyInRecRV,
|
|
FLMBOOL * pbKeyInIndexRV);
|
|
|
|
FSTATIC RCODE chkReportIxError(
|
|
STATE_INFO * pStateInfo,
|
|
IX_CHK_INFO * pIxChkInfo,
|
|
eCorruptionType eCorruption,
|
|
FLMUINT uiErrIx,
|
|
FLMUINT uiErrDrn,
|
|
FLMBYTE * pucErrKey,
|
|
FLMUINT uiErrKeyLen,
|
|
FLMBOOL * pbFixErrRV);
|
|
|
|
FSTATIC RCODE chkGetNextRSKey(
|
|
IX_CHK_INFO * pIxChkInfo);
|
|
|
|
FSTATIC RCODE chkResolveIXMissingKey(
|
|
STATE_INFO * pStateInfo,
|
|
IX_CHK_INFO * pIxChkInfo);
|
|
|
|
FSTATIC RCODE chkRSInit(
|
|
const char * pszIoPath,
|
|
IF_ResultSet ** ppRSet);
|
|
|
|
FSTATIC RCODE chkRSFinalize(
|
|
IX_CHK_INFO * pIxChkInfo,
|
|
FLMUINT64 * pui64TotalEntries);
|
|
|
|
FSTATIC FLMINT chkCompareKeySet(
|
|
FLMUINT uiIxNum1,
|
|
FLMBYTE * pData1,
|
|
FLMUINT uiLength1,
|
|
FLMUINT uiDrn1,
|
|
FLMUINT uiIxNum2,
|
|
FLMBYTE * pData2,
|
|
FLMUINT uiLength2,
|
|
FLMUINT uiDrn2);
|
|
|
|
FSTATIC RCODE chkBlkRead(
|
|
DB_INFO * pDbInfo,
|
|
FLMUINT uiBlkAddress,
|
|
LFILE * pLFile,
|
|
FLMBYTE ** ppBlk,
|
|
SCACHE ** ppSCache,
|
|
eCorruptionType * peCorruption);
|
|
|
|
FSTATIC RCODE chkVerifyBTrees(
|
|
DB_INFO * pDbInfo,
|
|
F_Pool * pPool,
|
|
FLMBOOL * pbStartOverRV);
|
|
|
|
FSTATIC 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);
|
|
|
|
FSTATIC RCODE chkGetNextRSKey(
|
|
IX_CHK_INFO * pIxChkInfo);
|
|
|
|
FSTATIC RCODE chkVerifyKeyNotUnique(
|
|
STATE_INFO * pStateInfo,
|
|
FLMUINT uiIndex,
|
|
FLMBYTE * pucKey,
|
|
FLMUINT uiKeyLen,
|
|
FLMUINT * puiRefCountRV);
|
|
|
|
FSTATIC RCODE chkStartUpdate(
|
|
STATE_INFO * pStateInfo,
|
|
IX_CHK_INFO * pIxChkInfo);
|
|
|
|
FSTATIC RCODE chkEndUpdate(
|
|
STATE_INFO * pStateInfo,
|
|
IX_CHK_INFO * pIxChkInfo);
|
|
|
|
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,
|
|
F_Pool * pTmpPool,
|
|
FLMUINT * puiErrElmRecOffsetRV,
|
|
eCorruptionType * peElmErrCorruptCode);
|
|
|
|
FSTATIC RCODE chkVerifySubTree(
|
|
DB_INFO * pDbInfo,
|
|
IX_CHK_INFO * pIxChkInfo,
|
|
STATE_INFO * ParentState,
|
|
STATE_INFO * pStateInfo,
|
|
FLMUINT uiBlkAddress,
|
|
F_Pool * pTmpPool,
|
|
FLMBYTE * pucResetKey,
|
|
FLMUINT uiResetKeyLen,
|
|
FLMUINT uiResetDrn);
|
|
|
|
FSTATIC RCODE chkGetLfInfo(
|
|
DB_INFO * pDbInfo,
|
|
F_Pool * pPool,
|
|
LF_STATS * pLfStats,
|
|
LFILE * pLFile,
|
|
LF_STATS * pCurrLfStats,
|
|
FLMBOOL * pbCurrLfLevelChangedRV);
|
|
|
|
FSTATIC RCODE chkSetupLfTable(
|
|
DB_INFO * pDbInfo,
|
|
F_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:
|
|
****************************************************************************/
|
|
class F_ChkResultSetCompare : public IF_ResultSetCompare
|
|
{
|
|
public:
|
|
|
|
RCODE FLMAPI compare(
|
|
const void * pvData1,
|
|
FLMUINT uiLength1,
|
|
const void * pvData2,
|
|
FLMUINT uiLength2,
|
|
FLMINT * piCompare)
|
|
{
|
|
FLMBYTE * pucData1 = (FLMBYTE *) pvData1;
|
|
FLMBYTE * pucData2 = (FLMBYTE *) pvData2;
|
|
FLMUINT uiIxNum1;
|
|
FLMUINT uiIxNum2;
|
|
FLMUINT uiDrn1;
|
|
FLMUINT uiDrn2;
|
|
|
|
uiIxNum1 = (FLMUINT) FB2UW( &(pucData1[RS_IX_OFFSET]));
|
|
uiIxNum2 = (FLMUINT) FB2UW( &(pucData2[RS_IX_OFFSET]));
|
|
uiDrn1 = (FLMUINT) FB2UD( &(pucData1[RS_REF_OFFSET]));
|
|
uiDrn2 = (FLMUINT) FB2UD( &(pucData2[RS_REF_OFFSET]));
|
|
|
|
*piCompare = chkCompareKeySet(
|
|
uiIxNum1, &(pucData1[RS_KEY_OFFSET]),
|
|
uiLength1 - RS_KEY_OVERHEAD, uiDrn1, uiIxNum2,
|
|
&(pucData2[RS_KEY_OFFSET]),
|
|
uiLength2 - RS_KEY_OVERHEAD, uiDrn2);
|
|
|
|
return( FERR_OK);
|
|
}
|
|
};
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
FINLINE RCODE chkCallProgFunc(
|
|
DB_INFO * pDbInfo)
|
|
{
|
|
if( (pDbInfo->fnStatusFunc) && (RC_OK( pDbInfo->LastStatusRc)))
|
|
{
|
|
pDbInfo->LastStatusRc = (*pDbInfo->fnStatusFunc)( FLM_CHECK_STATUS,
|
|
(void *)pDbInfo->pProgress,
|
|
(void *)0,
|
|
pDbInfo->pProgress->AppArg);
|
|
}
|
|
return( pDbInfo->LastStatusRc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
class F_ChkSortStatus : public IF_ResultSetSortStatus
|
|
{
|
|
public:
|
|
|
|
F_ChkSortStatus( IX_CHK_INFO * pIxChkInfo)
|
|
{
|
|
m_pIxChkInfo = pIxChkInfo;
|
|
}
|
|
|
|
RCODE FLMAPI reportSortStatus(
|
|
FLMUINT64 ui64EstTotalUnits,
|
|
FLMUINT64 ui64UnitsDone)
|
|
{
|
|
FLMINT iRetVal = 0;
|
|
DB_CHECK_PROGRESS * pProgress = m_pIxChkInfo->pDbInfo->pProgress;
|
|
|
|
// Set the status values.
|
|
|
|
pProgress->ui64NumRSUnits = ui64EstTotalUnits;
|
|
pProgress->ui64NumRSUnitsDone = ui64UnitsDone;
|
|
|
|
// Call the progress callback.
|
|
|
|
if (RC_BAD( chkCallProgFunc( m_pIxChkInfo->pDbInfo)))
|
|
{
|
|
iRetVal = -1;
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
|
|
pProgress->bStartFlag = FALSE;
|
|
return( iRetVal);
|
|
}
|
|
|
|
private:
|
|
|
|
IX_CHK_INFO * m_pIxChkInfo;
|
|
};
|
|
|
|
/****************************************************************************
|
|
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)
|
|
{
|
|
pIxInfoRV->pool.poolFree();
|
|
pIxInfoRV->pRSet->Release();
|
|
pIxInfoRV->pRSet = NULL;
|
|
|
|
f_free( &(pIxInfoRV->puiIxArray));
|
|
f_memset( pIxInfoRV, 0, sizeof(IX_CHK_INFO));
|
|
|
|
return (FERR_OK);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Checks for physical corruption in a FLAIM database. Note: The
|
|
routine verifies the database by first reading through the database to
|
|
count certain block types which are in linked lists. It then verifies
|
|
the linked lists. It also verifies the B-TREEs in the database. The
|
|
reason for the first pass is so that when we verify the linked lists,
|
|
we can keep ourselves from getting into an infinite loop if there is
|
|
a loop in the lists.
|
|
****************************************************************************/
|
|
FLMEXP RCODE FLMAPI FlmDbCheck(
|
|
HFDB hDb,
|
|
const char * pDbFileName,
|
|
const char * pszDataDir,
|
|
const char * pRflDir,
|
|
FLMUINT uiCheckFlags,
|
|
F_Pool * pPool,
|
|
DB_CHECK_PROGRESS * pCheckProgress,
|
|
STATUS_HOOK fnStatusFunc,
|
|
void * AppArg)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
F_SuperFileHdl * pSFileHdl = NULL;
|
|
FLMBYTE * pBlk = NULL;
|
|
FLMUINT uiFileEnd;
|
|
FLMUINT uiBlockSize;
|
|
DB_CHECK_PROGRESS Progress;
|
|
FLMBOOL bOpenedDb = FALSE;
|
|
FDB * pDb = (FDB *) hDb;
|
|
FLMBOOL bIgnore;
|
|
FLMUINT uiLoop;
|
|
FLMUINT64 ui64TmpSize;
|
|
FLMBOOL bStartOver;
|
|
FLMBOOL bOkToCloseTrans = FALSE;
|
|
DB_INFO * pDbInfo;
|
|
F_Pool localPool;
|
|
void * pvDbInfoMark;
|
|
|
|
if (hDb != HFDB_NULL && IsInCSMode( hDb))
|
|
{
|
|
rc = RC_SET( FERR_NOT_IMPLEMENTED);
|
|
goto ExitCS;
|
|
}
|
|
|
|
localPool.poolInit( 512);
|
|
|
|
if (!pPool)
|
|
{
|
|
pPool = &localPool;
|
|
}
|
|
|
|
if( RC_BAD( rc = pPool->poolCalloc( sizeof( DB_INFO), (void **)&pDbInfo)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
pvDbInfoMark = pPool->poolMark();
|
|
|
|
if (hDb == HFDB_NULL)
|
|
{
|
|
if (RC_BAD( rc = FlmDbOpen( pDbFileName, pszDataDir, pRflDir, 0, NULL,
|
|
&hDb)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
bOpenedDb = TRUE;
|
|
pDb = (FDB *) hDb;
|
|
}
|
|
|
|
pDbInfo->fnStatusFunc = fnStatusFunc;
|
|
pDbInfo->LastStatusRc = FERR_OK;
|
|
pDbInfo->pDb = pDb;
|
|
|
|
if (pCheckProgress)
|
|
{
|
|
pDbInfo->pProgress = pCheckProgress;
|
|
}
|
|
else
|
|
{
|
|
pDbInfo->pProgress = &Progress;
|
|
}
|
|
|
|
f_memset( pDbInfo->pProgress, 0, sizeof(DB_CHECK_PROGRESS));
|
|
|
|
pDbInfo->bDbInitialized = TRUE;
|
|
if (RC_BAD( rc = fdbInit( pDb, FLM_NO_TRANS, 0, 0, &bIgnore)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Initialize the information block and Progress structure. Because
|
|
// FlmDbCheck will start and stop read transactions during its
|
|
// processing we can't allow any existing read transactions to exist.
|
|
// However, it is OK for an update transaction to be in progress. An
|
|
// update transaction will NOT be stopped and restarted. The only
|
|
// reason a read transaction may be stopped and restarted is if we get
|
|
// an old view error - something that cannot normally happen during an
|
|
// update transaction.
|
|
|
|
if (flmGetDbTransType( pDb) == FLM_READ_TRANS)
|
|
{
|
|
|
|
// If it is an invisible transaction, it may be aborted.
|
|
|
|
if (pDb->uiFlags & FDB_INVISIBLE_TRANS)
|
|
{
|
|
if (RC_BAD( rc = flmAbortDbTrans( pDb)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
rc = RC_SET( FERR_TRANS_ACTIVE);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Since we know that the check will start read transactions during
|
|
// its processing, set the flag to indicate that the KRef table should
|
|
// be cleaned up on exit if we are still in a read transaction.
|
|
|
|
bOkToCloseTrans = TRUE;
|
|
uiBlockSize = pDb->pFile->FileHdr.uiBlockSize;
|
|
|
|
// Check does its own reads using the handle to the file.
|
|
|
|
pSFileHdl = pDb->pSFileHdl;
|
|
pDbInfo->pSFileHdl = pSFileHdl;
|
|
|
|
// Allocate memory to use for reading through the data blocks.
|
|
|
|
if (RC_BAD( rc = f_alloc( uiBlockSize, &pBlk)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
Begin_Check:
|
|
|
|
// Initialize all statistics in the DB_INFO structure.
|
|
|
|
rc = FERR_OK;
|
|
bStartOver = FALSE;
|
|
|
|
pPool->poolReset( pvDbInfoMark);
|
|
pDbInfo->pProgress->bPhysicalCorrupt = FALSE;
|
|
pDbInfo->pProgress->bLogicalIndexCorrupt = FALSE;
|
|
pDbInfo->pProgress->ui64DatabaseSize = 0;
|
|
pDbInfo->pProgress->uiNumFields = 0;
|
|
pDbInfo->pProgress->uiNumIndexes = 0;
|
|
pDbInfo->pProgress->uiNumContainers = 0;
|
|
pDbInfo->pProgress->uiNumLogicalFiles = 0;
|
|
pDbInfo->pLogicalFiles = NULL;
|
|
pDbInfo->pProgress->pLfStats = NULL;
|
|
pDbInfo->uiFlags = uiCheckFlags;
|
|
pDbInfo->bStartedUpdateTrans = FALSE;
|
|
f_memset( &pDbInfo->pProgress->AvailBlocks, 0, sizeof(BLOCK_INFO));
|
|
f_memset( &pDbInfo->pProgress->LFHBlocks, 0, sizeof(BLOCK_INFO));
|
|
f_memset( &pDbInfo->FileHdr, 0, sizeof(FILE_HDR));
|
|
f_memset( &Progress, 0, sizeof(DB_CHECK_PROGRESS));
|
|
Progress.AppArg = AppArg;
|
|
|
|
// Get the dictionary information for the file
|
|
|
|
if (RC_BAD( rc = chkGetDictInfo( pDbInfo)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
Progress.ui64BytesExamined = 0;
|
|
|
|
for (uiLoop = 1;
|
|
uiLoop <= MAX_DATA_BLOCK_FILE_NUMBER( pDb->pFile->FileHdr.uiVersionNum);
|
|
uiLoop++)
|
|
{
|
|
if (RC_BAD( pSFileHdl->getFileSize( uiLoop, &ui64TmpSize)))
|
|
{
|
|
break;
|
|
}
|
|
|
|
Progress.ui64DatabaseSize += ui64TmpSize;
|
|
}
|
|
|
|
// See if we have a valid end of file
|
|
|
|
uiFileEnd = pDbInfo->pDb->LogHdr.uiLogicalEOF;
|
|
if (FSGetFileOffset( uiFileEnd) % uiBlockSize != 0)
|
|
{
|
|
if (RC_BAD( rc = chkReportError( pDbInfo, FLM_BAD_FILE_SIZE, LOCALE_NONE,
|
|
0, 0, 0xFF, uiFileEnd, 0, 0, 0, 0xFFFF, 0, NULL)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
else if (Progress.ui64DatabaseSize < FSGetSizeInBytes( pDbInfo->pDb->pFile->uiMaxFileSize,
|
|
uiFileEnd))
|
|
{
|
|
Progress.ui64DatabaseSize = FSGetSizeInBytes( pDbInfo->pDb->pFile->uiMaxFileSize,
|
|
uiFileEnd);
|
|
}
|
|
|
|
// Verify and count the LFH and PCODE blocks, B-Trees, and the AVAIL
|
|
// list.
|
|
|
|
if (RC_BAD( rc = chkVerifyLFHBlocks( pDbInfo, &bStartOver)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (bStartOver)
|
|
{
|
|
goto Begin_Check;
|
|
}
|
|
|
|
// Check the b-trees.
|
|
|
|
if (RC_BAD( rc = chkVerifyBTrees( pDbInfo, pPool, &bStartOver)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (bStartOver)
|
|
{
|
|
goto Begin_Check;
|
|
}
|
|
|
|
// Check the avail list.
|
|
|
|
if (RC_BAD( rc = chkVerifyAvailList( pDbInfo, &bStartOver)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (bStartOver)
|
|
{
|
|
goto Begin_Check;
|
|
}
|
|
|
|
// Signal that we are finished.
|
|
|
|
pDbInfo->pProgress->iCheckPhase = CHECK_FINISHED;
|
|
pDbInfo->pProgress->bStartFlag = TRUE;
|
|
if (RC_BAD( rc = chkCallProgFunc( pDbInfo)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
pDbInfo->pProgress->bStartFlag = FALSE;
|
|
|
|
Exit:
|
|
|
|
// Pass out any error code returned by the callback.
|
|
|
|
if ((RC_OK( rc)) && (RC_BAD( pDbInfo->LastStatusRc)))
|
|
{
|
|
rc = pDbInfo->LastStatusRc;
|
|
}
|
|
|
|
if (pDb && pDbInfo->bDbInitialized)
|
|
{
|
|
|
|
// Close down the transaction, if one is going
|
|
|
|
if (bOkToCloseTrans && flmGetDbTransType( pDb) == FLM_READ_TRANS)
|
|
{
|
|
KrefCntrlFree( pDb);
|
|
(void) flmAbortDbTrans( pDb);
|
|
}
|
|
|
|
fdbExit( pDb);
|
|
}
|
|
|
|
// Free memory, if allocated
|
|
|
|
if (pBlk)
|
|
{
|
|
f_free( &pBlk);
|
|
}
|
|
|
|
// Close the database we opened.
|
|
|
|
if (bOpenedDb)
|
|
{
|
|
(void) FlmDbClose( &hDb);
|
|
}
|
|
|
|
ExitCS:
|
|
|
|
return (rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This routine opens a file and reads its dictionary into memory.
|
|
****************************************************************************/
|
|
FSTATIC RCODE chkGetDictInfo(
|
|
DB_INFO * pDbInfo)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FDB * pDb = pDbInfo->pDb;
|
|
|
|
// Close down the transaction, if one is going.
|
|
|
|
if (flmGetDbTransType( pDb) != FLM_UPDATE_TRANS)
|
|
{
|
|
if (pDb->uiTransType == FLM_READ_TRANS)
|
|
{
|
|
(void) flmAbortDbTrans( pDb);
|
|
}
|
|
|
|
// Start a read transaction on the file to ensure we are connected
|
|
// to the file's dictionary structures.
|
|
|
|
if (RC_BAD( rc = flmBeginDbTrans( pDb, FLM_READ_TRANS, 0,
|
|
FLM_DONT_POISON_CACHE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
f_memcpy( &pDbInfo->FileHdr, &pDb->pFile->FileHdr, sizeof(FILE_HDR));
|
|
pDbInfo->pProgress->uiVersionNum = pDbInfo->FileHdr.uiVersionNum;
|
|
pDbInfo->pProgress->uiBlockSize = pDbInfo->FileHdr.uiBlockSize;
|
|
pDbInfo->pProgress->uiDefaultLanguage = pDbInfo->FileHdr.uiDefaultLanguage;
|
|
}
|
|
|
|
Exit:
|
|
|
|
return (rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This routine follows all of the blocks in a chain, verifying that
|
|
they are properly linked. It also verifies each block's header.
|
|
****************************************************************************/
|
|
FSTATIC RCODE chkVerifyBlkChain(
|
|
DB_INFO * pDbInfo,
|
|
BLOCK_INFO * pBlkInfo,
|
|
eCorruptionLocale eLocale,
|
|
FLMUINT uiFirstBlkAddr,
|
|
FLMUINT uiBlkType,
|
|
FLMUINT * puiBlkCount,
|
|
FLMBOOL * pbStartOverRV)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
eCorruptionType eCorruption = FLM_NO_CORRUPTION;
|
|
SCACHE * pSCache = NULL;
|
|
FLMBYTE * pBlk = NULL;
|
|
FLMUINT uiPrevBlkAddress;
|
|
FLMUINT uiBlkCount = 0;
|
|
STATE_INFO StateInfo;
|
|
FLMBOOL bStateInitialized = FALSE;
|
|
FLMUINT64 ui64SaveBytesExamined;
|
|
FDB * pDb = pDbInfo->pDb;
|
|
FILE_HDR * pFileHdr = &pDb->pFile->FileHdr;
|
|
FLMUINT uiVersionNum = pFileHdr->uiVersionNum;
|
|
FLMUINT uiBlockSize = pFileHdr->uiBlockSize;
|
|
FLMUINT uiMaxBlocks;
|
|
|
|
uiMaxBlocks = (FLMUINT)(FSGetSizeInBytes( pDb->pFile->uiMaxFileSize,
|
|
pDb->LogHdr.uiLogicalEOF) / (FLMUINT64) uiBlockSize);
|
|
|
|
uiPrevBlkAddress = BT_END;
|
|
|
|
// There must be at least ONE block if it is the LFH chain.
|
|
|
|
if ((uiBlkType == BHT_LFH_BLK) && (uiFirstBlkAddr == BT_END))
|
|
{
|
|
eCorruption = FLM_BAD_LFH_LIST_PTR;
|
|
(void) chkReportError( pDbInfo, eCorruption, eLocale, 0, 0, 0xFF, 0, 0, 0,
|
|
0, 0xFFFF, 0, NULL);
|
|
goto Exit;
|
|
}
|
|
|
|
// Read through all of the blocks, verifying them as we go.
|
|
|
|
Restart_Chain:
|
|
|
|
uiBlkCount = 0;
|
|
flmInitReadState( &StateInfo, &bStateInitialized, uiVersionNum, pDb, NULL,
|
|
(FLMUINT) ((uiBlkType == BHT_FREE)
|
|
? (FLMUINT) 0xFF
|
|
: (FLMUINT) 0), uiBlkType, NULL);
|
|
|
|
ui64SaveBytesExamined = pDbInfo->pProgress->ui64BytesExamined;
|
|
StateInfo.uiBlkAddress = uiFirstBlkAddr;
|
|
|
|
while ((StateInfo.uiBlkAddress != BT_END) && (uiBlkCount < uiMaxBlocks))
|
|
{
|
|
StateInfo.pBlk = NULL;
|
|
if (RC_BAD( rc = chkBlkRead( pDbInfo, StateInfo.uiBlkAddress,
|
|
StateInfo.pLogicalFile ? StateInfo.pLogicalFile->pLFile : NULL,
|
|
&pBlk, &pSCache, &eCorruption)))
|
|
{
|
|
if (rc == FERR_OLD_VIEW)
|
|
{
|
|
FLMUINT uiSaveDictSeq = pDb->pDict->uiDictSeq;
|
|
|
|
if (RC_BAD( rc = chkGetDictInfo( pDbInfo)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// If the dictionary ID changed, start over.
|
|
|
|
if (pDb->pDict->uiDictSeq != uiSaveDictSeq)
|
|
{
|
|
*pbStartOverRV = TRUE;
|
|
goto Exit;
|
|
}
|
|
|
|
pDbInfo->pProgress->ui64BytesExamined = ui64SaveBytesExamined;
|
|
goto Restart_Chain;
|
|
}
|
|
|
|
pBlkInfo->eCorruption = eCorruption;
|
|
pBlkInfo->uiNumErrors++;
|
|
rc = chkReportError( pDbInfo, eCorruption, eLocale, 0, 0, 0xFF,
|
|
StateInfo.uiBlkAddress, 0, 0, 0, 0xFFFF, 0, pBlk);
|
|
}
|
|
|
|
StateInfo.pBlk = pBlk;
|
|
uiBlkCount++;
|
|
pDbInfo->pProgress->ui64BytesExamined += (FLMUINT64) uiBlockSize;
|
|
if (RC_BAD( rc = chkCallProgFunc( pDbInfo)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
f_yieldCPU();
|
|
|
|
if (( eCorruption = flmVerifyBlockHeader( &StateInfo, pBlkInfo,
|
|
uiBlockSize, 0, (uiBlkType == BHT_FREE) ? 0L : uiPrevBlkAddress,
|
|
TRUE, TRUE)) != FLM_NO_CORRUPTION)
|
|
{
|
|
pBlkInfo->eCorruption = eCorruption;
|
|
pBlkInfo->uiNumErrors++;
|
|
chkReportError( pDbInfo, eCorruption, eLocale, 0, 0, 0xFF,
|
|
StateInfo.uiBlkAddress, 0, 0, 0, 0xFFFF, 0, pBlk);
|
|
goto Exit;
|
|
}
|
|
|
|
uiPrevBlkAddress = StateInfo.uiBlkAddress;
|
|
StateInfo.uiBlkAddress = (FLMUINT) FB2UD( &pBlk[BH_NEXT_BLK]);
|
|
}
|
|
|
|
if ((StateInfo.uiBlkAddress != BT_END) && (RC_OK( pDbInfo->LastStatusRc)))
|
|
{
|
|
switch (uiBlkType)
|
|
{
|
|
case BHT_LFH_BLK:
|
|
eCorruption = FLM_BAD_LFH_LIST_END;
|
|
break;
|
|
case BHT_PCODE_BLK:
|
|
eCorruption = FLM_BAD_PCODE_LIST_END;
|
|
break;
|
|
case BHT_FREE:
|
|
eCorruption = FLM_BAD_AVAIL_LIST_END;
|
|
break;
|
|
}
|
|
|
|
pBlkInfo->eCorruption = eCorruption;
|
|
pBlkInfo->uiNumErrors++;
|
|
chkReportError( pDbInfo, eCorruption, eLocale, 0, 0, 0xFF,
|
|
uiPrevBlkAddress, 0, 0, 0, 0xFFFF, 0, pBlk);
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
|
|
if (puiBlkCount)
|
|
{
|
|
*puiBlkCount = uiBlkCount;
|
|
}
|
|
|
|
if (bStateInitialized && StateInfo.pRecord)
|
|
{
|
|
StateInfo.pRecord->Release();
|
|
}
|
|
|
|
if (pSCache)
|
|
{
|
|
ScaReleaseCache( pSCache, FALSE);
|
|
}
|
|
else if (pBlk)
|
|
{
|
|
f_free( &pBlk);
|
|
}
|
|
|
|
if (RC_OK( rc) && (eCorruption != FLM_NO_CORRUPTION))
|
|
{
|
|
rc = (uiBlkType == BHT_FREE)
|
|
? RC_SET( FERR_DATA_ERROR)
|
|
: RC_SET( FERR_DD_ERROR);
|
|
}
|
|
|
|
return (rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This routine verifies the LFH blocks.
|
|
****************************************************************************/
|
|
FSTATIC RCODE chkVerifyLFHBlocks(
|
|
DB_INFO * pDbInfo,
|
|
FLMBOOL * pbStartOverRV)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
|
|
pDbInfo->pProgress->uiLfNumber = 0;
|
|
pDbInfo->pProgress->uiLfType = 0;
|
|
pDbInfo->pProgress->iCheckPhase = CHECK_LFH_BLOCKS;
|
|
pDbInfo->pProgress->bStartFlag = TRUE;
|
|
|
|
if (RC_BAD( rc = chkCallProgFunc( pDbInfo)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
pDbInfo->pProgress->bStartFlag = FALSE;
|
|
|
|
// Go through the LFH blocks.
|
|
|
|
if (RC_BAD( rc = chkVerifyBlkChain( pDbInfo, &pDbInfo->pProgress->LFHBlocks,
|
|
LOCALE_LFH_LIST, pDbInfo->pDb->pFile->FileHdr.uiFirstLFHBlkAddr,
|
|
BHT_LFH_BLK, NULL, pbStartOverRV)) ||
|
|
*pbStartOverRV)
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
|
|
return (rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This routine reads through the blocks in the AVAIL list and
|
|
verifies that we don't have a loop or some other corruption in the
|
|
list.
|
|
****************************************************************************/
|
|
FSTATIC RCODE chkVerifyAvailList(
|
|
DB_INFO * pDbInfo,
|
|
FLMBOOL * pbStartOverRV)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FLMUINT uiBlkCount;
|
|
|
|
pDbInfo->pProgress->uiLfNumber = 0;
|
|
pDbInfo->pProgress->uiLfType = 0;
|
|
pDbInfo->pProgress->iCheckPhase = CHECK_AVAIL_BLOCKS;
|
|
pDbInfo->pProgress->bStartFlag = TRUE;
|
|
|
|
if (RC_BAD( rc = chkCallProgFunc( pDbInfo)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
pDbInfo->pProgress->bStartFlag = FALSE;
|
|
|
|
f_yieldCPU();
|
|
|
|
if (RC_BAD( rc = chkVerifyBlkChain( pDbInfo,
|
|
&pDbInfo->pProgress->AvailBlocks, LOCALE_AVAIL_LIST,
|
|
pDbInfo->pDb->LogHdr.uiFirstAvailBlkAddr, BHT_FREE, &uiBlkCount,
|
|
pbStartOverRV)) ||
|
|
*pbStartOverRV)
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// See if the block count matches the block count stored in the log
|
|
// header.
|
|
|
|
if (uiBlkCount != pDbInfo->pDb->LogHdr.uiAvailBlkCount)
|
|
{
|
|
(void) chkReportError( pDbInfo, FLM_BAD_AVAIL_BLOCK_COUNT,
|
|
LOCALE_AVAIL_LIST, 0, 0, 0xFF, 0, 0, 0, 0, 0xFFFF,
|
|
0, NULL);
|
|
rc = RC_SET( FERR_DATA_ERROR);
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
|
|
return (rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Verifies the key and reference counts against the counts that are
|
|
stored in the tracker record.
|
|
****************************************************************************/
|
|
FSTATIC RCODE chkVerifyTrackerCounts(
|
|
STATE_INFO * pStateInfo,
|
|
IX_CHK_INFO * pIxChkInfo,
|
|
FLMUINT uiIndexNum,
|
|
FLMUINT uiKeyCount,
|
|
FLMUINT uiRefCount)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FDB * pDb = pStateInfo->pDb;
|
|
FlmRecord * pRecord = NULL;
|
|
eCorruptionType eCorruption;
|
|
FLMUINT uiTrackerKeyCount = 0;
|
|
FLMUINT uiTrackerRefCount = 0;
|
|
void * pvField;
|
|
|
|
// Retrieve the tracker record from record cache.
|
|
|
|
if (RC_BAD( rc = flmRcaRetrieveRec( pDb, NULL, FLM_TRACKER_CONTAINER,
|
|
uiIndexNum, TRUE, NULL, NULL, &pRecord)))
|
|
{
|
|
if (rc != FERR_NOT_FOUND)
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
rc = FERR_OK;
|
|
}
|
|
else
|
|
{
|
|
if ((pvField = pRecord->find( pRecord->root(), FLM_KEY_TAG)) != NULL)
|
|
{
|
|
if (RC_BAD( rc = pRecord->getUINT( pvField, &uiTrackerKeyCount)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
if ((pvField = pRecord->find( pRecord->root(), FLM_REFS_TAG)) != NULL)
|
|
{
|
|
if (RC_BAD( rc = pRecord->getUINT( pvField, &uiTrackerRefCount)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
// See if the counts match what we got from the tracker record.
|
|
|
|
if (uiKeyCount != uiTrackerKeyCount || uiRefCount != uiTrackerRefCount)
|
|
{
|
|
|
|
// Log an error.
|
|
|
|
eCorruption = (eCorruptionType) ((uiKeyCount != uiTrackerKeyCount)
|
|
? FLM_KEY_COUNT_MISMATCH
|
|
: FLM_REF_COUNT_MISMATCH);
|
|
|
|
if (RC_BAD( rc = chkReportError( pIxChkInfo->pDbInfo, eCorruption,
|
|
LOCALE_INDEX, uiIndexNum, LF_INDEX, 0xFF, 0, 0, 0, 0, 0xFFFF,
|
|
0, NULL)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
if (pRecord)
|
|
{
|
|
pRecord->Release();
|
|
}
|
|
|
|
return (rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Determine if an index is an index that keeps key and reference
|
|
counts.
|
|
****************************************************************************/
|
|
FSTATIC RCODE chkIsCountIndex(
|
|
STATE_INFO * pStateInfo,
|
|
FLMUINT uiIndexNum,
|
|
FLMBOOL * pbIsCountIndex)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FDB * pDb = pStateInfo->pDb;
|
|
IXD * pIxd;
|
|
|
|
if (RC_BAD( rc = fdictGetIndex( pDb->pDict, pDb->pFile->bInLimitedMode,
|
|
uiIndexNum, NULL, &pIxd, TRUE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
*pbIsCountIndex = (FLMBOOL) ((pIxd->uiFlags & IXD_COUNT)
|
|
? (FLMBOOL) TRUE
|
|
: (FLMBOOL) FALSE);
|
|
|
|
Exit:
|
|
|
|
return (rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Verifies the current index key against the result set.
|
|
****************************************************************************/
|
|
RCODE chkVerifyIXRSet(
|
|
STATE_INFO * pStateInfo,
|
|
IX_CHK_INFO * pIxChkInfo,
|
|
FLMUINT uiIxRefDrn)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FLMINT iCmpVal = 0;
|
|
FLMUINT uiIteration = 0;
|
|
FLMBOOL bRSetEmpty = FALSE;
|
|
RS_IX_KEY * pCurrRSKey;
|
|
RS_IX_KEY * pPrevRSKey;
|
|
|
|
if (!pIxChkInfo->pCurrRSKey)
|
|
{
|
|
pIxChkInfo->pCurrRSKey = &pIxChkInfo->IxKey1;
|
|
pIxChkInfo->pPrevRSKey = &pIxChkInfo->IxKey2;
|
|
}
|
|
|
|
// Compare index and result set keys
|
|
|
|
while (!bRSetEmpty)
|
|
{
|
|
if (pIxChkInfo->bGetNextRSKey)
|
|
{
|
|
|
|
// Get the next key from the result set. If the result set is
|
|
// empty, then pIxChkInfo->uiRSKeyLen will be set to zero,
|
|
// forcing the problem to be resolved below.
|
|
|
|
if (RC_BAD( rc = chkGetNextRSKey( pIxChkInfo)))
|
|
{
|
|
if (rc == FERR_EOF_HIT || rc == FERR_NOT_FOUND)
|
|
{
|
|
|
|
// Set bRSetEmpty to TRUE so that the loop will exit after
|
|
// the current key is resolved. Otherwise, conflict
|
|
// resolution on the current key will be repeated forever
|
|
// (infinite loop).
|
|
|
|
bRSetEmpty = TRUE;
|
|
rc = FERR_OK;
|
|
}
|
|
else
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
// Updated statistics
|
|
|
|
pIxChkInfo->pDbInfo->pProgress->ui64NumKeysExamined++;
|
|
}
|
|
}
|
|
|
|
pCurrRSKey = pIxChkInfo->pCurrRSKey;
|
|
pPrevRSKey = pIxChkInfo->pPrevRSKey;
|
|
|
|
if (pCurrRSKey->uiRSKeyLen == 0 || bRSetEmpty)
|
|
{
|
|
|
|
// We don't have a key because we got an EOF when reading the
|
|
// result set. Need to resolve the fact that the result set does
|
|
// not have a key that is found in the index. Set iCmpVal to 1 to
|
|
// force this resolution.
|
|
|
|
iCmpVal = 1;
|
|
}
|
|
else
|
|
{
|
|
|
|
// Compare the index key and result set key.
|
|
|
|
iCmpVal = chkCompareKeySet( pCurrRSKey->uiRSIxNum,
|
|
&(pCurrRSKey->pucRSKeyBuf[RS_KEY_OFFSET]),
|
|
pCurrRSKey->uiRSKeyLen - RS_KEY_OVERHEAD,
|
|
pCurrRSKey->uiRSRefDrn,
|
|
pStateInfo->pLogicalFile->pLFile->uiLfNum,
|
|
pStateInfo->pCurKey,
|
|
pStateInfo->uiCurKeyLen, uiIxRefDrn);
|
|
}
|
|
|
|
if (iCmpVal < 0)
|
|
{
|
|
|
|
// If a comparison is done where the keys from the result set
|
|
// don't match what we got from the index, we will forego
|
|
// verifying the tracker counts. Verifying of tracker counts can
|
|
// only occur if we have an otherwise clean check of the index
|
|
// keys.
|
|
|
|
pIxChkInfo->bCheckCounts = FALSE;
|
|
|
|
// The result set key is less than the index key. This could mean
|
|
// that the result set key needs to be added to the index.
|
|
|
|
if ((RC_BAD( rc = chkResolveIXMissingKey( pStateInfo, pIxChkInfo))) ||
|
|
(pIxChkInfo->pDbInfo->bReposition))
|
|
{
|
|
|
|
// If the key was added to the index (bReposition == TRUE) or
|
|
// we got some other error, we don't want to get the next
|
|
// result set key.
|
|
|
|
pIxChkInfo->bGetNextRSKey = FALSE;
|
|
goto Exit;
|
|
}
|
|
else
|
|
{
|
|
|
|
// False alarm. The index is missing the key because of a
|
|
// concurrent update. We want to get the next RS key.
|
|
|
|
pIxChkInfo->bGetNextRSKey = TRUE;
|
|
}
|
|
}
|
|
else if (iCmpVal > 0)
|
|
{
|
|
|
|
// If a comparison is done where the keys from the result set
|
|
// don't match what we got from the index, we will forego
|
|
// verifying the tracker counts. Verifying of tracker counts can
|
|
// only occur if we have an otherwise clean check of the index
|
|
// keys.
|
|
|
|
pIxChkInfo->bCheckCounts = FALSE;
|
|
|
|
// The result set key is greater than the index key. This could
|
|
// mean that the index key needs to be deleted from the index.
|
|
// Whether we delete the index key or not, we don't need to get
|
|
// the next result set key, but we do want to reposition and get
|
|
// the next index key.
|
|
|
|
pIxChkInfo->bGetNextRSKey = FALSE;
|
|
if ((RC_BAD( rc = chkResolveRSetMissingKey( pStateInfo,
|
|
pIxChkInfo, uiIxRefDrn))) || pIxChkInfo->pDbInfo->bReposition)
|
|
{
|
|
goto Exit;
|
|
}
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
|
|
// The index and result set keys are equal. We want to get the
|
|
// next result set and index keys.
|
|
|
|
pIxChkInfo->bGetNextRSKey = TRUE;
|
|
|
|
// Determine if we have switched indexes. If so, verify the key
|
|
// and reference counts against the counts in the tracker record.
|
|
|
|
if (pCurrRSKey->uiRSIxNum != pPrevRSKey->uiRSIxNum)
|
|
{
|
|
if (pIxChkInfo->bCheckCounts)
|
|
{
|
|
|
|
// Verify the key and reference counts against tracker
|
|
// record.
|
|
|
|
if (RC_BAD( rc = chkVerifyTrackerCounts( pStateInfo, pIxChkInfo,
|
|
pPrevRSKey->uiRSIxNum, pIxChkInfo->uiRSIxKeyCount,
|
|
pIxChkInfo->uiRSIxRefCount)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Determine if the new index is one that supports counts.
|
|
|
|
if (RC_BAD( rc = chkIsCountIndex( pStateInfo, pCurrRSKey->uiRSIxNum,
|
|
&pIxChkInfo->bCheckCounts)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (pIxChkInfo->bCheckCounts)
|
|
{
|
|
|
|
// Set the counts to one.
|
|
|
|
pIxChkInfo->uiRSIxKeyCount = 1;
|
|
pIxChkInfo->uiRSIxRefCount = 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pIxChkInfo->bCheckCounts)
|
|
{
|
|
|
|
// Always increment the reference count.
|
|
|
|
pIxChkInfo->uiRSIxRefCount++;
|
|
|
|
// See if the key changed.
|
|
|
|
if (pCurrRSKey->uiRSKeyLen != pPrevRSKey->uiRSKeyLen ||
|
|
(pCurrRSKey->uiRSKeyLen > RS_KEY_OFFSET &&
|
|
f_memcmp( &pCurrRSKey->pucRSKeyBuf[RS_KEY_OFFSET],
|
|
&pPrevRSKey->pucRSKeyBuf[RS_KEY_OFFSET],
|
|
pCurrRSKey->uiRSKeyLen - RS_KEY_OFFSET) != 0))
|
|
{
|
|
pIxChkInfo->uiRSIxKeyCount++;
|
|
}
|
|
else
|
|
{
|
|
|
|
// If the keys are the same, at least the DRNs better
|
|
// be different.
|
|
|
|
flmAssert( pCurrRSKey->uiRSRefDrn != pPrevRSKey->uiRSRefDrn);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Call the yield function periodically
|
|
|
|
uiIteration++;
|
|
if (!(uiIteration & 0x1F))
|
|
{
|
|
f_yieldCPU();
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
return (rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Retrieves the next key from the sorted result set
|
|
****************************************************************************/
|
|
RCODE chkGetNextRSKey(
|
|
IX_CHK_INFO * pIxChkInfo)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
RS_IX_KEY * pCurrRSKey;
|
|
|
|
// Swap current and last key pointers - this allows us to always keep
|
|
// the last key without having to memcpy the keys.
|
|
|
|
pCurrRSKey = pIxChkInfo->pCurrRSKey;
|
|
pIxChkInfo->pCurrRSKey = pIxChkInfo->pPrevRSKey;
|
|
pIxChkInfo->pPrevRSKey = pCurrRSKey;
|
|
pCurrRSKey = pIxChkInfo->pCurrRSKey;
|
|
|
|
// Get the next key
|
|
|
|
if (RC_BAD( rc = pIxChkInfo->pRSet->getNext( pCurrRSKey->pucRSKeyBuf,
|
|
MAX_KEY_SIZ + RS_KEY_OVERHEAD, &pCurrRSKey->uiRSKeyLen)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Verify that the key meets the minimum length requirements
|
|
|
|
flmAssert( pCurrRSKey->uiRSKeyLen >= RS_KEY_OVERHEAD);
|
|
|
|
// Extract the index number and reference DRN
|
|
|
|
pCurrRSKey->uiRSIxNum =
|
|
(FLMUINT) FB2UW( &(pCurrRSKey->pucRSKeyBuf[RS_IX_OFFSET]));
|
|
|
|
pCurrRSKey->uiRSRefDrn =
|
|
(FLMUINT) FB2UD( &(pCurrRSKey->pucRSKeyBuf[RS_REF_OFFSET]));
|
|
|
|
Exit:
|
|
|
|
return (rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Resolves the case of a key found in the result set but not in the
|
|
current index.
|
|
****************************************************************************/
|
|
RCODE chkResolveIXMissingKey(
|
|
STATE_INFO * pStateInfo,
|
|
IX_CHK_INFO * pIxChkInfo)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FLMBOOL bKeyInRec;
|
|
FLMBOOL bKeyInIndex;
|
|
FLMBOOL bFixCorruption = FALSE;
|
|
RS_IX_KEY * pCurrRSKey = pIxChkInfo->pCurrRSKey;
|
|
|
|
// Determine if the record generates the key and if the key is found in
|
|
// the index.
|
|
|
|
if (RC_BAD( rc = chkGetKeySource( pStateInfo, pIxChkInfo,
|
|
pCurrRSKey->uiRSIxNum, &(pCurrRSKey->pucRSKeyBuf[RS_KEY_OFFSET]),
|
|
(FLMUINT) (pCurrRSKey->uiRSKeyLen - RS_KEY_OVERHEAD),
|
|
pCurrRSKey->uiRSRefDrn, NULL, &bKeyInRec, &bKeyInIndex)))
|
|
{
|
|
if (rc == FERR_INDEX_OFFLINE)
|
|
{
|
|
rc = FERR_OK;
|
|
}
|
|
|
|
goto Exit;
|
|
}
|
|
|
|
// If the record does not generate the key or the key+ref is in the
|
|
// index, the index is not corrupt.
|
|
|
|
if (!bKeyInRec || bKeyInIndex)
|
|
{
|
|
pIxChkInfo->pDbInfo->pProgress->ui64NumConflicts++;
|
|
goto Exit;
|
|
}
|
|
|
|
// Otherwise, the index is corrupt; Update statistics
|
|
|
|
pIxChkInfo->pDbInfo->pProgress->ui64NumRecKeysNotFound++;
|
|
pIxChkInfo->pDbInfo->pProgress->uiLogicalIndexCorruptions++;
|
|
|
|
// Report the error
|
|
|
|
if (RC_BAD( rc = chkReportIxError( pStateInfo, pIxChkInfo,
|
|
FLM_DRN_NOT_IN_KEY_REFSET, pCurrRSKey->uiRSIxNum,
|
|
pCurrRSKey->uiRSRefDrn, &(pCurrRSKey->pucRSKeyBuf[RS_KEY_OFFSET]),
|
|
(FLMUINT) (pCurrRSKey->uiRSKeyLen - RS_KEY_OVERHEAD),
|
|
&bFixCorruption)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Exit if the application does not want to repair the corruption.
|
|
|
|
if (!bFixCorruption)
|
|
{
|
|
|
|
// Set the logical corruption flag
|
|
|
|
pIxChkInfo->pDbInfo->pProgress->bLogicalIndexCorrupt = TRUE;
|
|
goto Exit;
|
|
}
|
|
|
|
// Fix the corruption; Update statistics
|
|
|
|
pIxChkInfo->pDbInfo->pProgress->uiLogicalIndexRepairs++;
|
|
|
|
// Add the key
|
|
|
|
if (RC_OK( rc = chkAddDelKeyRef( pStateInfo, pIxChkInfo,
|
|
pCurrRSKey->uiRSIxNum, &(pCurrRSKey->pucRSKeyBuf[RS_KEY_OFFSET]),
|
|
(FLMUINT) (pCurrRSKey->uiRSKeyLen - RS_KEY_OVERHEAD),
|
|
pCurrRSKey->uiRSRefDrn, 0)))
|
|
{
|
|
pIxChkInfo->pDbInfo->bReposition = TRUE;
|
|
goto Exit;
|
|
}
|
|
else
|
|
{
|
|
if (rc == FERR_NOT_UNIQUE)
|
|
{
|
|
|
|
// A subsequent record probably also generates this key, but the
|
|
// index is a unique index so we were not allowed to add the
|
|
// missing key + ref to the index. This record should probably be
|
|
// deleted.
|
|
|
|
if (RC_OK( rc = chkResolveNonUniqueKey( pStateInfo, pIxChkInfo,
|
|
pCurrRSKey->uiRSIxNum,
|
|
&(pCurrRSKey->pucRSKeyBuf[RS_KEY_OFFSET]),
|
|
(FLMUINT) (pCurrRSKey->uiRSKeyLen - RS_KEY_OVERHEAD),
|
|
pCurrRSKey->uiRSRefDrn)))
|
|
{
|
|
pIxChkInfo->pDbInfo->bReposition = TRUE;
|
|
goto Exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
// Set the logical corruption flag
|
|
|
|
pIxChkInfo->pDbInfo->pProgress->bLogicalIndexCorrupt = TRUE;
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
return (rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Resolves the case of a key found in the current index but not in
|
|
the result set.
|
|
****************************************************************************/
|
|
FSTATIC RCODE chkResolveRSetMissingKey(
|
|
STATE_INFO * pStateInfo,
|
|
IX_CHK_INFO * pIxChkInfo,
|
|
FLMUINT uiIxRefDrn)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FLMBOOL bKeyInRec;
|
|
FLMBOOL bKeyInIndex;
|
|
FLMBOOL bFixCorruption = FALSE;
|
|
|
|
// See if the key is found in the index and/or generated by the record.
|
|
|
|
if (RC_BAD( rc = chkGetKeySource( pStateInfo, pIxChkInfo,
|
|
pStateInfo->pLogicalFile->pLFile->uiLfNum, pStateInfo->pCurKey,
|
|
pStateInfo->uiCurKeyLen, uiIxRefDrn, NULL, &bKeyInRec,
|
|
&bKeyInIndex)))
|
|
{
|
|
if (rc == FERR_INDEX_OFFLINE)
|
|
{
|
|
rc = FERR_OK;
|
|
}
|
|
|
|
goto Exit;
|
|
}
|
|
|
|
// If the key is generated by the record or the key is not found in the
|
|
// index, the index is not corrupt.
|
|
|
|
if (bKeyInRec || !bKeyInIndex)
|
|
{
|
|
pIxChkInfo->pDbInfo->pProgress->ui64NumConflicts++;
|
|
goto Exit;
|
|
}
|
|
|
|
// Otherwise, the index is corrupt; Update statistics
|
|
|
|
pIxChkInfo->pDbInfo->pProgress->ui64NumKeysNotFound++;
|
|
pIxChkInfo->pDbInfo->pProgress->uiLogicalIndexCorruptions++;
|
|
|
|
// Report the error
|
|
|
|
if (RC_BAD( rc = chkReportIxError( pStateInfo, pIxChkInfo,
|
|
FLM_IX_KEY_NOT_FOUND_IN_REC,
|
|
pStateInfo->pLogicalFile->pLFile->uiLfNum, uiIxRefDrn,
|
|
pStateInfo->pCurKey, pStateInfo->uiCurKeyLen, &bFixCorruption)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Exit if the application does not want to repair the corruption.
|
|
|
|
if (!bFixCorruption)
|
|
{
|
|
|
|
// Set the logical corruption flag
|
|
|
|
pIxChkInfo->pDbInfo->pProgress->bLogicalIndexCorrupt = TRUE;
|
|
goto Exit;
|
|
}
|
|
|
|
// Fix the corruption; Update statistics
|
|
|
|
pIxChkInfo->pDbInfo->pProgress->uiLogicalIndexRepairs++;
|
|
|
|
// Delete the reference from the index
|
|
|
|
if (RC_OK( rc = chkAddDelKeyRef( pStateInfo, pIxChkInfo,
|
|
pStateInfo->pLogicalFile->pLFile->uiLfNum, pStateInfo->pCurKey,
|
|
pStateInfo->uiCurKeyLen, uiIxRefDrn, KREF_DELETE_FLAG)))
|
|
{
|
|
pIxChkInfo->pDbInfo->bReposition = TRUE;
|
|
}
|
|
else
|
|
{
|
|
|
|
// Set the logical corruption flag
|
|
|
|
pIxChkInfo->pDbInfo->pProgress->bLogicalIndexCorrupt = TRUE;
|
|
}
|
|
|
|
Exit:
|
|
|
|
return (rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Resolves the case of multiple references associated with a key in a
|
|
unique index.
|
|
****************************************************************************/
|
|
RCODE chkResolveNonUniqueKey(
|
|
STATE_INFO * pStateInfo,
|
|
IX_CHK_INFO * pIxChkInfo,
|
|
FLMUINT uiIndex,
|
|
FLMBYTE * pucKey,
|
|
FLMUINT uiKeyLen,
|
|
FLMUINT uiDrn)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FDB * pDb = pStateInfo->pDb;
|
|
LFILE * pRecLFile = NULL;
|
|
FLMBOOL bDeleteRec = FALSE;
|
|
FLMUINT uiRecContainer;
|
|
RCODE rc2 = FERR_OK;
|
|
FLMBOOL bFixCorruption = FALSE;
|
|
FlmRecord * pOldRecord = NULL;
|
|
|
|
// Verify that the record violates the constraints of the unique index
|
|
// and should be deleted.
|
|
|
|
if (RC_BAD( rc = chkVerifyDelNonUniqueRec( pStateInfo, pIxChkInfo, uiIndex,
|
|
pucKey, uiKeyLen, uiDrn, &uiRecContainer, &bDeleteRec)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (bDeleteRec)
|
|
{
|
|
|
|
// Update statistics
|
|
|
|
pIxChkInfo->pDbInfo->pProgress->ui64NumNonUniqueKeys++;
|
|
pIxChkInfo->pDbInfo->pProgress->uiLogicalIndexCorruptions++;
|
|
|
|
// Report the error
|
|
|
|
if (RC_BAD( rc = chkReportIxError( pStateInfo, pIxChkInfo,
|
|
FLM_NON_UNIQUE_ELM_KEY_REF, uiIndex, uiDrn, pucKey, uiKeyLen,
|
|
&bFixCorruption)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (!bFixCorruption)
|
|
{
|
|
|
|
// Set the logical corruption flag
|
|
|
|
pIxChkInfo->pDbInfo->pProgress->bLogicalIndexCorrupt = TRUE;
|
|
goto Exit;
|
|
}
|
|
|
|
// Delete the record that generated the non-unique reference. ;
|
|
// Update statistics
|
|
|
|
pIxChkInfo->pDbInfo->pProgress->uiLogicalIndexRepairs++;
|
|
|
|
// Start an update transaction, if necessary.
|
|
|
|
if (RC_BAD( rc = chkStartUpdate( pStateInfo, pIxChkInfo)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Re-verify that the record should be deleted.
|
|
|
|
if (RC_BAD( rc = chkVerifyDelNonUniqueRec( pStateInfo, pIxChkInfo,
|
|
uiIndex, pucKey, uiKeyLen, uiDrn, &uiRecContainer,
|
|
&bDeleteRec)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (bDeleteRec == TRUE)
|
|
{
|
|
void * pvMark;
|
|
|
|
// Mark the temporary pool.
|
|
|
|
pvMark = pDb->TempPool.poolMark();
|
|
|
|
// Call the internal delete function, passing boolean flags
|
|
// indicating that missing keys should not prevent the record
|
|
// deletion and that the record validator callback should not be
|
|
// called.
|
|
|
|
if (RC_BAD( rc = fdictGetContainer( pDb->pDict, uiRecContainer,
|
|
&pRecLFile)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
rc = flmDeleteRecord( pDb, pRecLFile, uiDrn, &pOldRecord, TRUE);
|
|
|
|
if (gv_FlmSysData.UpdateEvents.pEventCBList)
|
|
{
|
|
flmUpdEventCallback( pDb, F_EVENT_DELETE_RECORD, (HFDB) pDb, rc,
|
|
uiDrn, uiRecContainer, NULL, pOldRecord);
|
|
}
|
|
|
|
// Reset the temporary pool. The flmDeleteRecord call
|
|
// allocates space for the record that is being deleted.
|
|
|
|
pDb->TempPool.poolReset( pvMark);
|
|
|
|
if (RC_BAD( rc))
|
|
{
|
|
|
|
// If the record had already been deleted, continue the check
|
|
// without reporting the error.
|
|
|
|
if (rc == FERR_NOT_FOUND)
|
|
{
|
|
rc = FERR_OK;
|
|
|
|
// Update statistics
|
|
|
|
pIxChkInfo->pDbInfo->pProgress->uiNumProblemsFixed++;
|
|
}
|
|
else
|
|
{
|
|
|
|
// Set the logical corruption flag
|
|
|
|
pIxChkInfo->pDbInfo->pProgress->bLogicalIndexCorrupt = TRUE;
|
|
}
|
|
|
|
goto Exit;
|
|
}
|
|
|
|
// Update statistics
|
|
|
|
pIxChkInfo->pDbInfo->pProgress->uiNumProblemsFixed++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
// Increment the conflict counter
|
|
|
|
pIxChkInfo->pDbInfo->pProgress->ui64NumConflicts++;
|
|
}
|
|
|
|
Exit:
|
|
|
|
// End the update. chkEndUpdate will be a no-op if an update
|
|
// transaction was not started.
|
|
|
|
rc2 = chkEndUpdate( pStateInfo, pIxChkInfo);
|
|
|
|
if (pOldRecord)
|
|
{
|
|
pOldRecord->Release();
|
|
}
|
|
|
|
return ((RCODE) ((rc != FERR_OK) ? (RCODE) rc : (RCODE) rc2));
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Verifies that the specified record should be deleted because it
|
|
generates key(s) which violate the constraints of a unique index.
|
|
****************************************************************************/
|
|
FSTATIC RCODE chkVerifyDelNonUniqueRec(
|
|
STATE_INFO * pStateInfo,
|
|
IX_CHK_INFO * pIxChkInfo,
|
|
FLMUINT uiIndex,
|
|
FLMBYTE * pucKey,
|
|
FLMUINT uiKeyLen,
|
|
FLMUINT uiRecDrn,
|
|
FLMUINT * puiRecContainerRV,
|
|
FLMBOOL * pbDelRecRV)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FLMBOOL bKeyInRec;
|
|
FLMBOOL bRecRefdByKey;
|
|
FLMUINT uiRefCount;
|
|
FLMUINT uiRecContainer;
|
|
|
|
*pbDelRecRV = FALSE;
|
|
*puiRecContainerRV = 0;
|
|
|
|
// See if the key is found in the index and/or generated by the record.
|
|
|
|
if (RC_BAD( rc = chkGetKeySource( pStateInfo, pIxChkInfo, uiIndex, pucKey,
|
|
uiKeyLen, uiRecDrn, &uiRecContainer, &bKeyInRec, &bRecRefdByKey)))
|
|
{
|
|
if (rc == FERR_INDEX_OFFLINE)
|
|
{
|
|
rc = FERR_OK;
|
|
}
|
|
|
|
goto Exit;
|
|
}
|
|
|
|
*puiRecContainerRV = uiRecContainer;
|
|
|
|
if (bKeyInRec == TRUE)
|
|
{
|
|
|
|
// Verify that the key is not unique
|
|
|
|
if (RC_BAD( rc = chkVerifyKeyNotUnique( pStateInfo, uiIndex, pucKey,
|
|
uiKeyLen, &uiRefCount)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (uiRefCount > 1)
|
|
{
|
|
|
|
// The unique index has multiple references for the specified
|
|
// key. Since the current record generates a non-unique key, it
|
|
// should be deleted even if it is not one of the records
|
|
// referenced by the key. Of course, if it is already referenced
|
|
// by the key, deleting the record will reduce the number of
|
|
// references associated with the key by one.
|
|
|
|
*pbDelRecRV = TRUE;
|
|
}
|
|
else if (uiRefCount == 1 && bRecRefdByKey == FALSE)
|
|
{
|
|
|
|
// The unique index already has a key corresponding to the key
|
|
// being generated by the current record. However, the record is
|
|
// not referenced from the unique index. The record should still
|
|
// be deleted since it generates a non-unique key.
|
|
|
|
*pbDelRecRV = TRUE;
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
return (rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Determines if a key is generated by the current record and/or if
|
|
the key is found in the current index
|
|
****************************************************************************/
|
|
FSTATIC RCODE chkGetKeySource(
|
|
STATE_INFO * pStateInfo,
|
|
IX_CHK_INFO * pIxChkInfo,
|
|
FLMUINT uiIndex,
|
|
FLMBYTE * pucKey,
|
|
FLMUINT uiKeyLen,
|
|
FLMUINT uiDrn,
|
|
FLMUINT * puiRecordContainerRV,
|
|
FLMBOOL * pbKeyInRecRV,
|
|
FLMBOOL * pbKeyInIndexRV)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FlmRecord * pRecord = NULL;
|
|
FDB * pDb = pStateInfo->pDb;
|
|
LFILE * pLFile;
|
|
LFILE * pIxLFile;
|
|
REC_KEY * pKeys = NULL;
|
|
REC_KEY * pTempKey = NULL;
|
|
IXD * pIxd;
|
|
FLMBYTE ucRecKeyBuf[MAX_KEY_SIZ];
|
|
FLMUINT uiRecKeyLen;
|
|
FLMUINT uiKeyCount;
|
|
FLMBOOL bResetKRef = FALSE;
|
|
void * pIxPoolMark;
|
|
void * pDbPoolMark;
|
|
FLMUINT uiContainerNum;
|
|
|
|
// Initialize return values.
|
|
|
|
*pbKeyInRecRV = FALSE;
|
|
*pbKeyInIndexRV = FALSE;
|
|
|
|
if (puiRecordContainerRV)
|
|
{
|
|
*puiRecordContainerRV = 0;
|
|
}
|
|
|
|
// Initialize variables
|
|
|
|
pIxPoolMark = pIxChkInfo->pool.poolMark();
|
|
|
|
// 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 = pDb->TempPool.poolMark();
|
|
|
|
// Set up the KRef so that flmGetRecKeys will work
|
|
|
|
if (RC_BAD( rc = KrefCntrlCheck( pDb)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
bResetKRef = TRUE;
|
|
|
|
// Get the LFile and IXD of the index
|
|
|
|
if (RC_BAD( rc = fdictGetIndex( pDb->pDict, pDb->pFile->bInLimitedMode,
|
|
uiIndex, &pIxLFile, &pIxd)))
|
|
{
|
|
|
|
// Return FERR_INDEX_OFFLINE error.
|
|
|
|
goto Exit;
|
|
}
|
|
|
|
if ((uiContainerNum = pIxd->uiContainerNum) == 0)
|
|
{
|
|
|
|
// Container number is always the last two bytes of the key.
|
|
|
|
flmAssert( uiKeyLen > getIxContainerPartLen( pIxd));
|
|
uiContainerNum = getContainerFromKey( pucKey, uiKeyLen);
|
|
}
|
|
|
|
// Get the LFile of the record that caused the error
|
|
|
|
if (RC_BAD( rc = fdictGetContainer( pDb->pDict, uiContainerNum, &pLFile)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Set the record container return value
|
|
|
|
if (puiRecordContainerRV)
|
|
{
|
|
*puiRecordContainerRV = uiContainerNum;
|
|
}
|
|
|
|
// See if the key is in the index.
|
|
|
|
if (RC_BAD( rc = chkVerifyKeyExists( pDb, pIxLFile, pucKey, uiKeyLen, uiDrn,
|
|
pbKeyInIndexRV)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Read the record
|
|
|
|
if (RC_BAD( rc = flmRcaRetrieveRec( pDb, NULL, pLFile->uiLfNum, uiDrn, FALSE,
|
|
NULL, NULL, &pRecord)))
|
|
{
|
|
if (rc != FERR_NOT_FOUND)
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// NOTE: Deliberately not bringing in to cache if not found there.
|
|
|
|
if (RC_BAD( rc = FSReadRecord( pDb, pLFile, uiDrn, &pRecord, NULL, NULL)))
|
|
{
|
|
if (rc == FERR_NOT_FOUND)
|
|
{
|
|
*pbKeyInRecRV = FALSE;
|
|
rc = FERR_OK;
|
|
}
|
|
else
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pRecord)
|
|
{
|
|
|
|
// Generate record keys
|
|
|
|
if (RC_BAD( rc = flmGetRecKeys( pDb, pIxd, pRecord,
|
|
pRecord->getContainerID(), TRUE, &(pIxChkInfo->pool), &pKeys)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
uiKeyCount = 0;
|
|
pTempKey = pKeys;
|
|
while (pTempKey != NULL)
|
|
{
|
|
|
|
// Build the collated keys for each key tree.
|
|
|
|
if (RC_BAD( rc = KYTreeToKey( pDb, pIxd, pTempKey->pKey,
|
|
pTempKey->pKey->getContainerID(), ucRecKeyBuf, &uiRecKeyLen,
|
|
0)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (KYKeyCompare( pucKey, uiKeyLen, ucRecKeyBuf,
|
|
uiRecKeyLen) == BT_EQ_KEY)
|
|
{
|
|
*pbKeyInRecRV = TRUE;
|
|
break;
|
|
}
|
|
|
|
pTempKey = pTempKey->pNextKey;
|
|
uiKeyCount++;
|
|
|
|
// Release the CPU periodically to prevent CPU hog problems.
|
|
|
|
f_yieldCPU();
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
if (pKeys)
|
|
{
|
|
pTempKey = pKeys;
|
|
while (pTempKey)
|
|
{
|
|
pTempKey->pKey->Release();
|
|
pTempKey = pTempKey->pNextKey;
|
|
}
|
|
}
|
|
|
|
if (pRecord)
|
|
{
|
|
pRecord->Release();
|
|
}
|
|
|
|
// Remove any keys added to the KRef
|
|
|
|
if (bResetKRef)
|
|
{
|
|
KYAbortCurrentRecord( pDb);
|
|
}
|
|
|
|
// Reset the DB's temporary pool
|
|
|
|
pDb->TempPool.poolReset( pDbPoolMark);
|
|
|
|
// Reset the index check pool
|
|
|
|
pIxChkInfo->pool.poolReset( pIxPoolMark);
|
|
return (rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Verify that a key is (or is not) found in an index.
|
|
****************************************************************************/
|
|
FSTATIC RCODE chkVerifyKeyExists(
|
|
FDB * pDb,
|
|
LFILE * pLFile,
|
|
FLMBYTE * pucKey,
|
|
FLMUINT uiKeyLen,
|
|
FLMUINT uiRefDrn,
|
|
FLMBOOL * pbFoundRV)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
BTSK stackBuf[BH_MAX_LEVELS];
|
|
BTSK * stack = stackBuf;
|
|
FLMUINT uiDinDomain = DIN_DOMAIN( uiRefDrn) + 1;
|
|
FLMBYTE ucBtKeyBuf[MAX_KEY_SIZ];
|
|
DIN_STATE dinState;
|
|
FLMUINT uiTmpDrn;
|
|
|
|
*pbFoundRV = FALSE;
|
|
f_memset( &dinState, 0, sizeof(DIN_STATE));
|
|
|
|
// Initialize stack cache.
|
|
|
|
FSInitStackCache( &(stackBuf[0]), BH_MAX_LEVELS);
|
|
stack = stackBuf;
|
|
stack->pKeyBuf = ucBtKeyBuf;
|
|
|
|
// Search for the key.
|
|
|
|
if (RC_BAD( rc = FSBtSearch( pDb, pLFile, &stack, pucKey, uiKeyLen,
|
|
uiDinDomain)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (stack->uiCmpStatus == BT_EQ_KEY)
|
|
{
|
|
uiTmpDrn = uiRefDrn;
|
|
|
|
// Reading the current element, position to or after uiTmpDrn
|
|
|
|
rc = FSRefSearch( stack, &dinState, &uiTmpDrn);
|
|
|
|
// If the entry was not found, returns FERR_FAILURE
|
|
|
|
if (rc == FERR_OK)
|
|
{
|
|
*pbFoundRV = TRUE;
|
|
}
|
|
else if (rc != FERR_FAILURE)
|
|
{
|
|
goto Exit;
|
|
}
|
|
else
|
|
{
|
|
rc = FERR_OK;
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
// Free the stack cache
|
|
|
|
FSReleaseStackCache( stackBuf, BH_MAX_LEVELS, FALSE);
|
|
return (rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Compares a composite key (index, ref, key) for equality.
|
|
Note: Since index references are sorted in decending order, a composite key
|
|
with a lower ref DRN will sort after a key with a higher ref DRN.
|
|
****************************************************************************/
|
|
FLMINT chkCompareKeySet(
|
|
FLMUINT uiIxNum1,
|
|
FLMBYTE * pData1,
|
|
FLMUINT uiLength1,
|
|
FLMUINT uiDrn1,
|
|
FLMUINT uiIxNum2,
|
|
FLMBYTE * pData2,
|
|
FLMUINT uiLength2,
|
|
FLMUINT uiDrn2)
|
|
{
|
|
FLMINT iCmpVal = 0;
|
|
FLMUINT uiMinLen;
|
|
|
|
// Compare index numbers
|
|
|
|
if (uiIxNum1 > uiIxNum2)
|
|
{
|
|
iCmpVal = 1;
|
|
goto Exit;
|
|
}
|
|
else if (uiIxNum1 < uiIxNum2)
|
|
{
|
|
iCmpVal = -1;
|
|
goto Exit;
|
|
}
|
|
|
|
// Compare keys
|
|
|
|
uiMinLen = (FLMUINT) (uiLength1 < uiLength2) ? uiLength1 : uiLength2;
|
|
iCmpVal = f_memcmp( pData1, pData2, uiMinLen);
|
|
if (iCmpVal == 0)
|
|
{
|
|
|
|
// Compare references
|
|
|
|
if (uiLength1 == uiLength2)
|
|
{
|
|
|
|
// A key with a lower ref DRN will sort after a key with a higher
|
|
// ref DRN.
|
|
|
|
if (uiDrn1 > uiDrn2)
|
|
{
|
|
iCmpVal = -1;
|
|
}
|
|
else if (uiDrn1 < uiDrn2)
|
|
{
|
|
iCmpVal = 1;
|
|
}
|
|
else
|
|
{
|
|
iCmpVal = 0;
|
|
goto Exit;
|
|
}
|
|
}
|
|
else if (uiLength1 > uiLength2)
|
|
{
|
|
iCmpVal = 1;
|
|
}
|
|
else
|
|
{
|
|
iCmpVal = -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
iCmpVal = (FLMINT) ((iCmpVal > 0) ? (FLMINT) 1 : (FLMINT) -1);
|
|
}
|
|
|
|
Exit:
|
|
|
|
return (iCmpVal);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This routine adds or deletes an index key and/or reference.
|
|
****************************************************************************/
|
|
FSTATIC RCODE chkAddDelKeyRef(
|
|
STATE_INFO * pStateInfo,
|
|
IX_CHK_INFO * pIxChkInfo,
|
|
FLMUINT uiIndexNum,
|
|
FLMBYTE * pucKey,
|
|
FLMUINT uiKeyLen,
|
|
FLMUINT uiDrn,
|
|
FLMUINT uiFlags)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
RCODE rc2 = FERR_OK;
|
|
FLMBYTE ucKeyBuf[sizeof(KREF_ENTRY) + MAX_KEY_SIZ];
|
|
KREF_ENTRY * pKrefEntry = (KREF_ENTRY *) (&ucKeyBuf[0]);
|
|
IXD * pIxd;
|
|
LFILE * pLFile;
|
|
FLMBOOL bStartedUpdate = FALSE;
|
|
FLMBOOL bKeyInRec;
|
|
FLMBOOL bKeyInIndex;
|
|
|
|
// Start an update transaction, if necessary
|
|
|
|
if (RC_BAD( rc = chkStartUpdate( pStateInfo, pIxChkInfo)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
bStartedUpdate = TRUE;
|
|
|
|
// Look up the LFILE and IXD for the index.
|
|
|
|
if (RC_BAD( rc = fdictGetIndex( pStateInfo->pDb->pDict,
|
|
pStateInfo->pDb->pFile->bInLimitedMode, uiIndexNum, &pLFile, &pIxd)))
|
|
{
|
|
|
|
// Shouldn't get FERR_INDEX_OFFLINE in here.
|
|
|
|
goto Exit;
|
|
}
|
|
|
|
// Verify that the state has not changed
|
|
|
|
if (RC_BAD( rc = chkGetKeySource( pStateInfo, pIxChkInfo, uiIndexNum, pucKey,
|
|
uiKeyLen, uiDrn, NULL, &bKeyInRec, &bKeyInIndex)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if ((bKeyInIndex == TRUE && ((uiFlags & KREF_DELETE_FLAG) != 0)) ||
|
|
(bKeyInIndex == FALSE && uiFlags == 0))
|
|
{
|
|
|
|
// Setup the KrefEntry structure
|
|
|
|
flmAssert( uiIndexNum > 0 && uiIndexNum < FLM_UNREGISTERED_TAGS);
|
|
f_memcpy( &(ucKeyBuf[sizeof(KREF_ENTRY)]), pucKey, uiKeyLen);
|
|
pKrefEntry->ui16KeyLen = (FLMUINT16) uiKeyLen;
|
|
pKrefEntry->ui16IxNum = (FLMUINT16) uiIndexNum;
|
|
pKrefEntry->uiDrn = uiDrn;
|
|
pKrefEntry->uiTrnsSeq = 1;
|
|
pKrefEntry->uiFlags = uiFlags;
|
|
|
|
if ((pIxd->uiFlags & IXD_UNIQUE) != 0)
|
|
{
|
|
|
|
// Do not allow duplicate keys to be added to a unique index.
|
|
|
|
pKrefEntry->uiFlags |= KREF_UNIQUE_KEY;
|
|
}
|
|
|
|
// Add or delete the key/reference.
|
|
|
|
if (RC_BAD( rc = FSRefUpdate( pStateInfo->pDb, pLFile, pKrefEntry)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Update statistics
|
|
|
|
pIxChkInfo->pDbInfo->pProgress->uiNumProblemsFixed++;
|
|
}
|
|
|
|
Exit:
|
|
|
|
// End the update.
|
|
|
|
if (bStartedUpdate == TRUE)
|
|
{
|
|
if (RC_BAD( rc2 = chkEndUpdate( pStateInfo, pIxChkInfo)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
rc = (RCODE) ((rc != FERR_OK) ? (RCODE) rc : (RCODE) rc2);
|
|
|
|
return (rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Populates the CORRUPT_INFO structure and calls the user's callback
|
|
routine.
|
|
****************************************************************************/
|
|
FSTATIC RCODE chkReportIxError(
|
|
STATE_INFO * pStateInfo,
|
|
IX_CHK_INFO * pIxChkInfo,
|
|
eCorruptionType eCorruption,
|
|
FLMUINT uiErrIx,
|
|
FLMUINT uiErrDrn,
|
|
FLMBYTE * pucErrKey,
|
|
FLMUINT uiErrKeyLen,
|
|
FLMBOOL * pbFixErrRV)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FDB * pDb = pStateInfo->pDb;
|
|
F_Pool * pTmpPool;
|
|
IXD * pIxd;
|
|
LFILE * pLFile;
|
|
void * pIxPoolMark;
|
|
void * pDbPoolMark = NULL;
|
|
FLMBOOL bResetKRef = FALSE;
|
|
CORRUPT_INFO CorruptInfo;
|
|
FLMUINT uiContainerNum;
|
|
|
|
f_memset( &CorruptInfo, 0, sizeof(CORRUPT_INFO));
|
|
|
|
// Mark the index check pool
|
|
|
|
pIxPoolMark = pIxChkInfo->pool.poolMark();
|
|
pTmpPool = &(pIxChkInfo->pool);
|
|
|
|
// 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 = pDb->TempPool.poolMark();
|
|
|
|
// Set up the KRef so that flmGetRecKeys will work
|
|
|
|
if (RC_BAD( rc = KrefCntrlCheck( pDb)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
bResetKRef = TRUE;
|
|
|
|
// Report the error
|
|
|
|
CorruptInfo.eErrLocale = LOCALE_INDEX;
|
|
CorruptInfo.eCorruption = eCorruption;
|
|
CorruptInfo.uiErrLfNumber = uiErrIx;
|
|
CorruptInfo.uiErrDrn = uiErrDrn;
|
|
CorruptInfo.uiErrElmOffset = pStateInfo->uiElmOffset;
|
|
|
|
if (RC_BAD( rc = fdictGetIndex( pDb->pDict, pDb->pFile->bInLimitedMode,
|
|
uiErrIx, NULL, &pIxd, TRUE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Generate the key tree using the key that caused the error
|
|
|
|
if (RC_BAD( rc = flmIxKeyOutput( pIxd, pucErrKey, uiErrKeyLen,
|
|
&(CorruptInfo.pErrIxKey), FALSE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Get the LFile
|
|
|
|
if ((uiContainerNum = pIxd->uiContainerNum) == 0)
|
|
{
|
|
|
|
// Container number is always the last two bytes of the key.
|
|
|
|
flmAssert( uiErrKeyLen > getIxContainerPartLen( pIxd));
|
|
uiContainerNum = getContainerFromKey( pucErrKey, uiErrKeyLen);
|
|
}
|
|
|
|
// Get the LFile
|
|
|
|
if (RC_BAD( rc = fdictGetContainer( pDb->pDict, uiContainerNum, &pLFile)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Read the record
|
|
|
|
if (RC_BAD( rc = flmRcaRetrieveRec( pDb, NULL, pLFile->uiLfNum, uiErrDrn,
|
|
FALSE, NULL, NULL, &(CorruptInfo.pErrRecord))))
|
|
{
|
|
if (rc != FERR_NOT_FOUND)
|
|
{
|
|
goto Check_Error;
|
|
}
|
|
|
|
// NOTE: Deliberately not bringing in to cache if not found there.
|
|
|
|
if (RC_BAD( rc = FSReadRecord( pDb, pLFile, uiErrDrn,
|
|
&(CorruptInfo.pErrRecord), NULL, NULL)))
|
|
{
|
|
Check_Error:
|
|
|
|
// Record may have been deleted or cannot be returned because of
|
|
// an old view error.
|
|
|
|
if (rc == FERR_NOT_FOUND)
|
|
{
|
|
rc = FERR_OK;
|
|
}
|
|
else if (FlmErrorIsFileCorrupt( rc))
|
|
{
|
|
pIxChkInfo->pDbInfo->pProgress->bPhysicalCorrupt = TRUE;
|
|
rc = FERR_OK;
|
|
goto Exit;
|
|
}
|
|
else
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Generate index keys for the current index and record
|
|
|
|
if (CorruptInfo.pErrRecord != NULL)
|
|
{
|
|
if (RC_BAD( rc = flmGetRecKeys( pDb, pIxd, CorruptInfo.pErrRecord,
|
|
CorruptInfo.pErrRecord->getContainerID(), TRUE, pTmpPool,
|
|
&(CorruptInfo.pErrRecordKeyList))))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
*pbFixErrRV = FALSE;
|
|
if ((pIxChkInfo->pDbInfo->fnStatusFunc) &&
|
|
(RC_OK( pIxChkInfo->pDbInfo->LastStatusRc)))
|
|
{
|
|
pIxChkInfo->pDbInfo->LastStatusRc =
|
|
(*pIxChkInfo->pDbInfo->fnStatusFunc) ( FLM_PROBLEM_STATUS,
|
|
(void *) &CorruptInfo, (void *) pbFixErrRV,
|
|
pIxChkInfo->pDbInfo->pProgress->AppArg);
|
|
}
|
|
|
|
Exit:
|
|
|
|
if (CorruptInfo.pErrRecord)
|
|
{
|
|
CorruptInfo.pErrRecord->Release();
|
|
}
|
|
|
|
if (CorruptInfo.pErrIxKey)
|
|
{
|
|
CorruptInfo.pErrIxKey->Release();
|
|
}
|
|
|
|
if (CorruptInfo.pErrRecordKeyList)
|
|
{
|
|
REC_KEY * pTempKey = CorruptInfo.pErrRecordKeyList;
|
|
|
|
while (pTempKey)
|
|
{
|
|
pTempKey->pKey->Release();
|
|
pTempKey = pTempKey->pNextKey;
|
|
}
|
|
}
|
|
|
|
// Remove any keys added to the KRef
|
|
|
|
if (bResetKRef)
|
|
{
|
|
KYAbortCurrentRecord( pDb);
|
|
}
|
|
|
|
// Reset the DB's temporary pool
|
|
|
|
pDb->TempPool.poolReset( pDbPoolMark);
|
|
|
|
// Reset the index check pool
|
|
|
|
pIxChkInfo->pool.poolReset( pIxPoolMark);
|
|
return (rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: This routine verifies that a key is not unique
|
|
****************************************************************************/
|
|
FSTATIC RCODE chkVerifyKeyNotUnique(
|
|
STATE_INFO * pStateInfo,
|
|
FLMUINT uiIndex,
|
|
FLMBYTE * pucKey,
|
|
FLMUINT uiKeyLen,
|
|
FLMUINT * puiRefCountRV)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FDB * pDb = pStateInfo->pDb;
|
|
FlmRecord * pKeyTree = NULL;
|
|
IXD * pIxd;
|
|
FLMUINT uiRefDrn;
|
|
|
|
*puiRefCountRV = 0;
|
|
|
|
// Get the IXD
|
|
|
|
if (RC_BAD( rc = fdictGetIndex( pDb->pDict, pDb->pFile->bInLimitedMode,
|
|
uiIndex, NULL, &pIxd)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// This routine should not be called unless the index is a unique
|
|
// index.
|
|
|
|
flmAssert( ((pIxd->uiFlags & IXD_UNIQUE) != 0));
|
|
|
|
// Generate the key tree from the collation key.
|
|
|
|
if (RC_BAD( rc = flmIxKeyOutput( pIxd, pucKey, uiKeyLen, &pKeyTree, FALSE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Count up to the first two references for the key.
|
|
|
|
if (RC_BAD( rc = FlmKeyRetrieve( (HFDB) pDb, uiIndex,
|
|
pKeyTree->getContainerID(), pKeyTree, 0, FO_EXACT,
|
|
NULL, &uiRefDrn)))
|
|
{
|
|
|
|
// If the key is NOT found, the problem no longer exists.
|
|
|
|
if ((rc == FERR_NOT_FOUND) ||
|
|
(rc == FERR_BOF_HIT) ||
|
|
(rc == FERR_EOF_HIT))
|
|
{
|
|
rc = FERR_OK;
|
|
}
|
|
|
|
goto Exit;
|
|
}
|
|
|
|
// Found at least one reference.
|
|
|
|
*puiRefCountRV = 1;
|
|
|
|
// Go exclusive of the last key/reference found to see if there are
|
|
// more references for the key.
|
|
|
|
if (RC_BAD( rc = FlmKeyRetrieve( (HFDB) pDb, uiIndex,
|
|
pKeyTree->getContainerID(), pKeyTree, uiRefDrn,
|
|
FO_KEY_EXACT | FO_EXCL, NULL, &uiRefDrn)))
|
|
{
|
|
if ((rc == FERR_NOT_FOUND) ||
|
|
(rc == FERR_BOF_HIT) ||
|
|
(rc == FERR_EOF_HIT))
|
|
{
|
|
rc = FERR_OK;
|
|
}
|
|
|
|
goto Exit;
|
|
}
|
|
|
|
// May be more references, but it is sufficient to know that there are
|
|
// at least two.
|
|
|
|
*puiRefCountRV = 2;
|
|
|
|
Exit:
|
|
|
|
return (rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
FSTATIC RCODE chkStartUpdate(
|
|
STATE_INFO * pStateInfo,
|
|
IX_CHK_INFO * pIxChkInfo)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
RCODE rc2 = FERR_OK;
|
|
FDB * pDb = pStateInfo->pDb;
|
|
FLMBOOL bAbortedReadTrans = FALSE;
|
|
|
|
if (flmGetDbTransType( pDb) == FLM_READ_TRANS)
|
|
{
|
|
|
|
// Free the KrefCntrl
|
|
|
|
KrefCntrlFree( pDb);
|
|
|
|
// Abort the read transaction
|
|
|
|
if (RC_BAD( rc = flmAbortDbTrans( pDb)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
bAbortedReadTrans = TRUE;
|
|
|
|
// Try to start an update transaction
|
|
|
|
if (RC_BAD( rc = flmBeginDbTrans( pDb, FLM_UPDATE_TRANS,
|
|
FLM_NO_TIMEOUT, FLM_DONT_POISON_CACHE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
pIxChkInfo->pDbInfo->bStartedUpdateTrans = TRUE;
|
|
}
|
|
|
|
if (RC_BAD( pIxChkInfo->pDbInfo->LastStatusRc))
|
|
{
|
|
rc = pIxChkInfo->pDbInfo->LastStatusRc;
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
|
|
// If something went wrong after the update transaction was started,
|
|
// abort the transaction.
|
|
|
|
if (RC_BAD( rc))
|
|
{
|
|
if (pIxChkInfo->pDbInfo->bStartedUpdateTrans == TRUE)
|
|
{
|
|
(void) flmAbortDbTrans( pDb);
|
|
pIxChkInfo->pDbInfo->bStartedUpdateTrans = FALSE;
|
|
}
|
|
}
|
|
|
|
// Re-start the read transaction.
|
|
|
|
if (bAbortedReadTrans == TRUE &&
|
|
pIxChkInfo->pDbInfo->bStartedUpdateTrans == FALSE)
|
|
{
|
|
rc2 = flmBeginDbTrans( pDb, FLM_READ_TRANS, 0, FLM_DONT_POISON_CACHE);
|
|
}
|
|
|
|
rc = (RCODE) ((rc != FERR_OK) ? (RCODE) rc : (RCODE) rc2);
|
|
return (rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
FSTATIC RCODE chkEndUpdate(
|
|
STATE_INFO * pStateInfo,
|
|
IX_CHK_INFO * pIxChkInfo)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
RCODE rc2 = FERR_OK;
|
|
|
|
if (pIxChkInfo->pDbInfo->bStartedUpdateTrans == TRUE)
|
|
{
|
|
|
|
// Commit the update transaction that was started. If the
|
|
// transaction started by the application, do not commit it.
|
|
|
|
if (RC_BAD( rc = flmCommitDbTrans( pStateInfo->pDb, 0, FALSE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
pIxChkInfo->pDbInfo->bStartedUpdateTrans = FALSE;
|
|
}
|
|
|
|
Exit:
|
|
|
|
// Re-start read transaction
|
|
|
|
if (flmGetDbTransType( pStateInfo->pDb) == FLM_NO_TRANS)
|
|
{
|
|
rc2 = flmBeginDbTrans( pStateInfo->pDb, FLM_READ_TRANS, 0,
|
|
FLM_DONT_POISON_CACHE);
|
|
}
|
|
|
|
rc = (RCODE) ((rc != FERR_OK) ? (RCODE) rc : (RCODE) rc2);
|
|
return (rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Initializes a result set for use by the logical check code
|
|
****************************************************************************/
|
|
RCODE chkRSInit(
|
|
const char * pIoPath,
|
|
IF_ResultSet ** ppRSet)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
IF_ResultSet * pRSet = NULL;
|
|
F_ChkResultSetCompare * pCompare = NULL;
|
|
|
|
if( RC_BAD( rc = FlmAllocResultSet( &pRSet)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( (pCompare = f_new F_ChkResultSetCompare) == NULL)
|
|
{
|
|
rc = RC_SET( FERR_MEM);
|
|
goto Exit;
|
|
}
|
|
|
|
if( RC_BAD( rc = pRSet->setupResultSet( pIoPath, pCompare, 0)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
*ppRSet = pRSet;
|
|
pRSet = NULL;
|
|
|
|
Exit:
|
|
|
|
if( pRSet)
|
|
{
|
|
pRSet->Release();
|
|
}
|
|
|
|
if( pCompare)
|
|
{
|
|
pCompare->Release();
|
|
}
|
|
|
|
return (rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Sorts a result set.
|
|
****************************************************************************/
|
|
RCODE chkRSFinalize(
|
|
IX_CHK_INFO * pIxChkInfo,
|
|
FLMUINT64 * pui64TotalEntries)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
DB_CHECK_PROGRESS * pProgress = pIxChkInfo->pDbInfo->pProgress;
|
|
DB_CHECK_PROGRESS saveInfo;
|
|
F_ChkSortStatus chkSortStatus( pIxChkInfo);
|
|
|
|
// Save the current check phase information.
|
|
|
|
f_memcpy( &saveInfo, pProgress, sizeof(DB_CHECK_PROGRESS));
|
|
|
|
// Set information for the result set sort phase.
|
|
|
|
pProgress->iCheckPhase = CHECK_RS_SORT;
|
|
pProgress->bStartFlag = TRUE;
|
|
pProgress->ui64NumRSUnits = 0;
|
|
pProgress->ui64NumRSUnitsDone = 0;
|
|
|
|
if (RC_BAD( rc = pIxChkInfo->pRSet->finalizeResultSet(
|
|
&chkSortStatus, pui64TotalEntries)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
|
|
f_memcpy( pProgress, &saveInfo, sizeof( DB_CHECK_PROGRESS));
|
|
pProgress->bStartFlag = TRUE;
|
|
|
|
return (rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
RCODE chkBlkRead(
|
|
DB_INFO * pDbInfo,
|
|
FLMUINT uiBlkAddress,
|
|
LFILE * pLFile,
|
|
FLMBYTE ** ppBlk,
|
|
SCACHE ** ppSCache,
|
|
eCorruptionType * peCorruption)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FDB * pDb = pDbInfo->pDb;
|
|
FILE_HDR * pFileHdr = &pDb->pFile->FileHdr;
|
|
|
|
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,
|
|
F_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]
|
|
: 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:
|
|
case FLM_FOP_LARGE:
|
|
{
|
|
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;
|
|
}
|
|
|
|
default:
|
|
{
|
|
*peElmCorruptCode = FLM_BAD_FOP;
|
|
}
|
|
}
|
|
|
|
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 = pDbInfo->pDb->TempPool.poolMark();
|
|
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 = pTmpPool->poolMark();
|
|
|
|
// 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
|
|
|
|
pTmpPool->poolReset( 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
|
|
|
|
pDbInfo->pDb->TempPool.poolReset( pDbPoolMark);
|
|
bResetDbPool = FALSE;
|
|
}
|
|
}
|
|
|
|
if (pRecord)
|
|
{
|
|
pRecord->clear();
|
|
}
|
|
|
|
pValue = pTempValue = NULL;
|
|
pTmpPool->poolReset( 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)
|
|
{
|
|
pDbInfo->pDb->TempPool.poolReset( pDbPoolMark);
|
|
}
|
|
|
|
if (*peElmCorruptCode != FLM_NO_CORRUPTION || RC_BAD( rc))
|
|
{
|
|
pValue = pTempValue = NULL;
|
|
pTmpPool->poolReset( 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,
|
|
F_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 re-count 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,
|
|
F_Pool * pPool,
|
|
LF_STATS * pLfStats,
|
|
LFILE * pLFile,
|
|
LF_STATS * pCurrLfStats,
|
|
FLMBOOL * pbCurrLfLevelChangedRV)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
SCACHE * pSCache = NULL;
|
|
FLMBYTE * pBlk = NULL;
|
|
FLMUINT uiSaveLevel;
|
|
eCorruptionType eBlkCorruptionCode;
|
|
|
|
// 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)
|
|
{
|
|
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)
|
|
{
|
|
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;
|
|
|
|
// 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( RC_BAD( rc = pPool->poolCalloc(
|
|
(FLMUINT) (sizeof(LEVEL_INFO) * pLfStats->uiNumLevels),
|
|
(void **)&pLfStats->pLevelInfo)))
|
|
{
|
|
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,
|
|
F_Pool * pPool)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
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;
|
|
|
|
// 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 = 0;
|
|
pDbInfo->pProgress->uiNumIndexes = 0;
|
|
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( RC_BAD( rc = pPool->poolCalloc(
|
|
(FLMUINT)(sizeof(LF_HDR) * pDbInfo->pProgress->uiNumLogicalFiles),
|
|
(void **)&pDbInfo->pLogicalFiles)))
|
|
{
|
|
rc = RC_SET( FERR_MEM);
|
|
goto Exit;
|
|
}
|
|
|
|
if( RC_BAD( rc = pPool->poolCalloc(
|
|
(FLMUINT)(sizeof(LF_STATS) * pDbInfo->pProgress->uiNumLogicalFiles),
|
|
(void **)&pDbInfo->pProgress->pLfStats)))
|
|
{
|
|
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));
|
|
|
|
pDbInfo->pLogicalFiles [uiTmpIxOffset].pLfStats =
|
|
&pDbInfo->pProgress->pLfStats [uiTmpIxOffset];
|
|
|
|
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( RC_BAD( rc = pPool->poolAlloc( sizeof( LFILE), (void **)&pLFile)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
pLogicalFile->pLFile = pLFile;
|
|
|
|
// 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 * 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( RC_BAD( rc = pPool->poolAlloc(
|
|
(FLMUINT)(sizeof(IXD) + sizeof(IFD) * pTmpIxd->uiNumFlds),
|
|
(void **)&pLogicalFile->pIxd)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
pLogicalFile->pIfd = (IFD *) (&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,
|
|
F_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;
|
|
F_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 = pPool->poolMark();
|
|
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;
|
|
|
|
// 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.
|
|
|
|
tmpPool.poolInit( 512);
|
|
pDbInfo->bReposition = FALSE;
|
|
|
|
rc = chkVerifySubTree( pDbInfo, pIxChkInfo, NULL,
|
|
&State[pLfStats->uiNumLevels - 1],
|
|
pLFile->uiRootBlk, &tmpPool, pucResetKey,
|
|
uiResetKeyLen, uiResetDrn);
|
|
tmpPool.poolFree();
|
|
|
|
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));
|
|
pIxInfoRV->pool.poolInit( 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 = gv_FlmSysData.pFileSystem->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))
|
|
{
|
|
pIxInfoRV->pool.poolFree();
|
|
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)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FLMUINT uiKeyLen;
|
|
REC_KEY * pKey;
|
|
FLMBYTE ucBuf[MAX_KEY_SIZ + RS_KEY_OVERHEAD];
|
|
|
|
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 = pIxChkInfo->pRSet->addEntry( 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);
|
|
}
|