//------------------------------------------------------------------------- // 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; }