Files
mars-flaim/flaim/src/flverify.cpp

3262 lines
68 KiB
C++

//-------------------------------------------------------------------------
// Desc: Verify database structure.
// Tabs: 3
//
// Copyright (c) 1991-2006 Novell, Inc. All Rights Reserved.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of version 2 of the GNU General Public
// License as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, contact Novell, Inc.
//
// To contact Novell about this file by physical or electronic mail,
// you may find current contact information at www.novell.com
//
// $Id: flverify.cpp 12266 2006-01-19 14:45:33 -0700 (Thu, 19 Jan 2006) dsanders $
//-------------------------------------------------------------------------
#include "flaimsys.h"
extern FLMBYTE gnDaysInMonth[];
extern FLMBYTE SENLenArray[];
extern FLMBYTE flm_c60_max[];
FSTATIC eCorruptionType flmVerifyBlobField(
FLMBYTE * pBlobHdr,
FLMUINT uiBlobHdrLen);
FSTATIC eCorruptionType flmVerifyTextKey(
IFD * pIfd,
FLMBOOL bIxAsia,
FLMBOOL bIxArab,
FLMBYTE * pKey,
FLMUINT uiKeyLen,
FLMUINT * puiOffsetRV,
FLMUINT * puiCollateCountRV);
FSTATIC eCorruptionType flmVerifyNumberKey(
FLMBYTE * pKey,
FLMUINT uiKeyLen,
FLMUINT * puiOffset);
FSTATIC FLMBOOL flmGetSEN(
FLMBYTE * pTmpElmRec,
FLMUINT * puiDrnRV,
FLMUINT * puiNumBytesRV);
/****************************************************************************
Desc: Verifies that a WordPerfect character is a legal character.
****************************************************************************/
eCorruptionType flmVerifyWPChar(
FLMUINT uiCharSet,
FLMUINT uiChar)
{
if (uiCharSet < NCHSETS)
{
if (uiChar >= (FLMUINT) flm_c60_max[uiCharSet])
{
return (FLM_BAD_CHAR);
}
}
else if ((uiCharSet >= ACHSMIN) && (uiCharSet < ACHSETS))
{
if (uiChar > ACHCMAX)
{
return (FLM_BAD_ASIAN_CHAR);
}
}
else
{
return (FLM_BAD_CHAR_SET);
}
return (FLM_NO_CORRUPTION);
}
/****************************************************************************
Desc: This routine verifies a text field. It makes sure that all of the
characters, formatting codes, etc. are valid.
****************************************************************************/
eCorruptionType flmVerifyTextField(
FLMBYTE * pText,
FLMUINT uiTextLen)
{
FLMUINT uiChar1;
FLMUINT uiBytesProcessed;
FLMUINT uiObjType;
FLMUINT uiObjLen;
FLMUINT uiLength;
FLMUINT uiCharSet;
FLMUINT uiChar;
eCorruptionType eCorruptionCode;
if (!uiTextLen)
{
return (FLM_NO_CORRUPTION);
}
// Parse through the data, verifying that it is consistent with the
// internal TEXT format.
uiBytesProcessed = 0;
while (uiBytesProcessed < uiTextLen)
{
// Determine what we are pointing at
uiChar1 = (FLMUINT) * pText;
uiObjType = (FLMUINT) (flmTextObjType( uiChar1));
switch (uiObjType)
{
case ASCII_CHAR_CODE:
{
uiObjLen = 1;
// Before testing anything else, make sure that we are not going
// to access something beyond the end of the buffer.
if (uiBytesProcessed + uiObjLen > uiTextLen)
{
return (FLM_BAD_TEXT_FIELD);
}
// There should NEVER be a character less than 32.
if (uiChar1 < 32)
{
return (FLM_BAD_CHAR);
}
break;
}
case CHAR_SET_CODE:
{
uiObjLen = 2;
// Before testing anything else, make sure that we are not going
// to access something beyond the end of the buffer.
if (uiBytesProcessed + uiObjLen > uiTextLen)
{
return (FLM_BAD_TEXT_FIELD);
}
// If the character set is zero, it had better be a valid
// character.
uiChar = *(pText + 1);
if ((uiCharSet = (FLMUINT) (uiChar1 & (~uiObjType))) == 0)
{
if ((uiChar < 32) || (uiChar > 127))
{
return (FLM_BAD_CHAR);
}
}
else
{
// Make sure we have a valid WordPerfect character.
if ((eCorruptionCode =
flmVerifyWPChar( uiCharSet, uiChar)) != FLM_NO_CORRUPTION)
{
return (eCorruptionCode);
}
}
break;
}
case WHITE_SPACE_CODE:
{
uiObjLen = 1;
// Before testing anything else, make sure that we are not going
// to access something beyond the end of the buffer.
if (uiBytesProcessed + uiObjLen > uiTextLen)
{
return (FLM_BAD_TEXT_FIELD);
}
break;
}
case UNICODE_CODE:
{
uiObjLen = 3;
if (uiBytesProcessed + uiObjLen > uiTextLen)
{
return (FLM_BAD_TEXT_FIELD);
}
// Anything is valid except 0xFFFF or 0xFFFE.
break;
}
case UNK_GT_255_CODE:
case UNK_LE_255_CODE:
{
if (uiObjType == UNK_GT_255_CODE)
{
uiObjLen = 1 + sizeof(FLMUINT16);
// Before testing anything else, make sure that we are not
// going to access something beyond the end of the buffer.
if (uiBytesProcessed + uiObjLen > uiTextLen)
{
return (FLM_BAD_TEXT_FIELD);
}
uiLength = (FLMUINT) FB2UW( pText + 1);
}
else
{
uiObjLen = 2;
// Before testing anything else, make sure that we are not
// going to access something beyond the end of the buffer.
if (uiBytesProcessed + uiObjLen > uiTextLen)
{
return (FLM_BAD_TEXT_FIELD);
}
uiLength = (FLMUINT) * (pText + 1);
}
if (uiLength > 65535 - uiObjLen)
{
return (FLM_BAD_TEXT_FIELD);
}
uiObjLen += uiLength;
// Before testing anything else, make sure that we are not going
// to access something beyond the end of the buffer.
if (uiBytesProcessed + uiObjLen > uiTextLen)
{
return (FLM_BAD_TEXT_FIELD);
}
// Make sure it is one of our valid types.
if (((uiChar1 & (~uiObjType)) != WP60_TYPE) &&
((uiChar1 & (~uiObjType)) != NATIVE_TYPE))
{
return (FLM_BAD_TEXT_FIELD);
}
break;
}
case UNK_EQ_1_CODE:
{
uiObjLen = 2;
// Before testing anything else, make sure that we are not going
// to access something beyond the end of the buffer.
if (uiBytesProcessed + uiObjLen > uiTextLen)
{
return (FLM_BAD_TEXT_FIELD);
}
// Make sure it is one of our valid types.
if (((uiChar1 & (~uiObjType)) != WP60_TYPE) &&
((uiChar1 & (~uiObjType)) != NATIVE_TYPE))
{
return (FLM_BAD_TEXT_FIELD);
}
break;
}
case EXT_CHAR_CODE:
{
uiObjLen = 3;
// Before testing anything else, make sure that we are not going
// to access something beyond the end of the buffer.
if (uiBytesProcessed + uiObjLen > uiTextLen)
{
return (FLM_BAD_TEXT_FIELD);
}
// If the character set is zero, the character had better be
// between 32 and 127.
uiChar = (FLMUINT) (*(pText + 2));
if ((uiCharSet = (FLMUINT) (*(pText + 1))) == 0)
{
if ((uiChar < 32) || (uiChar > 127))
{
return (FLM_BAD_CHAR);
}
}
else
{
// Make sure we have a valid WordPerfect character.
if ((eCorruptionCode =
flmVerifyWPChar( uiCharSet, uiChar)) != FLM_NO_CORRUPTION)
{
return (eCorruptionCode);
}
}
break;
}
case OEM_CODE:
{
uiObjLen = 2;
// Before testing anything else, make sure that we are not going
// to access something beyond the end of the buffer.
if (uiBytesProcessed + uiObjLen > uiTextLen)
{
return (FLM_BAD_TEXT_FIELD);
}
// OEM characters must be > 127.
if (*(pText + 1) <= 127)
{
return (FLM_BAD_CHAR);
}
break;
}
default:
{
// These codes should NEVER HAPPEN.
return (FLM_BAD_TEXT_FIELD);
}
}
pText += uiObjLen;
uiBytesProcessed += uiObjLen;
}
return (FLM_NO_CORRUPTION);
}
/****************************************************************************
Desc: This routine verifies a number field.
****************************************************************************/
eCorruptionType flmVerifyNumberField(
FLMBYTE * pNumber,
FLMUINT uiNumberLen)
{
FLMUINT uiChar;
FLMBOOL bFirstNibble;
FLMUINT uiNibbleCount;
FLMBOOL bHitExponent = FALSE;
FLMBOOL bRealNumberFlag = FALSE;
if (!uiNumberLen)
{
return (FLM_NO_CORRUPTION);
}
bFirstNibble = TRUE;
uiNibbleCount = 0;
for (;;)
{
// Determine what we are pointing at
uiChar = (FLMUINT) (*pNumber);
if (bFirstNibble)
{
uiChar >>= 4;
uiChar &= 0x0F;
bFirstNibble = FALSE;
}
else
{
uiChar &= 0x0F;
pNumber++;
bFirstNibble = TRUE;
}
uiNibbleCount++;
switch (uiChar)
{
case 0x0A:
{
// Periods are currently NOT supported.
return (FLM_BAD_NUMBER_FIELD);
}
case 0x0B:
{
// The minus had better be the very FIRST character except E-xF.
if (uiNibbleCount > 1 && !bHitExponent)
{
return (FLM_BAD_NUMBER_FIELD);
}
break;
}
case 0x0C:
case 0x0D:
{
return (FLM_BAD_NUMBER_FIELD);
}
case 0x0E:
{
// 'E' should be in the first or second position, but the format
// has the ability to change at a later time.
if (bRealNumberFlag)
{
return (FLM_BAD_NUMBER_FIELD);
}
bRealNumberFlag = TRUE;
bHitExponent = TRUE;
break;
}
case 0x0F:
{
if (bHitExponent)
{
bHitExponent = FALSE;
break;
}
// If we didn't end right on the last byte, we have a problem.
if ((uiNibbleCount + 1) / 2 < uiNumberLen)
{
return (FLM_BAD_NUMBER_FIELD);
}
else
{
return (FLM_NO_CORRUPTION);
}
}
default:
{
break;
}
}
// If we are at the last byte, but have not encountered a 0x0F we
// have a corrupted number.
if (uiNibbleCount / 2 == uiNumberLen)
{
return (FLM_BAD_NUMBER_FIELD);
}
// Numbers greater than 11 digits not yet supported.
if (!bRealNumberFlag && (uiNibbleCount > 11))
{
return (FLM_BAD_NUMBER_FIELD);
}
}
}
/****************************************************************************
Desc:
****************************************************************************/
FSTATIC eCorruptionType flmVerifyBlobField(
FLMBYTE * pBlobHdr,
FLMUINT uiBlobHdrLen)
{
FLMUINT uiSubType;
FLMUINT uiPathLen;
FLMUINT uiCharLen = 1;
FLMBYTE * pPath;
#define BLOB_ALIGNMENT_FUDGE 8
if (!uiBlobHdrLen)
{
return (FLM_NO_CORRUPTION);
}
// Definitions taken from fblob.cpp
#define BLOB_H_VERSION_LEN_POS 0
#define BLOB_CODE_VERSION 28
#define BLOB_H_STORAGE_TYPE_POS 1
#define BLOB_H_FLAGS_POS 2
#define BLOB_H_TYPE_POS 4
// Type of DATA or 0 if unknown
#define BLOB_H_FUTURE2 6
#define BLOB_H_RAW_SIZE_POS 8
#define BLOB_H_STORAGE_SIZE_POS 12
#define BLOB_H_MATCH_STAMP_POS 16
#define BLOB_MATCH_STAMP_SIZE 8
#define BLOB_H_RIGHT_KEY_POS 24
// Non-portable Reference BLOB Field Layout
#define BLOB_R_CHARSET_POS 28
#define BLOB_R_STRLENGTH_POS 29
#define BLOB_R_PATH_POS 30
// Must be at least as big as the smallest header.
if (pBlobHdr[BLOB_H_VERSION_LEN_POS] != BLOB_CODE_VERSION)
{
return (FLM_BAD_BLOB_FIELD);
}
uiSubType = (FLMUINT) (pBlobHdr[BLOB_H_STORAGE_TYPE_POS] & 0x0F);
if (uiSubType == BLOB_REFERENCE_TYPE)
{
if (uiBlobHdrLen < BLOB_R_PATH_POS)
{
return (FLM_BAD_BLOB_FIELD);
}
if (pBlobHdr[BLOB_R_CHARSET_POS] == 1) // ANSI
{
uiPathLen = pBlobHdr[BLOB_R_STRLENGTH_POS];
}
else if (pBlobHdr[BLOB_R_CHARSET_POS] == 2) // UNICODE
{
uiPathLen = (FLMUINT) (pBlobHdr[BLOB_R_STRLENGTH_POS] * 2);
uiCharLen = 2;
}
else
{
return (FLM_BAD_BLOB_FIELD);
}
// uiPathLen will include the NULL byte(s).
if (uiBlobHdrLen < BLOB_R_PATH_POS + uiPathLen)
{
return (FLM_BAD_BLOB_FIELD);
}
pPath = pBlobHdr + BLOB_R_PATH_POS;
}
else
{
return (FLM_BAD_BLOB_FIELD);
}
// Verify FILENAME that no characters are less than 0x20 and zero
// terminates. uiPathLen includes the NULL byte/word so pre-decrement.
// Zero or one path length is invalid. EXTERNAL and REFERENCE BLOBS
// ONLY!
if (uiPathLen <= 1)
{
return (FLM_BAD_BLOB_FIELD);
}
for (; --uiPathLen;)
{
if (uiCharLen == 1)
{
if (*pPath++ < 0x20)
{
return (FLM_BAD_BLOB_FIELD);
}
}
else
{
if (FB2UW( pPath) < 0x20)
{
return (FLM_BAD_BLOB_FIELD);
}
pPath += 2;
}
}
if (uiCharLen == 1)
{
if (*pPath++ != 0)
{
return (FLM_BAD_BLOB_FIELD);
}
}
else
{
if (FB2UW( pPath) != 0)
{
return (FLM_BAD_BLOB_FIELD);
}
pPath += 2;
}
return (FLM_NO_CORRUPTION);
}
/****************************************************************************
Desc:
****************************************************************************/
eCorruptionType flmVerifyField(
FLMBYTE* pField,
FLMUINT uiFieldLen,
FLMUINT uiFieldType)
{
if (((uiFieldLen) && (!pField)) || ((!uiFieldLen) && (pField)))
{
return (FLM_BAD_FIELD_PTR);
}
switch (uiFieldType)
{
case FLM_TEXT_TYPE:
{
return (flmVerifyTextField( pField, uiFieldLen));
}
case FLM_NUMBER_TYPE:
{
return (flmVerifyNumberField( pField, uiFieldLen));
}
case FLM_BINARY_TYPE:
{
break;
}
case FLM_BLOB_TYPE:
{
return (flmVerifyBlobField( pField, uiFieldLen));
}
case FLM_CONTEXT_TYPE:
{
// Length must be zero or four in context fields.
if ((uiFieldLen != 0) && (uiFieldLen != 4))
{
return (FLM_BAD_CONTEXT_FIELD);
}
break;
}
default:
{
// Unknown type.
return (FLM_BAD_FIELD_TYPE);
}
}
return (FLM_NO_CORRUPTION);
}
/****************************************************************************
Desc: Verifies a text field within a key - compound or single.
****************************************************************************/
FSTATIC eCorruptionType flmVerifyTextKey(
IFD * pIfd, // Pointer to index field definition structure.
FLMBOOL bIxAsia, // Is this one of the ASIAN indexes?
FLMBOOL bIxArab, // Is this one of the ARAB indexes?
FLMBYTE * pKey, // Pointer to beginning of key.
FLMUINT uiKeyLen, // Byte length of entire key.
FLMUINT * puiOffsetRV, // Offset in key where text key begins.
// Returns offset where text key ends.
FLMUINT* puiCollateCountRV) // Returns the number of collation characters.
{
FLMUINT uiCntJ = *puiOffsetRV;
FLMUINT uiTextCollateCount = 0;
FLMUINT uiArabCollateCount = 0;
FLMUINT uiCntK;
FLMUINT uiBitsToSkip;
FLMUINT uiBit;
FLMUINT uiChar;
FLMUINT uiNextChar = 0;
FLMUINT uiCaseBits;
// See how many collated values there are - go until we hit 0x07, 0x02,
// 0x04, 0x05, 0x06, 0x01, or end of key.
while (uiCntJ < uiKeyLen)
{
uiNextChar = (FLMUINT) pKey[uiCntJ];
if (bIxAsia)
{
if (uiCntJ + 1 >= uiKeyLen)
{
return (FLM_BAD_KEY_LEN);
}
uiNextChar <<= 8;
uiNextChar += pKey[uiCntJ + 1];
}
if ((uiNextChar == END_COMPOUND_MARKER) ||
(uiNextChar == COMPOUND_MARKER) ||
(uiNextChar == COLL_FIRST_SUBSTRING) ||
(uiNextChar == (COLL_MARKER | SC_SUB_COL)) ||
(uiNextChar == (COLL_MARKER | SC_MIXED)) ||
((uiNextChar == (COLL_MARKER | SC_LOWER)) && (!bIxAsia)) ||
((uiNextChar == (COLL_MARKER | SC_UPPER)) && (!bIxAsia)) ||
(uiNextChar == COLL_TRUNCATED))
{
// These checks must be in order
if (uiCntJ > *puiOffsetRV && uiNextChar == COLL_FIRST_SUBSTRING)
{
uiCntJ++;
if (bIxAsia)
{
uiCntJ++;
}
continue;
}
if (uiNextChar == COLL_TRUNCATED)
{
uiCntJ++;
if (bIxAsia)
{
uiCntJ++;
}
// Get character after COLL_TRUNCATED, if any
if (uiCntJ < uiKeyLen)
{
uiNextChar = (FLMUINT) pKey[uiCntJ];
if (bIxAsia)
{
if (uiCntJ + 1 >= uiKeyLen)
{
return (FLM_BAD_KEY_LEN);
}
uiNextChar <<= 8;
uiNextChar += pKey[uiCntJ + 1];
}
}
}
break;
}
// It is impossible to have a collated value that is less than 0x20
// (space).
if (uiNextChar < 0x20)
{
return (FLM_BAD_TEXT_KEY_COLL_CHAR);
}
uiTextCollateCount++;
uiCntJ++;
if (bIxAsia)
{
uiCntJ++;
}
}
// See if we got sub-collation values.
if ((uiCntJ < uiKeyLen) && (uiNextChar == 0x07))
{
uiCntJ++;
if (bIxAsia)
{
uiCntJ++;
}
if (uiCntJ >= uiKeyLen)
{
return (FLM_BAD_KEY_LEN);
}
uiCntK = 0;
uiBit = 0x80;
uiChar = pKey[uiCntJ];
for (;;)
{
FLMUINT uiBitCode;
// Determine what the code is.
uiBitCode = 0;
while (uiChar & uiBit)
{
uiBitCode |= 0x01;
uiBitCode <<= 1;
// Get the next bit.
uiBit >>= 1;
// See if we need to get the next byte.
if (!uiBit)
{
uiCntJ++;
if (uiCntJ >= uiKeyLen)
{
return (FLM_BAD_KEY_LEN);
}
uiChar = (FLMUINT) pKey[uiCntJ];
uiBit = 0x80;
}
}
// The uiBitCode value tells whether or not there is a collating
// value and what type it is.
switch (uiBitCode)
{
// Code of zero means there is no collating value.
case 0:
{
if ((!uiTextCollateCount) && (bIxArab))
{
uiBitsToSkip = 0;
}
else
{
uiBitsToSkip = 1;
}
uiCntK++;
break;
}
// Code of 0x02 means that the sub-collation value is the next
// five bits.
case 0x02:
{
uiBitsToSkip = 6;
uiCntK++;
break;
}
// Code of 0x06 means that next two bytes contains sub-collation.
// Code of 0x0E should only happen in Arabic, means that next two
// bytes contains sub-collation, but should not be counted.
case 0x0E:
{
if (!bIxArab)
{
return (FLM_BAD_KEY_LEN);
}
uiArabCollateCount++;
// Fall through to 0x06 case.
}
case 0x06:
{
uiBitsToSkip = 0;
uiCntJ += 3;
if (uiCntJ > uiKeyLen)
{
return (FLM_BAD_KEY_LEN);
}
uiBit = 0x80;
uiChar = (FLMUINT) pKey[uiCntJ];
// If Arabic, the sub-collation should not be counted.
if (uiBitCode != 0x0E)
{
uiCntK++;
}
break;
}
// Unicode character that did not convert to a WP char. The
// actual unicode character follows the 1E.
case 0x1E:
{
uiBitsToSkip = 0;
uiCntJ += 3;
if (uiCntJ > uiKeyLen)
{
return (FLM_BAD_KEY_LEN);
}
uiBit = 0x80;
uiChar = (FLMUINT) pKey[uiCntJ];
uiCntK++;
// The spec for Unicode has an additional case bit. Added Oct
// 98. Note, the extra case bit is only set if we are not an
// asian index.
if (!bIxAsia)
{
uiArabCollateCount++;
}
break;
}
default:
{
return (FLM_BAD_KEY_LEN);
}
}
// Skip the required number of bits.
while (uiBitsToSkip)
{
uiBit >>= 1;
uiBitsToSkip--;
if ((!uiBit) && ((uiBitsToSkip) || (uiCntK < uiTextCollateCount)))
{
uiCntJ++;
if (uiCntJ == uiKeyLen)
{
return (FLM_BAD_KEY_LEN);
}
uiChar = (FLMUINT) pKey[uiCntJ];
uiBit = 0x80;
}
}
if (uiCntK >= uiTextCollateCount)
{
if (!bIxArab)
{
break;
}
// Arab languages have one more terminating bit. See if we
// need to go to the next byte.
if (!uiBit)
{
// Terminating bit is in next byte.
uiCntJ++;
if (uiCntJ >= uiKeyLen)
{
return (FLM_BAD_KEY_LEN);
}
uiChar = (FLMUINT) pKey[uiCntJ];
uiBit = 0x80;
}
// If the next bit isn't set, we are done. However, we must
// skip the current character
if (!(uiChar & uiBit))
{
uiCntJ++;
if (uiCntJ >= uiKeyLen)
{
return (FLM_BAD_KEY_LEN);
}
uiChar = (FLMUINT) pKey[uiCntJ];
uiBit = 0x80;
break;
}
}
}
// If NOT at beginning of byte, increment uiCntJ to skip rest of
// byte.
if (uiBit != 0x80)
{
uiCntJ++;
}
}
// Do lower/upper case bits -- unless a POST index.
if (pIfd->uiFlags & IFD_POST)
{
*puiCollateCountRV = uiTextCollateCount + uiArabCollateCount;
}
else
{
// If there are no UPPER/LOWER indicators, we have a problem.
if (uiCntJ >= uiKeyLen)
{
return (FLM_BAD_KEY_LEN);
}
uiNextChar = (FLMUINT) pKey[uiCntJ];
uiCntJ++;
if (bIxAsia)
{
if (uiCntJ >= uiKeyLen)
{
return (FLM_BAD_KEY_LEN);
}
uiNextChar <<= 8;
uiNextChar += (FLMUINT) pKey[uiCntJ];
uiCntJ++;
}
switch (uiNextChar)
{
case COLL_FIRST_SUBSTRING:
{
break;
}
case COLL_TRUNCATED:
{
break;
}
case (COLL_MARKER | SC_LOWER):
case (COLL_MARKER | SC_UPPER):
{
if (bIxAsia)
{
return (FLM_BAD_TEXT_KEY_CASE_MARKER);
}
break;
}
case (COLL_MARKER | SC_MIXED):
{
uiCaseBits = uiTextCollateCount + uiArabCollateCount;
if (bIxAsia)
{
uiCaseBits <<= 1;
}
uiCntJ += ((uiCaseBits + 7) >> 3);
break;
}
default:
{
return (FLM_BAD_TEXT_KEY_CASE_MARKER);
}
}
}
if (uiCntJ > uiKeyLen)
{
return (FLM_BAD_KEY_LEN);
}
*puiOffsetRV = uiCntJ;
return (FLM_NO_CORRUPTION);
}
/****************************************************************************
Desc:
****************************************************************************/
FSTATIC eCorruptionType flmVerifyNumberKey(
FLMBYTE * pKey,
FLMUINT uiKeyLen,
FLMUINT * puiOffsetRV)
{
FLMUINT uiCntJ = *puiOffsetRV;
FLMUINT uiChar;
FLMUINT uiNumDigits;
FLMUINT uiNibble;
// Determine the number of digits.
uiChar = pKey[uiCntJ++];
uiNumDigits = (FLMUINT) (uiChar & 0x7F);
// If negative, the number of digits must be NOTed.
if (!(uiChar & 0x80))
{
uiNumDigits = (FLMUINT) ((~uiNumDigits) & 0x7F);
}
// Adjust the number of digits by -64 + 1.
uiNumDigits = (FLMUINT) (uiNumDigits - COLLATED_NUM_EXP_BIAS + 1);
// Process until we run out of digits or key or until we hit the
// compound marker or post marker.
while( uiNumDigits && (uiCntJ < uiKeyLen) && (pKey[uiCntJ] != 0x02) &&
(pKey[uiCntJ] != 0x01))
{
// Check the first nibble.
uiNibble = (FLMUINT) ((pKey[uiCntJ] >> 4) & 0x0F);
if ((uiNibble < 0x05) || (uiNibble > 0x0E))
{
return (FLM_BAD_NUMBER_KEY);
}
uiNumDigits--;
// Check the 2nd nibble. If we are out of digits it had better be
// 0x0F.
uiNibble = (FLMUINT) (pKey[uiCntJ] & 0x0F);
if (!uiNumDigits)
{
if (uiNibble != 0x0F)
{
return (FLM_BAD_NUMBER_KEY);
}
}
else
{
if ((uiNibble < 0x05) || (uiNibble > 0x0E))
{
return (FLM_BAD_NUMBER_KEY);
}
uiNumDigits--;
}
uiCntJ++;
}
// If we ran out of key before we processed all of the digits, we have
// an error.
if ((uiNumDigits) && (uiCntJ > uiKeyLen))
{
return (FLM_BAD_KEY_LEN);
}
*puiOffsetRV = uiCntJ;
return (FLM_NO_CORRUPTION);
}
/****************************************************************************
Desc: This routine verifies that a collated key conforms to the index it
belongs to.
****************************************************************************/
eCorruptionType flmVerifyKey(
FLMBYTE* pKey, // Key which is to be verified.
FLMUINT uiKeyLen, // Byte length of pKey.
FLMUINT uiIxLang, // Language for index.
IFD* pIfdArray, // List of fields in index.
FLMUINT uiNumIxFields) // Number of fields in pIfdArray.
{
IFD* pIfd = pIfdArray;
FLMUINT uiI;
FLMUINT uiJ;
#define MAX_IX_FIELDS 100
FLMUINT uiCollateCount[MAX_IX_FIELDS];
FLMUINT uiPostByteCount;
eCorruptionType eCorruptionCode;
FLMUINT uiTotalTextChars = 0;
FLMBOOL bIxAsia;
FLMBOOL bIxArab;
FLMBOOL bIxIsPost = FALSE;
FLMUINT uiTrueNumIxFields = uiNumIxFields;
FLMUINT uiMarkerChar;
FLMUINT uiCaseBits;
FLMUINT uiFieldType;
bIxAsia = (uiIxLang >= FIRST_DBCS_LANG && uiIxLang <= LAST_DBCS_LANG)
? TRUE
: FALSE;
bIxArab = (uiIxLang == AR_LANG || uiIxLang == FA_LANG ||
uiIxLang == HE_LANG || uiIxLang == UR_LANG)
? TRUE
: FALSE;
// If we weren't able to get the IX information from the dictionary
// just return FLM_NO_CORRUPTION.
if ((!pIfdArray) || (!uiNumIxFields))
{
return (FLM_NO_CORRUPTION);
}
// See if we have a POST index.
for (uiI = 0; uiI < uiNumIxFields; uiI++)
{
if (pIfdArray[uiI].uiFlags & IFD_POST)
{
bIxIsPost = TRUE;
break;
}
}
// If it is not a compound index, set the uiNumIxFields to one - we
// only need to examine one field, because they are all the same type.
if (!(pIfdArray->uiFlags & IFD_COMPOUND))
{
if (bIxIsPost)
{
return (FLM_BAD_IX_DEF);
}
uiNumIxFields = 1;
}
uiJ = 0;
uiI = 0;
if (uiNumIxFields > MAX_IX_FIELDS)
{
return (FLM_BAD_IX_DEF);
}
while (uiJ < uiKeyLen)
{
uiCollateCount[uiI] = 0;
// First see if the component has anything in it. If we hit the
// compound marker right away, the component piece is empty.
uiMarkerChar = (FLMUINT) pKey[uiJ];
if ((bIxAsia) &&
(IFD_GET_FIELD_TYPE( pIfd) == FLM_TEXT_TYPE) &&
(!(pIfd->uiFlags & IFD_CONTEXT)))
{
if (uiJ + 1 == uiKeyLen)
{
return (FLM_BAD_KEY_LEN);
}
uiMarkerChar <<= 8;
uiMarkerChar += (FLMUINT) pKey[uiJ + 1];
}
if (uiMarkerChar == 2)
{
;
}
else if (uiMarkerChar == 1)
{
// If we hit a 1, it should be the beginning of upper/lower case
// bits for a POST index.
break;
}
else if (uiMarkerChar == NULL_KEY_MARKER)
{
uiJ++;
if ((bIxAsia) &&
(IFD_GET_FIELD_TYPE( pIfd) == FLM_TEXT_TYPE) &&
(!(pIfd->uiFlags & IFD_CONTEXT)))
{
uiJ++;
}
}
// See if indexing context. If not, use the field's type to
// determine how the key is formatted.
else if (pIfd->uiFlags & IFD_CONTEXT)
{
// If indexing context, the first byte of the key is a 0x1E
// followed by the two byte tag number in high/low order.
if (pKey[uiJ] != 0x1E)
{
return (FLM_BAD_CONTEXT_KEY);
}
// Verify that the tag portion of the key matches the field
// number.
if (pIfd->uiFlags & IFD_COMPOUND)
{
if (flmBigEndianToUINT16( &pKey[uiJ + 1]) != pIfd->uiFldNum)
{
return (FLM_BAD_CONTEXT_KEY);
}
}
else
{
FLMUINT uiH;
IFD * pTmpIfd;
// If it is NOT a compound index, be sure to check each field
// in the index to see if it matches that field number - it
// could be a multi-field index.
for (uiH = 0, pTmpIfd = pIfdArray;
uiH < uiTrueNumIxFields;
uiH++, pTmpIfd++)
{
if (flmBigEndianToUINT16( &pKey[uiJ + 1]) == pTmpIfd->uiFldNum)
{
break;
}
}
// If it did not match any of the fields, return an error.
if (uiH == uiTrueNumIxFields)
{
return (FLM_BAD_CONTEXT_KEY);
}
}
uiJ += 3;
}
else
{
switch (uiFieldType = (FLMUINT) (IFD_GET_FIELD_TYPE( pIfd)))
{
case FLM_TEXT_TYPE:
{
if ((eCorruptionCode = flmVerifyTextKey( pIfd, bIxAsia, bIxArab,
pKey, uiKeyLen, &uiJ,
&uiCollateCount[uiI])) != FLM_NO_CORRUPTION)
{
return (eCorruptionCode);
}
uiTotalTextChars += uiCollateCount[uiI];
break;
}
case FLM_NUMBER_TYPE:
{
if ((eCorruptionCode = flmVerifyNumberKey( pKey,
uiKeyLen, &uiJ)) != FLM_NO_CORRUPTION)
{
return (eCorruptionCode);
}
break;
}
case FLM_BINARY_TYPE:
{
while( (uiJ < uiKeyLen) && (pKey[uiJ] != 0x02) &&
(pKey[uiJ] != 0x01) && (pKey[uiJ] != COLL_TRUNCATED))
{
if ((pKey[uiJ] < 0x20) || (pKey[uiJ] > 0x2F))
{
return (FLM_BAD_BINARY_KEY);
}
uiJ++;
}
if (uiJ < uiKeyLen && pKey[uiJ] == COLL_TRUNCATED)
{
uiJ++;
}
break;
}
case FLM_CONTEXT_TYPE:
{
if (pKey[uiJ] != 0x1F)
{
return (FLM_BAD_DRN_KEY);
}
uiJ += 5;
break;
}
default:
{
return (FLM_BAD_KEY_FIELD_TYPE);
}
}
}
// See if there is another field.
while( ((pIfd->uiFlags & IFD_LAST) == 0) &&
(pIfd->uiCompoundPos == (pIfd + 1)->uiCompoundPos))
{
pIfd++;
}
// pIfd will point to the LAST ifd with the same compound position.
// pIfd increments below AFTER the compound marker is added.
uiI++;
if (uiI == uiNumIxFields)
{
break;
}
// If this is not the last field, make sure we are pointing to a
// compound marker.
if (uiJ >= uiKeyLen)
{
return (FLM_BAD_KEY_COMPOUND_MARKER);
}
uiMarkerChar = (FLMUINT) pKey[uiJ];
uiJ++;
if (bIxAsia &&
IFD_GET_FIELD_TYPE( pIfd) == FLM_TEXT_TYPE &&
!(pIfd->uiFlags & IFD_CONTEXT))
{
if (uiJ >= uiKeyLen)
{
return (FLM_BAD_KEY_COMPOUND_MARKER);
}
uiMarkerChar <<= 8;
uiMarkerChar += (FLMUINT) pKey[uiJ];
uiJ++;
}
if (uiMarkerChar != 2)
{
return (FLM_BAD_KEY_COMPOUND_MARKER);
}
pIfd++;
}
// If we didn't get through all of the fields in the loop above make
// sure the remaining fields are all optional. No need to check for
// alternate keys.
while (uiI < uiNumIxFields)
{
uiCollateCount[uiI] = 0;
uiI++;
pIfd++;
}
// If we have a POST index, get the lower/upper case bits for each text
// field.
if ((bIxIsPost) && (uiTotalTextChars))
{
// If it is not a compound key, we have an error.
if (uiNumIxFields == 1)
{
return (FLM_BAD_IX_DEF);
}
// If we did not hit a 0x01, we have an error.
if (uiJ >= uiKeyLen)
{
return (FLM_BAD_KEY_POST_MARKER);
}
uiMarkerChar = (FLMUINT) pKey[uiJ];
uiJ++;
// If the last field is text, and we are in an Asian index we need
// two bytes for the post marker.
if (bIxAsia)
{
pIfd = &pIfdArray[uiNumIxFields - 1];
if ((IFD_GET_FIELD_TYPE( pIfd) == FLM_TEXT_TYPE) &&
(!(pIfd->uiFlags & IFD_CONTEXT)))
{
if (uiJ >= uiKeyLen)
{
return (FLM_BAD_KEY_POST_MARKER);
}
uiMarkerChar <<= 8;
uiMarkerChar += (FLMUINT) pKey[uiJ];
uiJ++;
}
}
if (uiMarkerChar != 1)
{
return (FLM_BAD_KEY_POST_MARKER);
}
// Go through all of the fields looking for TEXT fields.
uiPostByteCount = 0;
uiI = 0;
pIfd = pIfdArray;
while ((uiI < uiNumIxFields) && (uiJ < uiKeyLen))
{
if (uiCollateCount[uiI])
{
FLMINT uiTempCnt;
uiMarkerChar = pKey[uiJ];
uiJ++;
uiPostByteCount++;
if (bIxAsia)
{
if (uiJ >= uiKeyLen)
{
return (FLM_BAD_KEY_POST_BYTE_COUNT);
}
uiMarkerChar <<= 8;
uiMarkerChar += (FLMUINT) pKey[uiJ];
uiJ++;
uiPostByteCount++;
}
switch (uiMarkerChar)
{
case 4:
case 6:
{
if (bIxAsia)
{
return (FLM_BAD_TEXT_KEY_CASE_MARKER);
}
break;
}
case 5:
{
uiCaseBits = uiCollateCount[uiI];
if (bIxAsia)
{
uiCaseBits <<= 1;
}
uiTempCnt = (FLMUINT) ((uiCaseBits + 7) >> 3);
uiJ += uiTempCnt;
uiPostByteCount += uiTempCnt;
break;
}
default:
{
return (FLM_BAD_TEXT_KEY_CASE_MARKER);
}
}
}
while( ((pIfd->uiFlags & IFD_LAST) == 0) &&
(pIfd->uiCompoundPos == (pIfd + 1)->uiCompoundPos))
{
uiI++; // Compares against uiNumIxFields.
pIfd++;
}
uiI++;
pIfd++;
}
// Account for the post byte count
if ((uiJ >= uiKeyLen) || (uiPostByteCount != (FLMUINT) pKey[uiJ]))
{
return (FLM_BAD_KEY_POST_BYTE_COUNT);
}
uiJ++;
}
// We must end exactly right on the end of the key.
return (((uiJ != uiKeyLen) || (uiI != uiNumIxFields))
? FLM_BAD_KEY_LEN
: FLM_NO_CORRUPTION);
}
/****************************************************************************
Desc: Verifies a block's header and sets up the STATE_INFO structure to
verify the rest of the block.
****************************************************************************/
eCorruptionType flmVerifyBlockHeader(
STATE_INFO * pStateInfo,
BLOCK_INFO * pBlockInfoRV,
FLMUINT uiBlockSize,
FLMUINT uiExpNextBlkAddr,
FLMUINT uiExpPrevBlkAddr,
FLMBOOL bCheckEOF,
FLMBOOL bCheckFullBlkAddr)
{
FLMBYTE * pBlk = pStateInfo->pBlk;
if (pBlockInfoRV)
{
pBlockInfoRV->uiBlockCount++;
}
pStateInfo->uiNextBlkAddr = (FLMUINT) FB2UD( &pBlk[BH_NEXT_BLK]);
if ((pStateInfo->uiEndOfBlock =
(FLMUINT) FB2UW( &pBlk[BH_ELM_END])) < BH_OVHD)
{
pStateInfo->uiEndOfBlock = BH_OVHD;
return (FLM_BAD_BLK_HDR_BLK_END);
}
else if (pStateInfo->uiEndOfBlock > uiBlockSize)
{
pStateInfo->uiEndOfBlock = uiBlockSize;
return (FLM_BAD_BLK_HDR_BLK_END);
}
else if (pBlockInfoRV)
{
pBlockInfoRV->ui64BytesUsed +=
(FLMUINT64) (pStateInfo->uiEndOfBlock - BH_OVHD);
}
pStateInfo->uiElmOffset = BH_OVHD;
// Verify the block address.
if (bCheckFullBlkAddr)
{
if (GET_BH_ADDR( pBlk) != pStateInfo->uiBlkAddress)
{
return (FLM_BAD_BLK_HDR_ADDR);
}
}
else
{
if ((GET_BH_ADDR( pBlk) & 0xFFFFFF00) !=
(pStateInfo->uiBlkAddress & 0xFFFFFF00))
{
return (FLM_BAD_BLK_HDR_ADDR);
}
}
// Verify that block address is below the logical EOF
if (bCheckEOF && pStateInfo->pDb)
{
if (!FSAddrIsBelow( pStateInfo->uiBlkAddress,
pStateInfo->pDb->LogHdr.uiLogicalEOF))
{
return (FLM_BAD_FILE_SIZE);
}
}
// Verify the block type.
if ((pStateInfo->uiBlkType != 0xFF) &&
(pStateInfo->uiBlkType != (FLMUINT) BH_GET_TYPE( pBlk)))
{
return (FLM_BAD_BLK_HDR_TYPE);
}
// Verify the block level.
if ((pStateInfo->uiLevel != 0xFF) && (pStateInfo->uiLevel != pBlk[BH_LEVEL]))
{
return (FLM_BAD_BLK_HDR_LEVEL);
}
// Verify the previous block address. If uiExpPrevBlkAddr is zero, we
// do not know what the previous address should be, so we don't verify.
if ((uiExpPrevBlkAddr) &&
(uiExpPrevBlkAddr != (FLMUINT) FB2UD( &pBlk[BH_PREV_BLK])))
{
return (FLM_BAD_BLK_HDR_PREV);
}
// Verify the next block address. If uiExpNextBlkAddr is zero, we do
// not know what the next address should be, se we don't verify.
if ((uiExpNextBlkAddr) && (uiExpNextBlkAddr != pStateInfo->uiNextBlkAddr))
{
return (FLM_BAD_BLK_HDR_NEXT);
}
// Verify that if it is a root block, the root bit flags is set, or if
// it is NOT a root block, that the root bit flag is NOT set.
if (pStateInfo->pLogicalFile)
{
if (pStateInfo->uiLevel != 0xFF)
{
FLMBOOL bShouldBeRootBlk;
bShouldBeRootBlk = (pStateInfo->uiLevel ==
pStateInfo->pLogicalFile->pLfStats->uiNumLevels - 1)
? TRUE
: FALSE;
if (((bShouldBeRootBlk) && (!BH_IS_ROOT_BLK( pBlk))) ||
((!bShouldBeRootBlk) && (BH_IS_ROOT_BLK( pBlk))))
{
return (FLM_BAD_BLK_HDR_ROOT_BIT);
}
}
// Verify the logical file number - if any.
if (pStateInfo->pLogicalFile->pLFile->uiLfNum !=
(FLMUINT) FB2UW( &pBlk[BH_LOG_FILE_NUM]))
{
return (FLM_BAD_BLK_HDR_LF_NUM);
}
}
return (FLM_NO_CORRUPTION);
}
/****************************************************************************
Desc:
****************************************************************************/
FLMINT flmCompareKeys(
FLMBYTE * pBuf1,
FLMUINT uiBuf1Len,
FLMBYTE * pBuf2,
FLMUINT uiBuf2Len)
{
FLMINT iStatus;
if (!uiBuf1Len)
{
iStatus = (!uiBuf2Len) ? 0 : -1;
}
else if (!uiBuf2Len)
{
iStatus = 1;
}
else if (uiBuf1Len < uiBuf2Len)
{
if ((iStatus = f_memcmp( pBuf1, pBuf2, uiBuf1Len)) == 0)
{
iStatus = -1;
}
}
else if (uiBuf1Len > uiBuf2Len)
{
if ((iStatus = f_memcmp( pBuf1, pBuf2, uiBuf2Len)) == 0)
{
iStatus = 1;
}
}
else
{
iStatus = f_memcmp( pBuf1, pBuf2, uiBuf1Len);
}
return (iStatus);
}
/****************************************************************************
Desc: Verify a index or data element in a b-tree and set pStateInfo.
****************************************************************************/
eCorruptionType flmVerifyElement(
STATE_INFO * pStateInfo,
FLMUINT uiFlags)
{
FLMUINT uiEndOfBlock = pStateInfo->uiEndOfBlock;
FLMUINT uiOffset = pStateInfo->uiElmOffset;
FLMUINT uiBlkType = pStateInfo->uiBlkType;
FLMBYTE * pElm;
FLMUINT uiElmLen;
FLMBYTE * pElmKey;
FLMUINT uiElmKeyLen;
FLMUINT uiElmPKCLen;
FLMINT iCmpStatus = 0;
FLMBYTE * pCurKey = pStateInfo->pCurKey;
FLMUINT uiCurKeyLen = pStateInfo->uiCurKeyLen;
FLMBOOL bLfIsContainer = FALSE;
FLMBOOL bLfIsIndex = FALSE;
FLMUINT uiLfType = LF_INVALID;
IXD * pIxd = NULL;
IFD * pIfd = NULL;
if (pStateInfo->pLogicalFile)
{
uiLfType = pStateInfo->pLogicalFile->pLFile->uiLfType;
bLfIsContainer = (uiLfType == LF_CONTAINER) ? TRUE : FALSE;
bLfIsIndex = (uiLfType == LF_INDEX) ? TRUE : FALSE;
pIxd = pStateInfo->pLogicalFile->pIxd;
pIfd = pStateInfo->pLogicalFile->pIfd;
}
// Get a pointer to the element to work with.
pElm = pStateInfo->pElm = &pStateInfo->pBlk[uiOffset];
// Get the element length.
if (uiBlkType == BHT_LEAF)
{
if (uiOffset + BBE_KEY > uiEndOfBlock)
{
return (FLM_BAD_ELM_LEN);
}
uiElmLen = pStateInfo->uiElmLen = (FLMUINT) (BBE_LEN( pElm));
pElmKey = pStateInfo->pElmKey = &pElm[BBE_KEY];
pStateInfo->pElmRec = BBE_REC_PTR( pElm);
pStateInfo->uiElmRecLen = BBE_GET_RL( pElm);
pStateInfo->uiElmRecOffset = 0;
// Get the element key length and previous key count (PKC).
uiElmKeyLen = pStateInfo->uiElmKeyLen = (FLMUINT) (BBE_GET_KL( pElm));
uiElmPKCLen = pStateInfo->uiElmPKCLen = (FLMUINT) (BBE_GET_PKC( pElm));
}
else if (uiBlkType == BHT_NON_LEAF_DATA)
{
if (uiOffset + pStateInfo->uiElmOvhd > uiEndOfBlock)
{
return (FLM_BAD_ELM_LEN);
}
uiElmLen = pStateInfo->uiElmLen = BNE_DATA_OVHD;
pElmKey = pStateInfo->pElmKey = pElm;
uiElmKeyLen = 4;
uiElmPKCLen = 0;
}
else
{
if (uiBlkType == BHT_NON_LEAF_COUNTS)
{
pStateInfo->uiChildCount = FB2UD( pElm + BNE_CHILD_COUNT);
}
if (uiOffset + pStateInfo->uiElmOvhd > uiEndOfBlock)
{
return (FLM_BAD_ELM_LEN);
}
uiElmLen = pStateInfo->uiElmLen = (FLMUINT) BBE_GET_KL( pElm) +
pStateInfo->uiElmOvhd +
(BNE_IS_DOMAIN( pElm) ? BNE_DOMAIN_LEN : 0);
pElmKey = pStateInfo->pElmKey = &pElm[pStateInfo->uiElmOvhd];
// Get the element key length and previous key count (PKC).
uiElmKeyLen = pStateInfo->uiElmKeyLen = (FLMUINT) (BBE_GET_KL( pElm));
uiElmPKCLen = pStateInfo->uiElmPKCLen = (FLMUINT) (BBE_GET_PKC( pElm));
}
// Make sure the element length is within the block boundary.
if (uiOffset + uiElmLen > uiEndOfBlock)
{
return (FLM_BAD_ELM_LEN);
}
// Get the record number from the element, if any.
pStateInfo->uiElmDrn = 0;
if ((bLfIsContainer) && (uiElmKeyLen + uiElmPKCLen == 4))
{
FLMBYTE ucRecBuff[4];
if (uiElmPKCLen)
{
if ((uiCurKeyLen >= uiElmPKCLen) && (pStateInfo->bValidKey))
{
f_memcpy( ucRecBuff, pCurKey, uiElmPKCLen);
}
else
{
f_memset( ucRecBuff, 0, uiElmPKCLen);
}
}
if (uiElmKeyLen)
{
f_memcpy( &ucRecBuff[uiElmPKCLen], pElmKey, uiElmKeyLen);
}
pStateInfo->uiElmDrn = flmBigEndianToUINT32( ucRecBuff);
if (pStateInfo->uiElmDrn == DRN_LAST_MARKER && uiBlkType == BHT_LEAF)
{
FLMUINT uiTempDrn;
// Verify that the marker value is > the last DRN value.
uiTempDrn = (FLMUINT) FB2UD( &pElmKey[uiElmKeyLen]);
if (uiTempDrn <= pStateInfo->uiLastElmDrn)
{
return (FLM_BAD_LAST_DRN);
}
pStateInfo->uiLastElmDrn = uiTempDrn;
}
else
{
pStateInfo->uiLastElmDrn = pStateInfo->uiElmDrn;
}
}
// Verify the first/last flags if it is a leaf element.
if (uiBlkType == BHT_LEAF)
{
FLMUINT uiFirstFlag = (FLMUINT) (BBE_IS_FIRST( pElm));
FLMUINT uiPrevLastFlag = pStateInfo->uiElmLastFlag;
// Verify the first element flag
pStateInfo->uiElmLastFlag = (FLMUINT) (BBE_IS_LAST( pElm));
if (uiPrevLastFlag != 0xFF)
{
if ((uiPrevLastFlag) && (!uiFirstFlag))
{
return (FLM_BAD_FIRST_ELM_FLAG);
}
else if ((!uiPrevLastFlag) && (uiFirstFlag))
{
return (FLM_BAD_LAST_ELM_FLAG);
}
}
}
// If we are on the last element, verify that we are indeed. If we are,
// set the key length to zero.
if ((uiElmLen == pStateInfo->uiElmOvhd) &&
(uiElmLen + uiOffset == uiEndOfBlock) &&
(pStateInfo->uiNextBlkAddr == BT_END))
{
pStateInfo->bValidKey = TRUE;
pStateInfo->uiCurKeyLen = uiCurKeyLen = 0;
}
// If the length in a leaf element is BBE_LEM_LEN and it is not the
// last element, we have an error.
else if ((uiBlkType == BHT_LEAF) && (uiElmLen == BBE_LEM_LEN))
{
return (FLM_BAD_LEM);
}
// If this is the last element in the block, and this is the last block
// in the chain, this had better be the LEM.
else if ((uiOffset + uiElmLen == uiEndOfBlock) &&
(pStateInfo->uiNextBlkAddr == BT_END))
{
return (FLM_BAD_LEM);
}
// Verify that the key is OK. The key must pass three tests in order
// for it to be OK. First, the total key length must not exceed the
// maximum key size. Second, if there is a previous key count, it must
// not exceed the size of the previous key. Third, the new key must be
// greater than or equal to the previous key -- all keys in the block
// must be in ascending order. The third part is tested by comparing
// only the part of the key which is going to change - the part pointed
// to by pElmKey. We already know that the part of the key represented
// in the previous key count is equal - by definition, so there is no
// need to test it.
else if ((uiElmKeyLen + uiElmPKCLen > MAX_KEY_SIZ) ||
(bLfIsContainer && (uiElmKeyLen + uiElmPKCLen != 4) &&
(uiBlkType != BHT_NON_LEAF_DATA)))
{
pStateInfo->bValidKey = FALSE;
return (FLM_BAD_ELM_KEY_SIZE);
}
else if ((uiOffset == BH_OVHD && uiElmPKCLen) ||
(uiElmPKCLen > uiCurKeyLen && uiCurKeyLen && pStateInfo->bValidKey))
{
pStateInfo->bValidKey = FALSE;
return (FLM_BAD_ELM_PKC_LEN);
}
else
{
eCorruptionType eKeyCorruptionCode = FLM_NO_CORRUPTION;
// NOTE: The reason we are saving the error code into
// eKeyCorruptionCode instead of returning when we detect it is
// because we want to save the new key into pStateInfo->pCurKey
// before we return. However, there are some checks which we must
// make before saving the new key.
if ((pStateInfo->bValidKey) || (uiElmPKCLen == 0))
{
if (pStateInfo->bValidKey)
{
if (uiBlkType == BHT_NON_LEAF_DATA)
{
iCmpStatus = flmCompareKeys( pElmKey, uiElmKeyLen, pCurKey,
DIN_KEY_SIZ);
}
else
{
iCmpStatus = flmCompareKeys( pElmKey, uiElmKeyLen,
&pCurKey[uiElmPKCLen],
uiCurKeyLen - uiElmPKCLen);
}
if (iCmpStatus < 0)
{
eKeyCorruptionCode = FLM_BAD_ELM_KEY_ORDER;
}
// Check the key compression to see if it is good.
else if ((uiBlkType != BHT_NON_LEAF_DATA) &&
(uiOffset > BH_OVHD) &&
(uiElmKeyLen > 0) &&
(uiElmPKCLen < BBE_PKC_MAX) &&
(uiElmPKCLen < uiCurKeyLen) &&
(pElmKey[0] == pCurKey[uiElmPKCLen]))
{
eKeyCorruptionCode = FLM_BAD_ELM_KEY_COMPRESS;
}
}
// The keys had better be equal if it is a continuation element.
// Otherwise, they had better be different.
if (!pStateInfo->bValidKey)
{
pStateInfo->ui64KeyCount++;
pStateInfo->ui64KeyRefs = 0;
}
else if (iCmpStatus != 0)
{
pStateInfo->ui64KeyCount++;
pStateInfo->ui64KeyRefs = 0;
// If this is a continuation element in a leaf block, the key
// should be the same as the last key.
if ((uiBlkType == BHT_LEAF) && (!BBE_IS_FIRST( pElm)))
{
eKeyCorruptionCode = FLM_BAD_CONT_ELM_KEY;
}
}
else
{
if (pIxd)
{
if (((uiBlkType == BHT_LEAF) && (BBE_IS_FIRST( pElm))) ||
((uiBlkType != BHT_LEAF) && (pIxd->uiFlags & IXD_UNIQUE)))
{
eKeyCorruptionCode = FLM_NON_UNIQUE_FIRST_ELM_KEY;
}
}
}
// Save the new key.
if (uiBlkType != BHT_NON_LEAF_DATA)
{
pStateInfo->uiCurKeyLen = uiCurKeyLen = uiElmPKCLen + uiElmKeyLen;
f_memcpy( &pCurKey[uiElmPKCLen], pElmKey, uiElmKeyLen);
}
else
{
pStateInfo->uiCurKeyLen = uiCurKeyLen = DIN_KEY_SIZ;
*(FLMUINT32*) pCurKey = *(FLMUINT32*) pElmKey;
}
pStateInfo->bValidKey = TRUE;
// Perform some additional checks on the key if an index key.
if (eKeyCorruptionCode == FLM_NO_CORRUPTION &&
bLfIsIndex &&
pIxd &&
(uiFlags & FLM_CHK_FIELDS))
{
if (!pIxd->uiContainerNum)
{
FLMUINT uiContainerPartLen = getIxContainerPartLen( pIxd);
RCODE tmpRc;
LFILE * pTmpLFile;
if (uiCurKeyLen <= uiContainerPartLen)
{
eKeyCorruptionCode = FLM_BAD_KEY_LEN;
goto Bad_Key;
}
if (pStateInfo->pDb)
{
if (RC_BAD( tmpRc = fdictGetContainer( pStateInfo->pDb->pDict,
getContainerFromKey( pCurKey, uiCurKeyLen),
&pTmpLFile)))
{
eKeyCorruptionCode = FLM_BAD_CONTAINER_IN_KEY;
goto Bad_Key;
}
}
uiCurKeyLen -= uiContainerPartLen;
}
eKeyCorruptionCode = flmVerifyKey( pCurKey, uiCurKeyLen,
pIxd->uiLanguage, pIfd,
pIxd->uiNumFlds);
}
}
if (eKeyCorruptionCode != FLM_NO_CORRUPTION)
{
Bad_Key:
pStateInfo->bValidKey = FALSE;
return (eKeyCorruptionCode);
}
}
return (FLM_NO_CORRUPTION);
}
/****************************************************************************
Desc:
****************************************************************************/
eCorruptionType flmVerifyElmFOP(
STATE_INFO * pStateInfo)
{
eCorruptionType eCorruptionCode = FLM_NO_CORRUPTION;
FLMBYTE * pElmRec = pStateInfo->pElmRec;
FLMUINT uiElmRecOffset = pStateInfo->uiElmRecOffset;
FLMUINT uiElmRecLen = pStateInfo->uiElmRecLen;
FLMBYTE * pField;
FLMBYTE * pTmpFld;
FLMBOOL bDictField;
FLMBOOL bFOPIsField;
FLMBYTE * pElm = pStateInfo->pElm;
FLMUINT uiFOPDataLen;
FLMUINT uiFldOverhead;
FLMUINT uiBaseFldFlags;
FLMUINT uiLfNumber;
FLMUINT uiMaxDictFieldNum = FLM_LAST_DICT_FIELD_NUM;
uiLfNumber = (pStateInfo->pLogicalFile)
? pStateInfo->pLogicalFile->pLFile->uiLfNum
: (FLMUINT) 0;
if ((BBE_IS_FIRST( pElm)) && (uiElmRecOffset == 0))
{
pStateInfo->uiFOPType = 0xFF;
pStateInfo->uiFieldLen = 0;
pStateInfo->uiFieldProcessedLen = 0;
pStateInfo->uiFieldType = 0xFF;
pStateInfo->uiFieldNum = 0;
pStateInfo->uiFieldLevel = 0;
pStateInfo->uiJumpLevel = 0;
pStateInfo->pFOPData = NULL;
pStateInfo->uiFOPDataLen = 0;
pStateInfo->pValue = NULL;
pStateInfo->uiEncId = 0;
pStateInfo->uiEncFieldLen = 0;
pStateInfo->pData = NULL;
pStateInfo->pvField = NULL;
if (pStateInfo->pRecord)
{
pStateInfo->pRecord->clear();
}
pStateInfo->bElmRecOK = TRUE;
}
// If the state is goofed up, just give back the reset of the element.
// Don't try to parse it.
if (!pStateInfo->bElmRecOK)
{
pStateInfo->uiFOPType = FLM_FOP_CONT_DATA;
pStateInfo->uiFieldLen = uiElmRecLen - uiElmRecOffset;
pStateInfo->uiFieldProcessedLen = 0;
}
// If we are at the first of an element's record and we have a half
// processed field, return that first.
else if (uiElmRecOffset == 0 &&
(pStateInfo->uiFieldProcessedLen < (pStateInfo->uiEncId
? pStateInfo->uiEncFieldLen
: pStateInfo->uiFieldLen)))
{
// If this is a FIRST element, we have a problem.
if (BBE_IS_FIRST( pElm))
{
pStateInfo->bElmRecOK = FALSE;
eCorruptionCode = FLM_BAD_FIRST_ELM_FLAG;
goto Exit;
}
else
{
pStateInfo->uiFOPType = FLM_FOP_CONT_DATA;
}
}
else if (pStateInfo->uiElmDrn == DRN_LAST_MARKER)
{
pStateInfo->uiFOPType = FLM_FOP_NEXT_DRN;
if (uiElmRecLen != 4)
{
eCorruptionCode = FLM_BAD_LAST_DRN;
goto Exit;
}
}
else
{
bDictField = FALSE;
bFOPIsField = TRUE;
pStateInfo->uiFieldType = 0xFF;
pStateInfo->uiFieldNum = 0;
pStateInfo->uiFieldLen = 0;
pStateInfo->uiEncId = 0;
pStateInfo->uiEncFieldLen = 0;
pField = &pElmRec[uiElmRecOffset];
// Test for STANDARD field -- must be defined in dictionary.
if (FOP_IS_STANDARD( pField))
{
pStateInfo->uiFOPType = FLM_FOP_STANDARD;
bDictField = TRUE;
uiFldOverhead = 2;
if (uiElmRecOffset + uiFldOverhead > uiElmRecLen)
{
pStateInfo->bElmRecOK = FALSE;
eCorruptionCode = FLM_BAD_ELM_FLD_OVERHEAD;
goto Exit;
}
pStateInfo->uiFieldLen = (FLMUINT) (FSTA_FLD_LEN( pField));
pStateInfo->uiFieldNum = FSTA_FLD_NUM( pField);
// See if field is a child or sibling of previous field.
if (FSTA_LEVEL( pField))
{
pStateInfo->uiFieldLevel++;
}
}
// Test for OPEN type -- must also be defined in dictionary.
else if (FOP_IS_OPEN( pField))
{
pStateInfo->uiFOPType = FLM_FOP_OPEN;
bDictField = TRUE;
// See if the field is a child or sibling of the previous field.
if (FOPE_LEVEL( pField))
{
pStateInfo->uiFieldLevel++;
}
// See if field number is one or two bytes.
pTmpFld = pField;
uiFldOverhead = 0;
uiBaseFldFlags = (FLMUINT) (FOP_GET_FLD_FLAGS( pTmpFld));
pTmpFld++;
if (FOP_2BYTE_FLDNUM( uiBaseFldFlags))
{
uiFldOverhead += 3;
if (uiElmRecOffset + uiFldOverhead > uiElmRecLen)
{
pStateInfo->bElmRecOK = FALSE;
eCorruptionCode = FLM_BAD_ELM_FLD_OVERHEAD;
goto Exit;
}
pStateInfo->uiFieldNum = (FLMUINT) FB2UW( pTmpFld);
pTmpFld += 2;
}
else
{
uiFldOverhead += 2;
if (uiElmRecOffset + uiFldOverhead > uiElmRecLen)
{
pStateInfo->bElmRecOK = FALSE;
eCorruptionCode = FLM_BAD_ELM_FLD_OVERHEAD;
goto Exit;
}
pStateInfo->uiFieldNum = (FLMUINT) (*pTmpFld);
pTmpFld++;
}
// Determine the field length
if (FOP_2BYTE_FLDLEN( uiBaseFldFlags))
{
uiFldOverhead += 2;
if (uiElmRecOffset + uiFldOverhead > uiElmRecLen)
{
pStateInfo->bElmRecOK = FALSE;
eCorruptionCode = FLM_BAD_ELM_FLD_OVERHEAD;
goto Exit;
}
pStateInfo->uiFieldLen = (FLMUINT) FB2UW( pTmpFld);
}
else
{
uiFldOverhead++;
if (uiElmRecOffset + uiFldOverhead > uiElmRecLen)
{
pStateInfo->bElmRecOK = FALSE;
eCorruptionCode = FLM_BAD_ELM_FLD_OVERHEAD;
goto Exit;
}
pStateInfo->uiFieldLen = (FLMUINT) (*pTmpFld);
}
}
// Test for UNREGISTERED fields -- not in dictionary.
else if (FOP_IS_TAGGED( pField))
{
pStateInfo->uiFOPType = FLM_FOP_TAGGED;
// See if the field is a child or sibling of the previous field.
if (FTAG_LEVEL( pField))
{
pStateInfo->uiFieldLevel++;
}
pTmpFld = pField;
uiBaseFldFlags = (FLMUINT) (FOP_GET_FLD_FLAGS( pTmpFld));
pTmpFld++;
uiFldOverhead = 1;
// Get the field type.
pStateInfo->uiFieldType = (FLMUINT) FTAG_GET_FLD_TYPE( *pTmpFld);
pTmpFld++;
uiFldOverhead++;
// See if field number is one or two bytes.
if (FOP_2BYTE_FLDNUM( uiBaseFldFlags))
{
uiFldOverhead += 2;
if (uiElmRecOffset + uiFldOverhead > uiElmRecLen)
{
pStateInfo->bElmRecOK = FALSE;
eCorruptionCode = FLM_BAD_ELM_FLD_OVERHEAD;
goto Exit;
}
pStateInfo->uiFieldNum = (FLMUINT) FB2UW( pTmpFld);
pTmpFld += 2;
}
else
{
uiFldOverhead++;
if (uiElmRecOffset + uiFldOverhead > uiElmRecLen)
{
pStateInfo->bElmRecOK = FALSE;
eCorruptionCode = FLM_BAD_ELM_FLD_OVERHEAD;
goto Exit;
}
pStateInfo->uiFieldNum = (FLMUINT) (*pTmpFld);
pTmpFld++;
}
// Toggle high bit to get true field number. FOP_TAGGED is now
// used for more than just UNREGISTERED fields.
if (pStateInfo->uiFieldNum & 0x8000)
{
pStateInfo->uiFieldNum &= (FLMUINT) 0x7FFF;
}
else
{
pStateInfo->uiFieldNum |= (FLMUINT) 0x8000;
}
// See if the field length is one or two bytes
if (FOP_2BYTE_FLDLEN( uiBaseFldFlags))
{
uiFldOverhead += 2;
if (uiElmRecOffset + uiFldOverhead > uiElmRecLen)
{
pStateInfo->bElmRecOK = FALSE;
eCorruptionCode = FLM_BAD_ELM_FLD_OVERHEAD;
goto Exit;
}
pStateInfo->uiFieldLen = (FLMUINT) FB2UW( pTmpFld);
}
else
{
uiFldOverhead++;
if (uiElmRecOffset + uiFldOverhead > uiElmRecLen)
{
pStateInfo->bElmRecOK = FALSE;
eCorruptionCode = FLM_BAD_ELM_FLD_OVERHEAD;
goto Exit;
}
pStateInfo->uiFieldLen = (FLMUINT) (*pTmpFld);
}
}
// Test for a field with NO value -- must be in dictionary
else if (FOP_IS_NO_VALUE( pField))
{
pStateInfo->uiFOPType = FLM_FOP_NO_VALUE;
bDictField = TRUE;
pStateInfo->uiFieldLen = 0;
// See if the field is a child or sibling of previous field
if (FNOV_LEVEL( pField))
{
pStateInfo->uiFieldLevel++;
}
// See if field number is one or two bytes
pTmpFld = pField + 1;
uiBaseFldFlags = (FLMUINT) (FOP_GET_FLD_FLAGS( pField));
if (FOP_2BYTE_FLDNUM( uiBaseFldFlags))
{
uiFldOverhead = 3;
if (uiElmRecOffset + uiFldOverhead > uiElmRecLen)
{
pStateInfo->bElmRecOK = FALSE;
eCorruptionCode = FLM_BAD_ELM_FLD_OVERHEAD;
goto Exit;
}
pStateInfo->uiFieldNum = (FLMUINT) FB2UW( pTmpFld);
pTmpFld += 2;
}
else
{
uiFldOverhead = 2;
if (uiElmRecOffset + uiFldOverhead > uiElmRecLen)
{
pStateInfo->bElmRecOK = FALSE;
eCorruptionCode = FLM_BAD_ELM_FLD_OVERHEAD;
goto Exit;
}
pStateInfo->uiFieldNum = (FLMUINT) (*pTmpFld);
pTmpFld++;
}
}
// Test for the code which just resets the field level
else if (FOP_IS_SET_LEVEL( pField))
{
FLMUINT uiTempLevel;
pStateInfo->uiFOPType = FLM_FOP_JUMP_LEVEL;
uiFldOverhead = 1;
if (uiElmRecOffset + uiFldOverhead > uiElmRecLen)
{
pStateInfo->bElmRecOK = FALSE;
eCorruptionCode = FLM_BAD_ELM_FLD_OVERHEAD;
goto Exit;
}
bFOPIsField = FALSE;
pStateInfo->uiFieldNum = 0;
pStateInfo->uiFieldLen = 0;
pStateInfo->uiFieldType = 0xFF;
// Jumping back better not cause us to go below level one
uiTempLevel = (FLMUINT) (FSLEV_GET( pField));
pStateInfo->uiJumpLevel = uiTempLevel;
if (pStateInfo->uiFieldLevel <= uiTempLevel)
{
pStateInfo->bElmRecOK = FALSE;
eCorruptionCode = FLM_BAD_ELM_FLD_LEVEL_JUMP;
goto Exit;
}
pStateInfo->uiFieldLevel -= uiTempLevel;
}
else if (FOP_IS_RECORD_INFO( pField))
{
bFOPIsField = FALSE;
pStateInfo->uiFOPType = FLM_FOP_REC_INFO;
uiFldOverhead = 1;
pStateInfo->uiFieldNum = 0;
pStateInfo->uiFieldType = 0xFF;
pTmpFld = pField + 1;
uiBaseFldFlags = (FLMUINT) (FOP_GET_FLD_FLAGS( pField));
if (FOP_2BYTE_FLDLEN( uiBaseFldFlags))
{
uiFldOverhead += 2;
if (uiElmRecOffset + uiFldOverhead > uiElmRecLen)
{
pStateInfo->bElmRecOK = FALSE;
eCorruptionCode = FLM_BAD_ELM_FLD_OVERHEAD;
goto Exit;
}
pStateInfo->uiFieldLen = *pTmpFld++;
pStateInfo->uiFieldLen += ((FLMUINT) * pTmpFld++) << 8;
}
else
{
uiFldOverhead++;
if (uiElmRecOffset + uiFldOverhead > uiElmRecLen)
{
pStateInfo->bElmRecOK = FALSE;
eCorruptionCode = FLM_BAD_ELM_FLD_OVERHEAD;
goto Exit;
}
pStateInfo->uiFieldLen = *pTmpFld++;
}
}
else if (FOP_IS_ENCRYPTED( pField))
{
FLMBOOL bTagSz;
FLMBOOL bLenSz;
FLMBOOL bENumSz;
FLMBOOL bELenSz;
pStateInfo->uiFOPType = FLM_FOP_ENCRYPTED;
bFOPIsField = TRUE;
uiFldOverhead = 2;
if (uiElmRecOffset + uiFldOverhead > uiElmRecLen)
{
pStateInfo->bElmRecOK = FALSE;
eCorruptionCode = FLM_BAD_ELM_FLD_OVERHEAD;
goto Exit;
}
if (FENC_LEVEL( pField))
{
pStateInfo->uiFieldLevel++;
}
pStateInfo->uiFieldType = (FLMUINT) (FENC_FLD_TYPE( pField));
bTagSz = FENC_TAG_SZ( pField);
if (bTagSz)
{
uiFldOverhead += 2;
}
else
{
uiFldOverhead++;
}
bLenSz = FENC_LEN_SZ( pField);
if (bLenSz)
{
uiFldOverhead += 2;
}
else
{
uiFldOverhead++;
}
bENumSz = FENC_ETAG_SZ( pField);
if (bENumSz)
{
uiFldOverhead += 2;
}
else
{
uiFldOverhead++;
}
bELenSz = FENC_ELEN_SZ( pField);
if (bELenSz)
{
uiFldOverhead += 2;
}
else
{
uiFldOverhead++;
}
if (uiElmRecOffset + uiFldOverhead > uiElmRecLen)
{
pStateInfo->bElmRecOK = FALSE;
eCorruptionCode = FLM_BAD_ELM_FLD_OVERHEAD;
goto Exit;
}
pTmpFld = pField + 2;
pStateInfo->uiFieldNum = (FLMUINT) * pTmpFld++;
if (bTagSz)
{
pStateInfo->uiFieldNum += ((FLMUINT) * pTmpFld++) << 8;
}
pStateInfo->uiFieldLen = (FLMUINT) * pTmpFld++;
if (bLenSz)
{
pStateInfo->uiFieldLen += ((FLMUINT) * pTmpFld++) << 8;
}
pStateInfo->uiEncId = (FLMUINT) * pTmpFld++;
if (bENumSz)
{
pStateInfo->uiEncId += ((FLMUINT) * pTmpFld++) << 8;
}
pStateInfo->uiEncFieldLen = (FLMUINT) * pTmpFld++;
if (bELenSz)
{
pStateInfo->uiEncFieldLen += ((FLMUINT) * pTmpFld++) << 8;
}
}
else if (FOP_IS_LARGE( pField))
{
if( pStateInfo->uiVersionNum < FLM_FILE_FORMAT_VER_4_61)
{
pStateInfo->uiFOPType = FLM_FOP_BAD;
pStateInfo->bElmRecOK = FALSE;
eCorruptionCode = FLM_BAD_ELM_FLD_OVERHEAD;
goto Exit;
}
pStateInfo->uiFOPType = FLM_FOP_LARGE;
bFOPIsField = TRUE;
uiFldOverhead = 2;
if (uiElmRecOffset + uiFldOverhead > uiElmRecLen)
{
pStateInfo->bElmRecOK = FALSE;
eCorruptionCode = FLM_BAD_ELM_FLD_OVERHEAD;
goto Exit;
}
if (FLARGE_LEVEL( pField))
{
pStateInfo->uiFieldLevel++;
}
pStateInfo->uiFieldType = FLARGE_FLD_TYPE( pField);
pStateInfo->uiFieldNum = FLARGE_TAG_NUM( pField);
uiFldOverhead += 2;
pStateInfo->uiFieldLen = FLARGE_DATA_LEN( pField);
uiFldOverhead += 4;
if (FLARGE_ENCRYPTED( pField))
{
pStateInfo->uiEncId = FLARGE_ETAG_NUM( pField);
uiFldOverhead += 2;
pStateInfo->uiEncFieldLen = FLARGE_EDATA_LEN( pField);
uiFldOverhead += 4;
}
if (uiElmRecOffset + uiFldOverhead > uiElmRecLen)
{
pStateInfo->bElmRecOK = FALSE;
eCorruptionCode = FLM_BAD_ELM_FLD_OVERHEAD;
goto Exit;
}
}
else
{
// Anything else is a code we don't understand
pStateInfo->uiFOPType = FLM_FOP_BAD;
pStateInfo->bElmRecOK = FALSE;
eCorruptionCode = FLM_BAD_ELM_FLD_OVERHEAD;
goto Exit;
}
// If it is a field, get its type and make further checks
if (bFOPIsField)
{
FLMUINT uiFldNum = pStateInfo->uiFieldNum;
if (pStateInfo->pLogicalFile)
{
pStateInfo->pLogicalFile->pLfStats->ui64FldRefCount++;
}
// If it is a dictionary field, verify that it is indeed and get
// the field type from the dictionary.
if (bDictField)
{
// If the field number is a reserved dictionary tag, it must
// be a TEXT field. These are always stored in the FOP_OPEN
// format - hence, the bDictField flag will be TRUE.
if ((uiFldNum >= FLM_DICT_FIELD_NUMS) &&
(uiFldNum <= uiMaxDictFieldNum))
{
pStateInfo->uiFieldType = FLM_TEXT_TYPE;
}
// If we have no dictionary, set the field type to binary so
// that the field will pass every test.
else if (!pStateInfo->pDb)
{
pStateInfo->uiFieldType = FLM_BINARY_TYPE;
}
else
{
// If we can't find the field in the dictionary we have a
// corruption.
if (RC_BAD( fdictGetField( pStateInfo->pDb->pDict, uiFldNum,
&pStateInfo->uiFieldType, NULL, NULL)))
{
pStateInfo->bElmRecOK = FALSE;
pStateInfo->uiFieldType = FLM_BINARY_TYPE;
eCorruptionCode = FLM_BAD_ELM_FLD_NUM;
// Keep processing and fill out the rest of the state
// information. Even though we have a bad field number
// here, the caller may want to simply skip the field
// instead of aborting the entire element.
goto Keep_Processing_Field;
}
}
}
// Check the field type
switch (pStateInfo->uiFieldType)
{
case FLM_TEXT_TYPE:
case FLM_NUMBER_TYPE:
case FLM_BINARY_TYPE:
case FLM_BLOB_TYPE:
{
break;
}
case FLM_CONTEXT_TYPE:
{
if (pStateInfo->uiFieldLen != 0 && pStateInfo->uiFieldLen != 4)
{
pStateInfo->bElmRecOK = FALSE;
eCorruptionCode = FLM_BAD_ELM_FLD_LEN;
goto Exit;
}
break;
}
default:
{
pStateInfo->bElmRecOK = FALSE;
eCorruptionCode = FLM_BAD_ELM_FLD_TYPE;
goto Exit;
}
}
}
Keep_Processing_Field:
// At this point, it is possible for us to have a bad field number
// error, but we still want to set up pStateInfo so the caller can
// simply skip the field if desired.
uiElmRecOffset += uiFldOverhead;
pStateInfo->uiFieldProcessedLen = 0;
}
// Setup the state structure to point to the data
if (pStateInfo->uiEncId)
{
uiFOPDataLen = pStateInfo->uiEncFieldLen - pStateInfo->uiFieldProcessedLen;
}
else
{
uiFOPDataLen = pStateInfo->uiFieldLen - pStateInfo->uiFieldProcessedLen;
}
if (uiFOPDataLen > uiElmRecLen - uiElmRecOffset)
{
uiFOPDataLen = uiElmRecLen - uiElmRecOffset;
if (BBE_IS_LAST( pElm))
{
eCorruptionCode = FLM_BAD_ELM_FLD_LEN;
goto Exit;
}
}
pStateInfo->uiFieldProcessedLen += uiFOPDataLen;
pStateInfo->uiFOPDataLen = uiFOPDataLen;
pStateInfo->pFOPData = &pElmRec[uiElmRecOffset];
uiElmRecOffset += uiFOPDataLen;
pStateInfo->uiElmRecOffset = uiElmRecOffset;
Exit:
return (eCorruptionCode);
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE flmVerifyIXRefs(
STATE_INFO * pStateInfo,
IX_CHK_INFO * pIxChkInfo,
FLMUINT uiResetDrn,
eCorruptionType * peElmCorruptionCode)
{
FLMUINT uiElmRecOffset = pStateInfo->uiElmRecOffset;
FLMBYTE * pElm = pStateInfo->pElm;
FLMBYTE * pElmRec = pStateInfo->pElmRec;
FLMUINT uiElmRecLen = pStateInfo->uiElmRecLen;
FLMUINT uiLowestDrn;
FLMUINT uiDrn;
FLMUINT uiTmpNum;
FLMBYTE * pTmpElmRec;
FLMUINT uiNumBytes;
FLMUINT uiElmRefs = 0;
FLMBOOL bOneRun;
RCODE rc = FERR_OK;
*peElmCorruptionCode = FLM_NO_CORRUPTION;
if ((BBE_IS_FIRST( pElm)) && (uiElmRecOffset == 0))
{
pStateInfo->uiCurrIxRefDrn = 0;
pStateInfo->bElmRecOK = TRUE;
}
// Determine the element domain
uiLowestDrn = 0;
if (*pElmRec == 0xFC)
{
uiElmRecOffset++;
if (!flmGetSEN( &pElmRec[uiElmRecOffset], &uiLowestDrn, &uiNumBytes))
{
pStateInfo->bElmRecOK = FALSE;
*peElmCorruptionCode = FLM_BAD_ELM_DOMAIN_SEN;
goto Exit;
}
uiElmRecOffset += uiNumBytes;
uiLowestDrn <<= 8;
}
// Get the base DRN for the element
if (!flmGetSEN( &pElmRec[uiElmRecOffset], &uiDrn, &uiNumBytes))
{
pStateInfo->bElmRecOK = FALSE;
*peElmCorruptionCode = FLM_BAD_ELM_BASE_SEN;
goto Exit;
}
uiElmRecOffset += uiNumBytes;
// If this is the first element or the state info has not yet been
// set, set the pStateInfo.
if ((BBE_IS_FIRST( pElm)) || (!pStateInfo->uiCurrIxRefDrn))
{
pStateInfo->uiCurrIxRefDrn = uiDrn;
}
else if (uiDrn >= pStateInfo->uiCurrIxRefDrn)
{
// If the DRN's are not descending, we have a problem
pStateInfo->bElmRecOK = FALSE;
*peElmCorruptionCode = FLM_BAD_ELM_IX_REF;
goto Exit;
}
uiElmRefs++;
if (uiDrn <= uiResetDrn)
{
uiResetDrn = 0;
}
if (pIxChkInfo != NULL && !uiResetDrn)
{
pStateInfo->uiCurrIxRefDrn = uiDrn;
if ((RC_BAD( rc = chkVerifyIXRSet( pStateInfo, pIxChkInfo, uiDrn))) ||
(pIxChkInfo->pDbInfo->bReposition))
{
goto Exit;
}
}
while (uiElmRecOffset < uiElmRecLen)
{
pTmpElmRec = &pElmRec[uiElmRecOffset];
// See if we have a one run
bOneRun = FALSE;
if (*pTmpElmRec >= 0xF0 && *pTmpElmRec <= 0xF7)
{
uiTmpNum = (FLMUINT) ((*pTmpElmRec & 0x0F) + 2);
uiElmRefs += uiTmpNum;
uiNumBytes = 1;
bOneRun = TRUE;
}
else if (*pTmpElmRec == 0xF8)
{
if (!flmGetSEN( pTmpElmRec + 1, &uiTmpNum, &uiNumBytes))
{
pStateInfo->bElmRecOK = FALSE;
*peElmCorruptionCode = FLM_BAD_ELM_ONE_RUN_SEN;
goto Exit;
}
uiNumBytes++;
uiElmRefs += uiTmpNum;
bOneRun = TRUE;
}
else
{
// We have a delta
if (!flmGetSEN( pTmpElmRec, &uiTmpNum, &uiNumBytes))
{
pStateInfo->bElmRecOK = FALSE;
*peElmCorruptionCode = FLM_BAD_ELM_DELTA_SEN;
goto Exit;
}
uiElmRefs++;
}
// The new drn must not take us zero or negative
if (uiDrn <= uiTmpNum)
{
pStateInfo->bElmRecOK = FALSE;
*peElmCorruptionCode = FLM_BAD_ELM_IX_REF;
goto Exit;
}
if (bOneRun)
{
while (uiTmpNum > 0)
{
uiTmpNum--;
uiDrn--;
if (uiDrn <= uiResetDrn)
{
uiResetDrn = 0;
}
pStateInfo->uiCurrIxRefDrn = uiDrn;
if (pIxChkInfo != NULL && !uiResetDrn)
{
// Verify that the key+ref is in the result set
if ((RC_BAD( rc = chkVerifyIXRSet( pStateInfo, pIxChkInfo,
uiDrn))) || pIxChkInfo->pDbInfo->bReposition)
{
goto Exit;
}
}
}
}
else
{
uiDrn -= uiTmpNum;
if (uiDrn <= uiResetDrn)
{
uiResetDrn = 0;
}
pStateInfo->uiCurrIxRefDrn = uiDrn;
if (pIxChkInfo != NULL && !uiResetDrn)
{
// Verify that the key+ref is in the result set
if ((RC_BAD( rc = chkVerifyIXRSet( pStateInfo, pIxChkInfo,
uiDrn))) || pIxChkInfo->pDbInfo->bReposition)
{
goto Exit;
}
}
}
uiElmRecOffset += uiNumBytes;
}
// If we didn't end up right at the end of the element, we have
// corruption.
if (uiElmRecOffset != uiElmRecLen)
{
pStateInfo->bElmRecOK = FALSE;
*peElmCorruptionCode = FLM_BAD_ELM_END;
goto Exit;
}
// The last drn must not be lower than the lowest Drn for the element
if (uiDrn < uiLowestDrn)
{
pStateInfo->bElmRecOK = FALSE;
*peElmCorruptionCode = FLM_BAD_ELM_DOMAIN;
goto Exit;
}
pStateInfo->uiCurrIxRefDrn = uiDrn;
pStateInfo->uiElmRecOffset = uiElmRecOffset;
if (pStateInfo->pLogicalFile)
{
pStateInfo->pLogicalFile->pLfStats->ui64FldRefCount += (FLMUINT64) uiElmRefs;
}
pStateInfo->ui64KeyRefs += (FLMUINT64) uiElmRefs;
// Check for a non-unique reference in a unique key
if ((pStateInfo->pLogicalFile) &&
(pStateInfo->pLogicalFile->pIxd->uiFlags & IXD_UNIQUE) &&
(pStateInfo->ui64KeyRefs > (FLMUINT64) 1))
{
pStateInfo->bElmRecOK = FALSE;
if (pIxChkInfo != NULL)
{
// Give the application the option of deleting the record to
// resolve the corruption.
if (RC_BAD( rc = chkResolveNonUniqueKey( pStateInfo, pIxChkInfo,
pStateInfo->pLogicalFile->pLFile->uiLfNum,
pStateInfo->pCurKey, pStateInfo->uiCurKeyLen,
pStateInfo->uiCurrIxRefDrn)))
{
if (rc == FERR_OLD_VIEW)
{
// Set uiCurrIxRefDrn to zero so that the check will
// re-position to the first reference of the current key.
pStateInfo->uiCurrIxRefDrn = 0;
}
goto Exit;
}
}
*peElmCorruptionCode = FLM_NON_UNIQUE_ELM_KEY_REF;
}
Exit:
return (rc);
}
/****************************************************************************
Desc:
****************************************************************************/
FLMBOOL flmGetSEN(
FLMBYTE * pTmpElmRec,
FLMUINT * puiDrnRV,
FLMUINT * puiNumBytesRV)
{
FLMUINT uiNumBytes;
FLMUINT uiDrn;
FLMUINT uiChar = (FLMUINT) *pTmpElmRec;
FLMUINT uiTempDrn;
pTmpElmRec++;
if (!(uiChar & 0x80))
{
uiNumBytes = 0;
uiDrn = (FLMUINT) uiChar;
}
else if ((uiChar & 0xC0) == 0x80)
{
uiNumBytes = 1;
uiDrn = (FLMUINT) (uiChar & 0x3F);
}
else if ((uiChar & 0xF0) == 0xC0)
{
uiNumBytes = 2;
uiDrn = (FLMUINT) (uiChar & 0x0F);
}
else if ((uiChar & 0xF0) == 0xD0)
{
uiNumBytes = 3;
uiDrn = (FLMUINT) (uiChar & 0x0F);
}
else if ((uiChar & 0xF0) == 0xE0)
{
uiNumBytes = 4;
uiDrn = (FLMUINT) (uiChar & 0x0F);
}
else
{
return (FALSE);
}
*puiNumBytesRV = uiNumBytes + 1;
while (uiNumBytes)
{
// Check for overflow
uiTempDrn = (FLMUINT) (*pTmpElmRec);
if (0xFFFFFFFF - uiDrn < (FLMUINT) 256 + uiTempDrn)
{
return (FALSE);
}
uiDrn <<= 8;
uiDrn += uiTempDrn;
pTmpElmRec++;
uiNumBytes--;
}
*puiDrnRV = uiDrn;
return (TRUE);
}
/****************************************************************************
Desc:
****************************************************************************/
void flmInitReadState(
STATE_INFO * pStateInfo,
FLMBOOL * pbStateInitialized,
FLMUINT uiVersionNum,
FDB * pDb, // May be NULL.
LF_HDR * pLogicalFile,
FLMUINT uiLevel,
FLMUINT uiBlkType,
FLMBYTE * pKeyBuffer)
{
if ((*pbStateInitialized) && (pStateInfo->pRecord))
{
pStateInfo->pRecord->Release();
pStateInfo->pRecord = NULL;
}
f_memset( pStateInfo, 0, sizeof(STATE_INFO));
*pbStateInitialized = TRUE;
pStateInfo->uiVersionNum = uiVersionNum;
pStateInfo->pDb = pDb;
pStateInfo->pLogicalFile = pLogicalFile;
pStateInfo->uiLevel = uiLevel;
// Special cases for leaf and non-leaf blocks
if (uiBlkType == BHT_LEAF)
{
pStateInfo->uiElmOvhd = BBE_KEY;
}
else if (uiBlkType == BHT_NON_LEAF)
{
if (pLogicalFile)
{
if (pLogicalFile->pLFile->uiLfType == LF_INDEX)
{
if (pLogicalFile->pIxd &&
(pLogicalFile->pIxd->uiFlags & IXD_POSITIONING))
{
uiBlkType = BHT_NON_LEAF_COUNTS;
}
else
{
uiBlkType = BHT_NON_LEAF;
}
}
else
{
uiBlkType = BHT_NON_LEAF_DATA;
}
}
}
if (uiBlkType == BHT_NON_LEAF_DATA)
{
pStateInfo->uiElmOvhd = BNE_DATA_OVHD;
}
else if (uiBlkType == BHT_NON_LEAF)
{
pStateInfo->uiElmOvhd = BNE_KEY_START;
}
else if (uiBlkType == BHT_NON_LEAF_COUNTS)
{
pStateInfo->uiElmOvhd = BNE_KEY_COUNTS_START;
}
pStateInfo->uiBlkType = uiBlkType;
pStateInfo->pCurKey = pKeyBuffer;
pStateInfo->uiElmLastFlag = 0xFF;
pStateInfo->uiFieldType = 0xFF;
}