//------------------------------------------------------------------------- // Desc: Calculate block checksum // Tabs: 3 // // Copyright (c) 1999-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: checksum.cpp 12245 2006-01-19 14:29:51 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------- #include "flaimsys.h" /******************************************************************** Desc: Compares or sets the checksum value in a block. Operates to compare the block checksum with the actual checksum. Ret: if (Compare) returns FERR_BLOCK_CHECKSUM block checksum does not agree with checksum header values. *********************************************************************/ RCODE BlkCheckSum( FLMBYTE * pucBlkPtr, FLMINT iCompare, FLMUINT uiBlkAddress, FLMUINT uiBlkSize) { RCODE rc = FERR_OK; FLMUINT uiCurrChecksum = 0; FLMUINT uiNewChecksum; FLMUINT uiEncryptSize; FLMUINT uiAdds = 0; FLMUINT uiXORs = 0; FLMBYTE * pucSaveBlkPtr = pucBlkPtr; // Check the block length against the maximum block size uiEncryptSize = (FLMUINT)getEncryptSize( pucBlkPtr); if( uiEncryptSize > uiBlkSize || uiEncryptSize < BH_OVHD) { rc = RC_SET( FERR_BLOCK_CHECKSUM); goto Exit; } // If we are comparing, but there is no current checksum just return. // The next time the checksum is modified, the comparison will be performed. // Version 3.x will store the full block address or if // a checksum is used, the lost low byte of block address is checksummed. if( iCompare == CHECKSUM_CHECK) { uiCurrChecksum = (FLMUINT)(((FLMUINT)pucBlkPtr[ BH_CHECKSUM_HIGH] << 8) + (FLMUINT)pucBlkPtr[ BH_CHECKSUM_LOW]); } // We need to checksum the data that is encrypted. // This is done by the getEncryptSize() call. // Check all of block, except for embedded checksum bytes. // For speed, the initial values of uiAdds and uiXORs effectively ignore/skip // the checksum values already embedded in the source: (a - a) == 0 and // (a ^ a) == 0 so the initial values, net of the 2nd operations, equal zero // too. uiAdds = 0 - (pucBlkPtr[ BH_CHECKSUM_LOW] + pucBlkPtr[ BH_CHECKSUM_HIGH]); uiXORs = pucBlkPtr[ BH_CHECKSUM_LOW] ^ pucBlkPtr[ BH_CHECKSUM_HIGH]; // The 3.x version checksums the low byte of the address. if( uiBlkAddress != BT_END) { uiAdds += (FLMBYTE)uiBlkAddress; uiXORs ^= (FLMBYTE)uiBlkAddress; } f_calcFastChecksum( pucBlkPtr, uiEncryptSize, &uiAdds, &uiXORs); uiNewChecksum = (((uiAdds << 8) + uiXORs) & 0xFFFF); // Set the checksum if (iCompare == CHECKSUM_SET) { pucSaveBlkPtr[ BH_CHECKSUM_HIGH] = (FLMBYTE)(uiNewChecksum >> 8); pucSaveBlkPtr[ BH_CHECKSUM_LOW] = (FLMBYTE)uiNewChecksum; goto Exit; } // The checksum is different from the stored checksum. // For version 3.x database we don't store the low byte of the // address. Thus, it will have to be computed from the checksum. if( uiBlkAddress == BT_END) { FLMBYTE byXor; FLMBYTE byAdd; FLMBYTE byDelta; // If there is a one byte value that will satisfy both // sides of the checksum, the checksum is OK and that value // is the first byte value. byXor = (FLMBYTE) uiNewChecksum; byAdd = (FLMBYTE) (uiNewChecksum >> 8); byDelta = byXor ^ pucSaveBlkPtr [BH_CHECKSUM_LOW]; // Here is the big check, if byDelta is also what is // off with the add portion of the checksum, we have // a good value. if( ((FLMBYTE) (byAdd + byDelta)) == pucSaveBlkPtr[ BH_CHECKSUM_HIGH] ) { // Set the low checksum value with the computed value. pucSaveBlkPtr[ BH_CHECKSUM_LOW] = byDelta; goto Exit; } } else { // This has the side effect of setting the low block address byte // in the block thus getting rid of the low checksum byte. // NOTE: We are allowing the case where the calculated checksum is // zero and the stored checksum is one because we used to change // a calculated zero to a one in old databases and store the one. // This is probably a somewhat rare case (1 out of 65536 checksums // will be zero), so forgiving it will be OK most of the time. // So that those don't cause us to report block checksum errors, // we just allow it - checksumming isn't a perfect check anyway. if (uiNewChecksum == uiCurrChecksum || ((!uiNewChecksum) && (uiCurrChecksum == 1))) { pucSaveBlkPtr [BH_CHECKSUM_LOW] = (FLMBYTE) uiBlkAddress; goto Exit; } } // Otherwise, we have a checksum error. rc = RC_SET( FERR_BLOCK_CHECKSUM); Exit: return( rc); } /******************************************************************** Desc: *********************************************************************/ FLMUINT lgHdrCheckSum( FLMBYTE * pucLogHdr, FLMBOOL bCompare) { FLMUINT uiCnt; FLMUINT uiTempSum; FLMUINT uiCurrChecksum; FLMUINT uiTempSum2; FLMUINT uiBytesToChecksum; uiBytesToChecksum = (FB2UW( &pucLogHdr [LOG_FLAIM_VERSION]) < FLM_FILE_FORMAT_VER_4_3) ? LOG_HEADER_SIZE_VER40 : LOG_HEADER_SIZE; // If we are comparing, but there is no current checksum, return // zero to indicate success. The next time the checksum is // modified, the comparison will be performed. // // Unconverted databases may have a 0xFFFF or a zero in the checksum // If 0xFFFF, change to a zero so we only have to deal with one value. if( (uiCurrChecksum = (FLMUINT)FB2UW( &pucLogHdr[ LOG_HDR_CHECKSUM])) == 0xFFFF) { uiCurrChecksum = 0; } if( bCompare && !uiCurrChecksum) { return( 0); } // Check all of log header except for the bytes which contain the // checksum. // // For speed, uiTempSum is initialized to effectively ignore or skip // the checksum embedded in the source: (a - a) == 0 so we store a negative // that the later addition clears out. Also, the loop counter, i, // is 1 larger than the number of FLMUINT16's so that we can // pre-decrement by "for(;--i != 0;)" -- basically "loop-non-zero". for( uiTempSum = 0 - (FLMUINT)FB2UW( &pucLogHdr[ LOG_HDR_CHECKSUM]), uiCnt = 1 + uiBytesToChecksum / sizeof( FLMUINT16); --uiCnt != 0; ) { uiTempSum += (FLMUINT)FB2UW( pucLogHdr); pucLogHdr += sizeof( FLMUINT16); } // Don't want a zero or 0xFFFF checksum - change to 1 if( (0 == (uiTempSum2 = (uiTempSum & 0xFFFF))) || (uiTempSum2 == 0xFFFF)) { uiTempSum2 = 1; } return( (FLMUINT)(((bCompare) && (uiTempSum2 == uiCurrChecksum)) ? (FLMUINT)0 : uiTempSum2) ); }