git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@486 0109f412-320b-0410-ab79-c3e0c5ffbbe6
935 lines
23 KiB
C++
935 lines
23 KiB
C++
//------------------------------------------------------------------------------
|
|
// Desc: Check a 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 3112 2006-01-19 13:12:40 -0700 (Thu, 19 Jan 2006) dsanders $
|
|
//------------------------------------------------------------------------------
|
|
|
|
#include "flaimsys.h"
|
|
|
|
/****************************************************************************
|
|
Desc: Constructor
|
|
****************************************************************************/
|
|
F_DbCheck::~F_DbCheck()
|
|
{
|
|
if (m_pXRefRS)
|
|
{
|
|
m_pXRefRS->Release();
|
|
m_pXRefRS = NULL;
|
|
}
|
|
|
|
// Cleanup any temporary index check files
|
|
|
|
if (m_pIxRSet)
|
|
{
|
|
m_pIxRSet->Release();
|
|
}
|
|
f_free( &m_puiIxArray);
|
|
|
|
if (m_pDb)
|
|
{
|
|
m_pDb->Release();
|
|
}
|
|
|
|
if (m_pDbInfo)
|
|
{
|
|
m_pDbInfo->Release();
|
|
}
|
|
|
|
(void)closeAndDeleteResultSetDb();
|
|
|
|
if (m_pRandGen)
|
|
{
|
|
m_pRandGen->Release();
|
|
}
|
|
|
|
if (m_pBtPool)
|
|
{
|
|
m_pBtPool->Release();
|
|
}
|
|
if (m_pBlkEntries)
|
|
{
|
|
f_free( &m_pBlkEntries);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
RCODE F_DbCheck::createAndOpenResultSetDb( void)
|
|
{
|
|
RCODE rc = NE_XFLM_OK;
|
|
XFLM_CREATE_OPTS createOpts;
|
|
|
|
if (m_pResultSetDb)
|
|
{
|
|
if (RC_BAD( rc = closeAndDeleteResultSetDb()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
f_memset( &createOpts, 0, sizeof( XFLM_CREATE_OPTS));
|
|
for (;;)
|
|
{
|
|
|
|
// Generate a random file name
|
|
|
|
f_sprintf( m_szResultSetDibName,
|
|
"%d.db", (int)m_pRandGen->getUINT32( 100, 20000));
|
|
|
|
if (RC_OK( rc = gv_pXFlmDbSystem->dbCreate(
|
|
m_szResultSetDibName, NULL, NULL, NULL, NULL,
|
|
&createOpts, TRUE, (IF_Db **)&m_pResultSetDb)))
|
|
{
|
|
break;
|
|
}
|
|
if (rc == NE_XFLM_FILE_EXISTS || rc == NE_FLM_IO_ACCESS_DENIED)
|
|
{
|
|
rc = NE_XFLM_OK;
|
|
}
|
|
else
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Shouldn't have an RFL object - don't want anything
|
|
// logged by this database.
|
|
|
|
flmAssert( !m_pResultSetDb->getDatabase()->m_pRfl);
|
|
|
|
Exit:
|
|
|
|
return rc;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Close the database file and delete it.
|
|
****************************************************************************/
|
|
RCODE F_DbCheck::closeAndDeleteResultSetDb( void)
|
|
{
|
|
RCODE rc = NE_XFLM_OK;
|
|
|
|
if (m_pResultSetDb)
|
|
{
|
|
if (m_pResultSetDb->getTransType() != XFLM_NO_TRANS)
|
|
{
|
|
m_pResultSetDb->transAbort();
|
|
}
|
|
m_pResultSetDb->Release();
|
|
m_pResultSetDb = NULL;
|
|
}
|
|
|
|
if (RC_BAD( rc = gv_pXFlmDbSystem->dbRemove(
|
|
m_szResultSetDibName, NULL, NULL, TRUE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
f_memset( m_szResultSetDibName, 0, sizeof( m_szResultSetDibName));
|
|
|
|
Exit:
|
|
|
|
return rc;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Method to get a new F_BtResultSet object. This method will create a
|
|
new collection for the result set to use before handing it off.
|
|
****************************************************************************/
|
|
RCODE F_DbCheck::getBtResultSet(
|
|
F_BtResultSet ** ppBtRSet
|
|
)
|
|
{
|
|
RCODE rc = NE_XFLM_OK;
|
|
FLMUINT uiCollection;
|
|
F_BtResultSet * pBtRSet = NULL;
|
|
F_Database * pDatabase = NULL;
|
|
|
|
// If there is already a BtResultSet, let's get rid of it.
|
|
|
|
if (*ppBtRSet)
|
|
{
|
|
(*ppBtRSet)->Release();
|
|
*ppBtRSet = NULL;
|
|
}
|
|
|
|
// Create the new BtResultSet object first.
|
|
|
|
if ((pBtRSet = f_new F_BtResultSet( m_pResultSetDb, m_pBtPool)) == NULL)
|
|
{
|
|
rc = RC_SET( NE_XFLM_MEM);
|
|
goto Exit;
|
|
}
|
|
|
|
pDatabase = m_pResultSetDb->m_pDatabase;
|
|
|
|
for (;;)
|
|
{
|
|
|
|
// Now create a new collection. Randomly select a collection number to use.
|
|
|
|
uiCollection = m_pRandGen->getUINT32( 100, XFLM_MAX_COLLECTION_NUM);
|
|
|
|
// Check to see if it already exists.
|
|
|
|
if (RC_OK( rc = pDatabase->lFileCreate( m_pResultSetDb,
|
|
&pBtRSet->m_Collection.lfInfo, &pBtRSet->m_Collection, uiCollection,
|
|
XFLM_LF_COLLECTION, FALSE, TRUE)))
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (rc != NE_XFLM_EXISTS)
|
|
{
|
|
goto Exit;
|
|
}
|
|
rc = NE_XFLM_OK;
|
|
}
|
|
|
|
*ppBtRSet = pBtRSet;
|
|
pBtRSet = NULL;
|
|
|
|
Exit:
|
|
|
|
if (pBtRSet)
|
|
{
|
|
pBtRSet->Release();
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc : Checks for physical corruption in a FLAIM database.
|
|
DNote: 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.
|
|
****************************************************************************/
|
|
RCODE F_DbCheck::dbCheck(
|
|
const char * pszDbFileName,
|
|
// [IN] Full path and file name of the database which
|
|
// is to be checked. NULL can be passed as the value of
|
|
// this parameter if pDb is non-NULL.
|
|
const char * pszDataDir,
|
|
// [IN] Directory for data files.
|
|
const char * pszRflDir,
|
|
// [IN] RFL directory. NULL can be passed as the value of
|
|
// this parameter to indicate that the log files are located
|
|
// in the same directory as the database or if pDb is non-NULL.
|
|
const char * pszPassword,
|
|
// [IN] Database password. Needed to open the database if the database
|
|
// key has been wrapped in a password. NULL by default.
|
|
FLMUINT uiFlags,
|
|
// [IN] Check flags. Possible flags include:
|
|
//
|
|
// XFLM_ONLINE. This flag instructs the check to repair any
|
|
// index corruptions it finds. The database must have been
|
|
// opened in read/write mode in order for the check to
|
|
// successfully repair corruptions. An update transaction
|
|
// will be started whenever a corruption is repaired.
|
|
//
|
|
// XFLM_DO_LOGICAL_CHECK. This flag instructs the check to
|
|
// perform a logical check of the databases's indexes
|
|
// in addition to the structural check.
|
|
//
|
|
// XFLM_SKIP_DOM_LINK_CHECK. This flag instructs the check to skip
|
|
// verifying the DOM links. This check can take quite a long time
|
|
// to execute.
|
|
//
|
|
// XFLM_ALLOW_LIMITED_MODE. This flag instructs the check to allow
|
|
// the database to be opened in limited mode if the database key is
|
|
// wrapped in a password and the password we pass is incorrect
|
|
// (or non-existent).
|
|
IF_DbInfo ** ppDbInfo,
|
|
// [IN] Pointer to a DB_INFO structure which is used to store
|
|
// statistics collected during the database check.
|
|
IF_DbCheckStatus * pDbCheckStatus
|
|
// [IN] Status interface. Functions in this interface are called
|
|
// periodically to iform the calling application of the progress
|
|
// being made. This allows the application to monitor and/or display
|
|
// the progress of the database check. NULL may be passed as the
|
|
// value of this parameter if the callback feature is not needed.
|
|
)
|
|
{
|
|
RCODE rc = NE_XFLM_OK;
|
|
FLMBYTE * pBlk = NULL;
|
|
FLMUINT uiFileEnd;
|
|
FLMUINT uiBlockSize;
|
|
FLMUINT uiLoop;
|
|
FLMUINT64 ui64TmpSize;
|
|
FLMBOOL bStartOver;
|
|
FLMBOOL bOkToCloseTrans = FALSE;
|
|
FLMBOOL bAllowLimitedMode = ( uiFlags & XFLM_ALLOW_LIMITED_MODE)
|
|
? TRUE
|
|
: FALSE;
|
|
|
|
if (RC_BAD( rc = gv_pXFlmDbSystem->dbOpen( pszDbFileName, pszDataDir,
|
|
pszRflDir, pszPassword, bAllowLimitedMode, (IF_Db **)&m_pDb)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if ((m_pDbInfo = f_new F_DbInfo) == NULL)
|
|
{
|
|
rc = RC_SET( NE_XFLM_MEM);
|
|
goto Exit;
|
|
}
|
|
if (ppDbInfo)
|
|
{
|
|
*ppDbInfo = m_pDbInfo;
|
|
(*ppDbInfo)->AddRef();
|
|
}
|
|
|
|
m_pDbCheckStatus = pDbCheckStatus;
|
|
m_LastStatusRc = NE_XFLM_OK;
|
|
|
|
// Get the file size...
|
|
|
|
if (uiFlags & XFLM_SKIP_DOM_LINK_CHECK)
|
|
{
|
|
m_bSkipDOMLinkCheck = TRUE;
|
|
}
|
|
|
|
// Initialize the information block and Progress structure.
|
|
|
|
// 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 = m_pDb->m_pDatabase->getBlockSize();
|
|
|
|
// Allocate memory to use for reading through the data blocks.
|
|
|
|
if( RC_BAD( rc = f_alloc( uiBlockSize, &pBlk)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if ((m_pBtPool = f_new F_BtPool) == NULL)
|
|
{
|
|
rc = RC_SET( NE_XFLM_MEM);
|
|
goto Exit;
|
|
}
|
|
|
|
if (RC_BAD( rc = m_pBtPool->btpInit()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Setup the result set database.
|
|
|
|
if( RC_BAD( rc = FlmAllocRandomGenerator( &m_pRandGen)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
m_pRandGen->setSeed( 9768);
|
|
|
|
if (RC_BAD( rc = createAndOpenResultSetDb()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
Begin_Check:
|
|
|
|
// Initialize all statistics in the DB_INFO structure.
|
|
|
|
rc = NE_XFLM_OK;
|
|
bStartOver = FALSE;
|
|
|
|
m_pDbInfo->m_ui64FileSize = 0;
|
|
m_pDbInfo->freeLogicalFiles();
|
|
m_bPhysicalCorrupt = FALSE;
|
|
m_bIndexCorrupt = FALSE;
|
|
m_uiFlags = uiFlags;
|
|
m_bStartedUpdateTrans = FALSE;
|
|
f_memset( &m_pDbInfo->m_AvailBlocks, 0, sizeof( BLOCK_INFO));
|
|
f_memset( &m_pDbInfo->m_LFHBlocks, 0, sizeof( BLOCK_INFO));
|
|
|
|
f_memset( &m_Progress, 0, sizeof( XFLM_PROGRESS_CHECK_INFO));
|
|
|
|
/* Get the dictionary information for the file. */
|
|
|
|
if (RC_BAD( rc = getDictInfo()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
m_Progress.ui64BytesExamined = 0;
|
|
|
|
for (uiLoop = 1;
|
|
uiLoop <= MAX_DATA_BLOCK_FILE_NUMBER;
|
|
uiLoop++)
|
|
{
|
|
if (RC_BAD( m_pDb->m_pSFileHdl->getFileSize( uiLoop, &ui64TmpSize)))
|
|
{
|
|
break;
|
|
}
|
|
|
|
m_Progress.ui64FileSize += ui64TmpSize;
|
|
}
|
|
|
|
// See if we have a valid end of file
|
|
|
|
uiFileEnd = m_pDb->m_uiLogicalEOF;
|
|
if (FSGetFileOffset( uiFileEnd) % uiBlockSize != 0)
|
|
{
|
|
if (RC_BAD( rc = chkReportError( FLM_BAD_FILE_SIZE, XFLM_LOCALE_NONE,
|
|
0, 0, 0xFF, uiFileEnd, 0, 0, 0)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
else if (m_Progress.ui64FileSize <
|
|
FSGetSizeInBytes( m_pDb->m_pDatabase-> getMaxFileSize(),
|
|
uiFileEnd))
|
|
{
|
|
m_Progress.ui64FileSize =
|
|
FSGetSizeInBytes( m_pDb->m_pDatabase->getMaxFileSize(),
|
|
uiFileEnd);
|
|
}
|
|
|
|
m_pDbInfo->m_ui64FileSize = m_Progress.ui64FileSize;
|
|
|
|
// Verify the LFH blocks, B-Trees, and the AVAIL list.
|
|
|
|
if( RC_BAD( rc = verifyLFHBlocks( &bStartOver)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
if (bStartOver)
|
|
{
|
|
goto Begin_Check;
|
|
}
|
|
|
|
// Check the b-trees.
|
|
|
|
if (RC_BAD( rc = verifyBTrees( &bStartOver)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
if (bStartOver)
|
|
{
|
|
goto Begin_Check;
|
|
}
|
|
|
|
// Check the avail list.
|
|
|
|
if (RC_BAD( rc = verifyAvailList( &bStartOver)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
if (bStartOver)
|
|
{
|
|
goto Begin_Check;
|
|
}
|
|
|
|
Exit:
|
|
|
|
if ((m_bPhysicalCorrupt || m_bIndexCorrupt) &&
|
|
!gv_pXFlmDbSystem->errorIsFileCorrupt( rc))
|
|
{
|
|
rc = RC_SET( NE_XFLM_DATA_ERROR);
|
|
}
|
|
|
|
if (RC_OK( rc) && RC_BAD( m_LastStatusRc))
|
|
{
|
|
rc = m_LastStatusRc;
|
|
}
|
|
|
|
if (m_pDb)
|
|
{
|
|
// Close down the transaction, if one is going
|
|
|
|
if( bOkToCloseTrans &&
|
|
m_pDb->getTransType( ) == XFLM_READ_TRANS)
|
|
{
|
|
m_pDb->krefCntrlFree();
|
|
m_pDb->transAbort();
|
|
}
|
|
}
|
|
|
|
// Free memory, if allocated
|
|
|
|
if (pBlk)
|
|
{
|
|
f_free( &pBlk);
|
|
}
|
|
|
|
// Close the FLAIM database we opened.
|
|
|
|
if (m_pDb)
|
|
{
|
|
m_pDb->Release();
|
|
m_pDb = NULL;
|
|
}
|
|
|
|
return( rc);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Desc: This routine opens a file and reads its dictionary into memory.
|
|
*****************************************************************************/
|
|
RCODE F_DbCheck::getDictInfo()
|
|
{
|
|
RCODE rc = NE_XFLM_OK;
|
|
|
|
// Close down the transaction, if one is going.
|
|
|
|
if (m_pDb->getTransType() != XFLM_UPDATE_TRANS)
|
|
{
|
|
if (m_pDb->getTransType() == XFLM_READ_TRANS)
|
|
{
|
|
(void)m_pDb->transAbort();
|
|
}
|
|
|
|
// Start a read transaction on the file to ensure we are connected
|
|
// to the file's dictionary structures.
|
|
|
|
if (RC_BAD( rc = m_pDb->transBegin( XFLM_READ_TRANS,
|
|
FLM_NO_TIMEOUT, XFLM_DONT_POISON_CACHE, &m_pDbInfo->m_dbHdr)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
f_memcpy( &m_pDbInfo->m_dbHdr, m_pDb->m_pDatabase->getUncommittedDbHdr(),
|
|
sizeof( XFLM_DB_HDR));
|
|
}
|
|
|
|
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.
|
|
*********************************************************************/
|
|
RCODE F_DbCheck::verifyBlkChain(
|
|
BLOCK_INFO * pBlkInfo,
|
|
FLMUINT uiLocale,
|
|
FLMUINT uiFirstBlkAddr,
|
|
FLMUINT uiBlkType,
|
|
FLMBOOL * pbStartOverRV
|
|
)
|
|
{
|
|
RCODE rc = NE_XFLM_OK;
|
|
FLMINT iVerifyCode = 0;
|
|
F_CachedBlock * pSCache = NULL;
|
|
F_BLK_HDR * pBlkHdr = NULL;
|
|
FLMUINT uiPrevBlkAddress;
|
|
FLMUINT uiBlkCount = 0;
|
|
STATE_INFO StateInfo;
|
|
FLMBOOL bStateInitialized = FALSE;
|
|
FLMUINT64 ui64SaveBytesExamined;
|
|
FLMUINT uiBlockSize = m_pDb->m_pDatabase->getBlockSize();
|
|
FLMUINT uiMaxBlocks = (FLMUINT)(FSGetSizeInBytes(
|
|
m_pDb->m_pDatabase->getMaxFileSize(),
|
|
m_pDb->m_uiLogicalEOF) /
|
|
(FLMUINT64)uiBlockSize);
|
|
|
|
uiPrevBlkAddress = 0;
|
|
|
|
/* There must be at least ONE block if it is the LFH chain. */
|
|
|
|
if ((uiBlkType == BT_LFH_BLK) && (uiFirstBlkAddr == 0))
|
|
{
|
|
iVerifyCode = FLM_BAD_LFH_LIST_PTR;
|
|
(void)chkReportError( iVerifyCode,
|
|
uiLocale,
|
|
0,
|
|
0,
|
|
0xFF,
|
|
0,
|
|
0,
|
|
0,
|
|
0);
|
|
goto Exit;
|
|
}
|
|
|
|
/* Read through all of the blocks, verifying them as we go. */
|
|
|
|
Restart_Chain:
|
|
uiBlkCount = 0;
|
|
flmInitReadState( &StateInfo,
|
|
&bStateInitialized,
|
|
(FLMUINT)m_pDb->m_pDatabase->
|
|
m_lastCommittedDbHdr.ui32DbVersion,
|
|
m_pDb,
|
|
NULL,
|
|
(FLMUINT)((uiBlkType == BT_FREE)
|
|
? (FLMUINT)0xFF
|
|
: (FLMUINT)0),
|
|
uiBlkType,
|
|
NULL);
|
|
|
|
ui64SaveBytesExamined = m_Progress.ui64BytesExamined;
|
|
StateInfo.ui32BlkAddress = (FLMUINT32)uiFirstBlkAddr;
|
|
|
|
while ((StateInfo.ui32BlkAddress != 0) && (uiBlkCount < uiMaxBlocks))
|
|
{
|
|
StateInfo.pBlkHdr = NULL;
|
|
if( RC_BAD( rc = blkRead( StateInfo.ui32BlkAddress, &pBlkHdr,
|
|
&pSCache, &iVerifyCode)))
|
|
{
|
|
if (rc == NE_XFLM_OLD_VIEW)
|
|
{
|
|
FLMUINT uiSaveDictSeq = m_pDb->m_pDict->getDictSeq();
|
|
|
|
if (RC_BAD( rc = getDictInfo()))
|
|
goto Exit;
|
|
|
|
// If the dictionary ID changed, start over.
|
|
|
|
if (m_pDb->m_pDict->getDictSeq() != uiSaveDictSeq)
|
|
{
|
|
*pbStartOverRV = TRUE;
|
|
goto Exit;
|
|
}
|
|
|
|
m_Progress.ui64BytesExamined = ui64SaveBytesExamined;
|
|
goto Restart_Chain;
|
|
}
|
|
pBlkInfo->iErrCode = iVerifyCode;
|
|
pBlkInfo->uiNumErrors++;
|
|
rc = chkReportError( iVerifyCode,
|
|
uiLocale,
|
|
0,
|
|
0,
|
|
0xFF,
|
|
StateInfo.ui32BlkAddress,
|
|
0,
|
|
0,
|
|
0);
|
|
}
|
|
StateInfo.pBlkHdr = pBlkHdr;
|
|
uiBlkCount++;
|
|
m_Progress.ui64BytesExamined += (FLMUINT64)uiBlockSize;
|
|
if (RC_BAD( rc = chkCallProgFunc()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
f_yieldCPU();
|
|
|
|
if ((iVerifyCode = flmVerifyBlockHeader( &StateInfo,
|
|
pBlkInfo,
|
|
uiBlockSize,
|
|
0xFFFFFFFF,
|
|
uiPrevBlkAddress,
|
|
TRUE)) != 0)
|
|
{
|
|
pBlkInfo->iErrCode = iVerifyCode;
|
|
pBlkInfo->uiNumErrors++;
|
|
chkReportError( iVerifyCode,
|
|
uiLocale,
|
|
0,
|
|
0,
|
|
0xFF,
|
|
StateInfo.ui32BlkAddress,
|
|
0,
|
|
0,
|
|
0);
|
|
goto Exit;
|
|
}
|
|
uiPrevBlkAddress = StateInfo.ui32BlkAddress;
|
|
StateInfo.ui32BlkAddress = pBlkHdr->ui32NextBlkInChain;
|
|
}
|
|
if (StateInfo.ui32BlkAddress != 0 && RC_OK( m_LastStatusRc))
|
|
{
|
|
switch (uiBlkType)
|
|
{
|
|
case BT_LFH_BLK:
|
|
iVerifyCode = FLM_BAD_LFH_LIST_END;
|
|
break;
|
|
case BT_FREE:
|
|
iVerifyCode = FLM_BAD_AVAIL_LIST_END;
|
|
break;
|
|
}
|
|
pBlkInfo->iErrCode = iVerifyCode;
|
|
pBlkInfo->uiNumErrors++;
|
|
chkReportError( iVerifyCode,
|
|
uiLocale,
|
|
0,
|
|
0,
|
|
0xFF,
|
|
uiPrevBlkAddress,
|
|
0,
|
|
0,
|
|
0);
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
|
|
if( pSCache)
|
|
{
|
|
ScaReleaseCache( pSCache, FALSE);
|
|
}
|
|
else if( pBlkHdr)
|
|
{
|
|
f_free( &pBlkHdr);
|
|
}
|
|
|
|
if (RC_OK(rc) && (iVerifyCode != 0))
|
|
{
|
|
rc = RC_SET( NE_XFLM_DATA_ERROR);
|
|
}
|
|
|
|
return( rc);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
Desc: This routine verifies the LFH blocks.
|
|
*****************************************************************************/
|
|
RCODE F_DbCheck::verifyLFHBlocks(
|
|
FLMBOOL * pbStartOverRV)
|
|
{
|
|
RCODE rc = NE_XFLM_OK;
|
|
|
|
m_Progress.uiLfNumber = 0;
|
|
m_Progress.uiLfType = 0;
|
|
m_Progress.iCheckPhase = XFLM_CHECK_LFH_BLOCKS;
|
|
m_Progress.bStartFlag = TRUE;
|
|
if (RC_BAD( rc = chkCallProgFunc()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
m_Progress.bStartFlag = FALSE;
|
|
|
|
f_yieldCPU();
|
|
|
|
// Go through the LFH blocks.
|
|
|
|
if (RC_BAD( rc = verifyBlkChain(
|
|
&m_pDbInfo->m_LFHBlocks, XFLM_LOCALE_LFH_LIST,
|
|
(FLMUINT)m_pDb->m_pDatabase->m_lastCommittedDbHdr.ui32FirstLFBlkAddr,
|
|
BT_LFH_BLK, 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.
|
|
*****************************************************************************/
|
|
RCODE F_DbCheck::verifyAvailList(
|
|
FLMBOOL * pbStartOverRV)
|
|
{
|
|
RCODE rc = NE_XFLM_OK;
|
|
|
|
m_Progress.uiLfNumber = 0;
|
|
m_Progress.uiLfType = 0;
|
|
m_Progress.iCheckPhase = XFLM_CHECK_AVAIL_BLOCKS;
|
|
m_Progress.bStartFlag = TRUE;
|
|
if (RC_BAD( rc = chkCallProgFunc()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
m_Progress.bStartFlag = FALSE;
|
|
|
|
f_yieldCPU();
|
|
|
|
if (RC_BAD( rc = verifyBlkChain( &m_pDbInfo->m_AvailBlocks,
|
|
XFLM_LOCALE_AVAIL_LIST,
|
|
m_pDb->m_uiFirstAvailBlkAddr,
|
|
BT_FREE, pbStartOverRV)) ||
|
|
*pbStartOverRV)
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Returns the next B-Tree information that was collected during the
|
|
database check.
|
|
****************************************************************************/
|
|
void FLMAPI F_DbInfo::getBTreeInfo(
|
|
FLMUINT uiNthLogicalFile,
|
|
FLMUINT * puiLfNum,
|
|
eLFileType * peLfType,
|
|
FLMUINT * puiRootBlkAddress,
|
|
FLMUINT * puiNumLevels
|
|
)
|
|
{
|
|
LF_HDR * pLfHdr;
|
|
|
|
if (uiNthLogicalFile < m_uiNumLogicalFiles)
|
|
{
|
|
pLfHdr = &m_pLogicalFiles[ uiNthLogicalFile];
|
|
*puiLfNum = pLfHdr->uiLfNum;
|
|
*peLfType = pLfHdr->eLfType;
|
|
*puiRootBlkAddress = pLfHdr->uiRootBlk;
|
|
*puiNumLevels = pLfHdr->uiNumLevels;
|
|
}
|
|
else
|
|
{
|
|
flmAssert( 0);
|
|
*puiLfNum = 0;
|
|
*puiRootBlkAddress = 0;
|
|
*puiNumLevels = 0;
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Returns block information on the specified b-tree and level within
|
|
that b-tree.
|
|
****************************************************************************/
|
|
void FLMAPI F_DbInfo::getBTreeBlockStats(
|
|
FLMUINT uiNthLogicalFile,
|
|
FLMUINT uiLevel,
|
|
FLMUINT64 * pui64KeyCount,
|
|
FLMUINT64 * pui64BytesUsed,
|
|
FLMUINT64 * pui64ElementCount,
|
|
FLMUINT64 * pui64ContElementCount,
|
|
FLMUINT64 * pui64ContElmBytes,
|
|
FLMUINT * puiBlockCount,
|
|
FLMINT * piLastError,
|
|
FLMUINT * puiNumErrors
|
|
)
|
|
{
|
|
LF_HDR * pLfHdr;
|
|
|
|
if (uiNthLogicalFile < m_uiNumLogicalFiles &&
|
|
uiLevel < m_pLogicalFiles [uiNthLogicalFile].uiNumLevels)
|
|
{
|
|
pLfHdr = &m_pLogicalFiles[ uiNthLogicalFile];
|
|
*pui64KeyCount = pLfHdr->pLevelInfo [uiLevel].ui64KeyCount;
|
|
*pui64BytesUsed = pLfHdr->pLevelInfo [uiLevel].BlockInfo.ui64BytesUsed;
|
|
*pui64ElementCount = pLfHdr->pLevelInfo [uiLevel].BlockInfo.ui64ElementCount;
|
|
*pui64ContElementCount = pLfHdr->pLevelInfo [uiLevel].BlockInfo.ui64ContElementCount;
|
|
*pui64ContElmBytes = pLfHdr->pLevelInfo [uiLevel].BlockInfo.ui64ContElmBytes;
|
|
*puiBlockCount = pLfHdr->pLevelInfo [uiLevel].BlockInfo.uiBlockCount;
|
|
*piLastError = pLfHdr->pLevelInfo [uiLevel].BlockInfo.iErrCode;
|
|
*puiNumErrors = pLfHdr->pLevelInfo [uiLevel].BlockInfo.uiNumErrors;
|
|
}
|
|
else
|
|
{
|
|
flmAssert( 0);
|
|
*pui64KeyCount = 0;
|
|
*pui64BytesUsed = 0;
|
|
*pui64ElementCount = 0;
|
|
*pui64ContElementCount = 0;
|
|
*pui64ContElmBytes = 0;
|
|
*puiBlockCount = 0;
|
|
*piLastError = 0;
|
|
*puiNumErrors = 0;
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
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.
|
|
****************************************************************************/
|
|
RCODE FLMAPI F_DbSystem::dbCheck(
|
|
const char * pszDbFileName,
|
|
// [IN] Full path and file name of the database which
|
|
// is to be checked. NULL can be passed as the value of
|
|
// this parameter if pDb is non-NULL.
|
|
const char * pszDataDir,
|
|
// [IN] Directory for data files.
|
|
const char * pszRflDir,
|
|
// [IN] RFL directory. NULL can be passed as the value of
|
|
// this parameter to indicate that the log files are located
|
|
// in the same directory as the database or if pDb is non-NULL.
|
|
const char * pszPassword,
|
|
// [IN] Database password. This is necessary to open the database if
|
|
// the key has been wrapped in a password. NULL by default.
|
|
FLMUINT uiFlags,
|
|
// [IN] Check flags. Possible flags include:
|
|
//
|
|
// XFLM_ONLINE. This flag instructs the check to repair any
|
|
// index corruptions it finds. The database must have been
|
|
// opened in read/write mode in order for the check to
|
|
// successfully repair corruptions. An update transaction
|
|
// will be started whenever a corruption is repaired.
|
|
//
|
|
// XFLM_DO_LOGICAL_CHECK. This flag instructs the check to
|
|
// perform a logical check of the databases's indexes
|
|
// in addition to the structural check.
|
|
//
|
|
// XFLM_SKIP_DOM_LINK_CHECK. This flag instructs the check to skip
|
|
// verifying the DOM links. This check can take quite a long time
|
|
// to execute.
|
|
//
|
|
// XFLM_ALLOW_LIMITED_MODE. This flag instructs the check to allow
|
|
// the database to be opened in limited mode if the database key is
|
|
// wrapped in a password and the password we pass is incorrect
|
|
// (or non-existent).
|
|
IF_DbInfo ** ppDbInfo,
|
|
// [IN] Pointer to a DB_INFO structure which is used to store
|
|
// statistics collected during the database check.
|
|
IF_DbCheckStatus * pDbCheckStatus
|
|
// [IN] Status interface. Functions in this interface are called
|
|
// periodically to iform the calling application of the progress
|
|
// being made. This allows the application to monitor and/or display
|
|
// the progress of the database check. NULL may be passed as the
|
|
// value of this parameter if the callback feature is not needed.
|
|
)
|
|
{
|
|
RCODE rc = NE_XFLM_OK;
|
|
F_DbCheck * pCheckObj = NULL;
|
|
|
|
if ((pCheckObj = f_new F_DbCheck) == NULL)
|
|
{
|
|
rc = RC_SET( NE_XFLM_MEM);
|
|
goto Exit;
|
|
}
|
|
|
|
rc = pCheckObj->dbCheck( pszDbFileName, pszDataDir, pszRflDir, pszPassword,
|
|
uiFlags, ppDbInfo, pDbCheckStatus);
|
|
|
|
Exit:
|
|
|
|
if (pCheckObj)
|
|
{
|
|
pCheckObj->Release();
|
|
}
|
|
return( rc);
|
|
}
|
|
|