git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@7 0109f412-320b-0410-ab79-c3e0c5ffbbe6
7664 lines
178 KiB
C++
7664 lines
178 KiB
C++
//-------------------------------------------------------------------------
|
|
// Desc: Routines for roll-forward logging.
|
|
// Tabs: 3
|
|
//
|
|
// Copyright (c) 1998-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: rfl.cpp 12334 2006-01-23 12:45:35 -0700 (Mon, 23 Jan 2006) dsanders $
|
|
//-------------------------------------------------------------------------
|
|
|
|
#include "flaimsys.h"
|
|
|
|
#define MOD_512( uiNum) (FLMUINT)((uiNum) & 511)
|
|
#define ON_512_BYTE_BOUNDARY( uiNum) (!MOD_512(uiNum))
|
|
#define ROUND_DOWN_TO_NEAREST_512( uiNum) \
|
|
(FLMUINT)((uiNum) & (~((FLMUINT)511)))
|
|
|
|
FSTATIC RCODE RflCheckMaxLogged(
|
|
FLMUINT * puiMaxBytesNeededRV,
|
|
FLMUINT uiPacketsLogged,
|
|
FLMUINT * puiCurrTotalLoggedRV,
|
|
FLMUINT uiBytesToLog);
|
|
|
|
FSTATIC void RflChangeCallback(
|
|
GRD_DifferenceData & DiffData,
|
|
void * CallbackData);
|
|
|
|
/********************************************************************
|
|
Desc: Constructor
|
|
*********************************************************************/
|
|
F_Rfl::F_Rfl()
|
|
{
|
|
m_pFile = NULL;
|
|
m_hBufMutex = F_MUTEX_NULL;
|
|
m_pCommitBuf = NULL;
|
|
m_pCurrentBuf = NULL;
|
|
m_uiRflWriteBufs = DEFAULT_RFL_WRITE_BUFFERS;
|
|
m_uiBufferSize = DEFAULT_RFL_BUFFER_SIZE;
|
|
f_memset( &m_Buf1, 0, sizeof( m_Buf1));
|
|
f_memset( &m_Buf2, 0, sizeof( m_Buf2));
|
|
m_bKeepRflFiles = FALSE;
|
|
m_uiRflMinFileSize = DEFAULT_MIN_RFL_FILE_SIZE;
|
|
m_uiRflMaxFileSize = DEFAULT_MAX_RFL_FILE_SIZE;
|
|
m_pFileHdl = NULL;
|
|
m_uiLastRecoverFileNum = 0;
|
|
f_memset( m_ucCurrSerialNum, 0, sizeof( m_ucCurrSerialNum));
|
|
m_bLoggingOff = FALSE;
|
|
m_bLoggingUnknown = FALSE;
|
|
m_uiUnknownPacketLen = 0;
|
|
m_bReadingUnknown = FALSE;
|
|
m_uiUnknownPacketBodyLen = 0;
|
|
m_pucUnknownPacketBody = NULL;
|
|
m_uiUnknownBodyLenProcessed = 0;
|
|
m_uiUnknownPacketRc = FERR_OK;
|
|
m_uiTransStartFile = 0;
|
|
m_uiTransStartAddr = 0;
|
|
m_uiCurrTransID = 0;
|
|
m_uiLastTransID = 0;
|
|
m_uiLastLoggedCommitTransID = 0;
|
|
m_uiOperCount = 0;
|
|
m_uiRflReadOffset = 0;
|
|
m_uiFileEOF = 0;
|
|
m_pRestore = NULL;
|
|
f_memset( m_szDbPrefix, 0, sizeof( m_szDbPrefix));
|
|
f_memset( m_szRflDir, 0, sizeof( m_szRflDir));
|
|
m_bRflDirSameAsDb = FALSE;
|
|
m_bCreateRflDir = FALSE;
|
|
f_memset( m_ucNextSerialNum, 0, sizeof( m_ucNextSerialNum));
|
|
m_bRflVolumeOk = TRUE;
|
|
m_bRflVolumeFull = FALSE;
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Destructor
|
|
*********************************************************************/
|
|
F_Rfl::~F_Rfl()
|
|
{
|
|
// Better not be in the middle of logging unknown packets for
|
|
// the application.
|
|
|
|
flmAssert( !m_bLoggingUnknown);
|
|
|
|
if (m_Buf1.pIOBuffer)
|
|
{
|
|
m_Buf1.pIOBuffer->Release();
|
|
m_Buf1.pIOBuffer = NULL;
|
|
}
|
|
if (m_Buf2.pIOBuffer)
|
|
{
|
|
m_Buf2.pIOBuffer->Release();
|
|
m_Buf2.pIOBuffer = NULL;
|
|
}
|
|
|
|
if( m_Buf1.pBufferMgr)
|
|
{
|
|
flmAssert( !m_Buf1.pBufferMgr->havePendingIO() &&
|
|
!m_Buf1.pBufferMgr->haveUsed());
|
|
m_Buf1.pBufferMgr->Release();
|
|
m_Buf1.pBufferMgr = NULL;
|
|
}
|
|
|
|
if( m_Buf2.pBufferMgr)
|
|
{
|
|
flmAssert( !m_Buf2.pBufferMgr->havePendingIO() &&
|
|
!m_Buf2.pBufferMgr->haveUsed());
|
|
m_Buf2.pBufferMgr->Release();
|
|
m_Buf2.pBufferMgr = NULL;
|
|
}
|
|
|
|
if (m_hBufMutex != F_MUTEX_NULL)
|
|
{
|
|
f_mutexDestroy( &m_hBufMutex);
|
|
}
|
|
|
|
if (m_pFileHdl)
|
|
{
|
|
m_pFileHdl->Close();
|
|
m_pFileHdl->Release();
|
|
m_pFileHdl = NULL;
|
|
m_pFile = NULL;
|
|
}
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Returns a boolean indicating whether or not we are at
|
|
the end of the RFL log - will only be TRUE when we are
|
|
doing recovery.
|
|
*********************************************************************/
|
|
FLMBOOL F_Rfl::atEndOfLog( void)
|
|
{
|
|
return( (!m_pRestore &&
|
|
m_uiFileEOF &&
|
|
m_pCurrentBuf->uiRflFileOffset +
|
|
m_pCurrentBuf->uiRflBufBytes >= m_uiFileEOF &&
|
|
m_uiRflReadOffset == m_pCurrentBuf->uiRflBufBytes &&
|
|
m_pCurrentBuf->uiCurrFileNum == m_uiLastRecoverFileNum)
|
|
? TRUE
|
|
: FALSE);
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Gets the base RFL file name - does not have directory part.
|
|
This needs to be separate from the F_Rfl object so it can
|
|
be called without having to instantiate and set up an F_Rfl
|
|
object.
|
|
*********************************************************************/
|
|
void rflGetBaseFileName(
|
|
FLMUINT uiDbVersion,
|
|
const char * pszDbPrefix,
|
|
FLMUINT uiFileNum,
|
|
char * pszBaseNameOut)
|
|
{
|
|
FLMINT iCnt = 0;
|
|
FLMUINT uiDigit;
|
|
char * pszTmp = pszBaseNameOut;
|
|
|
|
if (uiDbVersion < FLM_VER_4_3)
|
|
{
|
|
|
|
// Output the database name prefix (up to three characters).
|
|
|
|
f_strcpy( pszTmp, pszDbPrefix);
|
|
while (*pszTmp)
|
|
{
|
|
pszTmp++;
|
|
}
|
|
|
|
// Output as five digit base 36 number.
|
|
|
|
pszTmp += 4;
|
|
while (iCnt < 5)
|
|
{
|
|
uiDigit = (FLMUINT)(uiFileNum % 36);
|
|
uiFileNum /= 36;
|
|
if (uiDigit <= 9)
|
|
{
|
|
uiDigit += NATIVE_ZERO;
|
|
}
|
|
else
|
|
{
|
|
uiDigit += (NATIVE_LOWER_A - 10);
|
|
}
|
|
|
|
*pszTmp = (FLMBYTE)uiDigit;
|
|
pszTmp--;
|
|
iCnt++;
|
|
}
|
|
|
|
// Skip to end of digits and append ".log" to name
|
|
|
|
pszTmp += 6;
|
|
f_strcpy( pszTmp, ".log");
|
|
}
|
|
else
|
|
{
|
|
|
|
// Output as eight digit hex number.
|
|
|
|
pszTmp += 7;
|
|
while (iCnt < 8)
|
|
{
|
|
uiDigit = (FLMUINT)(uiFileNum & 0xF);
|
|
uiFileNum >>= 4;
|
|
if (uiDigit <= 9)
|
|
{
|
|
uiDigit += NATIVE_ZERO;
|
|
}
|
|
else
|
|
{
|
|
uiDigit += (NATIVE_LOWER_A - 10);
|
|
}
|
|
*pszTmp = (FLMBYTE)uiDigit;
|
|
pszTmp--;
|
|
iCnt++;
|
|
}
|
|
|
|
// Skip to end of digits and append ".log" to name
|
|
|
|
pszTmp += 9;
|
|
f_strcpy( pszTmp, ".log");
|
|
}
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Gets the base RFL file name - does not have directory part.
|
|
*********************************************************************/
|
|
void F_Rfl::getBaseRflFileName(
|
|
FLMUINT uiFileNum,
|
|
char * pszBaseName)
|
|
{
|
|
rflGetBaseFileName( m_pFile->FileHdr.uiVersionNum,
|
|
m_szDbPrefix,
|
|
uiFileNum, pszBaseName);
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Generates the full roll forward log file name.
|
|
Name is based on the sequence number and the first three
|
|
characters of the database if the DB is less than version 4.3.
|
|
Otherwise, it is a hex number.
|
|
*********************************************************************/
|
|
RCODE F_Rfl::getFullRflFileName(
|
|
FLMUINT uiFileNum,
|
|
char * pszRflFileName)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
char szBaseName [F_FILENAME_SIZE];
|
|
|
|
// Get the directory name.
|
|
|
|
f_strcpy( pszRflFileName, m_szRflDir);
|
|
|
|
// Get the base RFL file name.
|
|
|
|
getBaseRflFileName( uiFileNum, szBaseName);
|
|
|
|
// Append the two together.
|
|
|
|
if (RC_BAD( rc = f_pathAppend( pszRflFileName, szBaseName)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
return( rc);
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Positions to the offset specified in the RFL file.
|
|
*********************************************************************/
|
|
RCODE F_Rfl::positionTo(
|
|
FLMUINT uiFileOffset
|
|
)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FLMUINT uiBytesToRead;
|
|
FLMUINT uiBytesRead;
|
|
|
|
// Should never be attempting to position to something less
|
|
// than 512 - the header is stored in the first 512 bytes.
|
|
|
|
flmAssert( uiFileOffset >= 512);
|
|
|
|
// If the position is within our current buffer, see if we
|
|
// can adjust things without having to go back and re-read
|
|
// the buffer from disk.
|
|
|
|
if (m_pCurrentBuf->uiRflBufBytes &&
|
|
uiFileOffset >= m_pCurrentBuf->uiRflFileOffset &&
|
|
uiFileOffset <= m_pCurrentBuf->uiRflFileOffset +
|
|
m_pCurrentBuf->uiRflBufBytes)
|
|
{
|
|
|
|
// Whatever is in the buffer beyond uiFileOffset is irrelevant
|
|
// and can be discarded.
|
|
|
|
m_pCurrentBuf->uiRflBufBytes = uiFileOffset -
|
|
m_pCurrentBuf->uiRflFileOffset;
|
|
}
|
|
else
|
|
{
|
|
|
|
// Populate the buffer from the 512 byte boundary that is just
|
|
// before the offset we are trying to position to.
|
|
|
|
uiBytesToRead = MOD_512( uiFileOffset);
|
|
m_pCurrentBuf->uiRflFileOffset = ROUND_DOWN_TO_NEAREST_512( uiFileOffset);
|
|
m_pCurrentBuf->uiRflBufBytes = MOD_512( uiFileOffset);
|
|
if (m_pCurrentBuf->uiRflBufBytes)
|
|
{
|
|
if (RC_BAD( rc = m_pFileHdl->SectorRead( m_pCurrentBuf->uiRflFileOffset,
|
|
m_pCurrentBuf->uiRflBufBytes,
|
|
m_pCurrentBuf->pIOBuffer->m_pucBuffer,
|
|
&uiBytesRead)))
|
|
{
|
|
if (rc == FERR_IO_END_OF_FILE)
|
|
{
|
|
rc = RC_SET( FERR_NOT_RFL);
|
|
}
|
|
else
|
|
{
|
|
m_bRflVolumeOk = FALSE;
|
|
}
|
|
goto Exit;
|
|
}
|
|
else if (uiBytesRead < m_pCurrentBuf->uiRflBufBytes)
|
|
{
|
|
rc = RC_SET( FERR_NOT_RFL);
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Get the ACTUAL RFL directory, using as input parameters the
|
|
database version, the name of the database, and the
|
|
user specified RFL directory. Also return the database
|
|
prefix.
|
|
*********************************************************************/
|
|
RCODE rflGetDirAndPrefix(
|
|
FLMUINT uiDbVersionNum,
|
|
const char * pszDbFileName,
|
|
const char * pszRflDirIn,
|
|
char * pszRflDirOut,
|
|
char * pszDbPrefixOut)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
char szDbPath [F_PATH_MAX_SIZE];
|
|
char szBaseName [F_FILENAME_SIZE];
|
|
|
|
// Parse the database name into directory and base name
|
|
|
|
if (RC_BAD( rc = f_pathReduce( pszDbFileName,
|
|
szDbPath, szBaseName)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Get the base path
|
|
|
|
flmGetDbBasePath( pszDbPrefixOut, szBaseName, NULL);
|
|
|
|
if (uiDbVersionNum >= FLM_VER_4_3)
|
|
{
|
|
|
|
// Determine the RFL directory. If one was
|
|
// specified, it is whatever was specified.
|
|
// Otherwise, it is relative to the database
|
|
// directory.
|
|
|
|
if (pszRflDirIn && *pszRflDirIn)
|
|
{
|
|
f_strcpy( pszRflDirOut, pszRflDirIn);
|
|
}
|
|
else
|
|
{
|
|
f_strcpy( pszRflDirOut, szDbPath);
|
|
}
|
|
|
|
// For 4.3 and above, the RFL files go in
|
|
// a subdirectory underneath the directory where
|
|
// the database is located or the specified
|
|
// directory.
|
|
|
|
f_strcpy( szBaseName, pszDbPrefixOut);
|
|
f_strcat( szBaseName, ".rfl");
|
|
f_pathAppend( pszRflDirOut, szBaseName);
|
|
}
|
|
else
|
|
{
|
|
f_strcpy( pszRflDirOut, szDbPath);
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Set the RFL directory. If pszRflDir is NULL or empty string,
|
|
the RFL directory is set to the same directory as the
|
|
database.
|
|
*********************************************************************/
|
|
RCODE F_Rfl::setRflDir(
|
|
const char * pszRflDir)
|
|
{
|
|
// Better have set up the FFILE pointer.
|
|
|
|
flmAssert( m_pFile != NULL);
|
|
|
|
m_bRflDirSameAsDb = (!pszRflDir || !(*pszRflDir))
|
|
? TRUE
|
|
: FALSE;
|
|
|
|
flmAssert( m_pFile->FileHdr.uiVersionNum);
|
|
|
|
if (m_pFile->FileHdr.uiVersionNum < FLM_VER_4_3)
|
|
{
|
|
|
|
// Don't allow RFL directory to be specified for versions
|
|
// less than 4.3
|
|
|
|
pszRflDir = NULL;
|
|
m_bRflDirSameAsDb = TRUE;
|
|
}
|
|
|
|
m_bCreateRflDir =
|
|
(m_pFile->FileHdr.uiVersionNum >= FLM_VER_4_3)
|
|
? TRUE
|
|
: FALSE;
|
|
return( rflGetDirAndPrefix(
|
|
m_pFile->FileHdr.uiVersionNum, m_pFile->pszDbPath,
|
|
pszRflDir, m_szRflDir, m_szDbPrefix));
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Gets an RFL file name - based on DB name and RFL directory.
|
|
*********************************************************************/
|
|
RCODE rflGetFileName(
|
|
FLMUINT uiDbVersion,
|
|
const char * pszDbName,
|
|
const char * pszRflDir,
|
|
FLMUINT uiFileNum,
|
|
char * pszRflFileName)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
char szDbPrefix[ F_FILENAME_SIZE];
|
|
char szBaseName[ F_FILENAME_SIZE];
|
|
|
|
// Get the full RFL file name.
|
|
|
|
if (RC_BAD( rc = rflGetDirAndPrefix( uiDbVersion, pszDbName,
|
|
pszRflDir, pszRflFileName, szDbPrefix)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
rflGetBaseFileName( uiDbVersion, szDbPrefix, uiFileNum, szBaseName);
|
|
if (RC_BAD( rc = f_pathAppend( pszRflFileName, szBaseName)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
Exit:
|
|
return( rc);
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Gets an RFL file number from the RFL file name.
|
|
*********************************************************************/
|
|
FLMBOOL rflGetFileNum(
|
|
FLMUINT uiDbVersion,
|
|
const char * pszDbPrefix,
|
|
const char * pszRflFileName,
|
|
FLMUINT * puiFileNum)
|
|
{
|
|
FLMBOOL bGotNum = FALSE;
|
|
char szDir[F_PATH_MAX_SIZE];
|
|
char szBaseName[F_FILENAME_SIZE];
|
|
char * pszTmp;
|
|
FLMUINT uiCharCnt;
|
|
|
|
if( RC_BAD( f_pathReduce( pszRflFileName, szDir, szBaseName)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// See if it has a .log extension.
|
|
|
|
pszTmp = &szBaseName [0];
|
|
while (*pszTmp && *pszTmp != '.')
|
|
{
|
|
pszTmp++;
|
|
}
|
|
|
|
// If we do not have a .log extension, it is not a legitimate
|
|
// RFL file.
|
|
|
|
if (f_stricmp( pszTmp, ".log") != 0)
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Parse out the name according to the rules for this DB version.
|
|
|
|
*pszTmp = 0; // Set period to zero
|
|
pszTmp = &szBaseName [0];
|
|
*puiFileNum = 0;
|
|
uiCharCnt = 0;
|
|
if (uiDbVersion >= FLM_VER_4_3)
|
|
{
|
|
|
|
// Name up to the period should be a hex number
|
|
|
|
while (*pszTmp)
|
|
{
|
|
(*puiFileNum) <<= 4;
|
|
if (*pszTmp >= NATIVE_ZERO && *pszTmp <= NATIVE_NINE)
|
|
{
|
|
*puiFileNum += (FLMUINT)(*pszTmp - NATIVE_ZERO);
|
|
}
|
|
else if (*pszTmp >= NATIVE_LOWER_A && *pszTmp <= NATIVE_LOWER_F)
|
|
{
|
|
*puiFileNum += ((FLMUINT)(*pszTmp - NATIVE_LOWER_A) + 10);
|
|
}
|
|
else if (*pszTmp >= NATIVE_UPPER_A && *pszTmp <= NATIVE_UPPER_F)
|
|
{
|
|
*puiFileNum += ((FLMUINT)(*pszTmp - NATIVE_UPPER_A) + 10);
|
|
}
|
|
else
|
|
{
|
|
goto Exit; // Not a hex number
|
|
}
|
|
uiCharCnt++;
|
|
pszTmp++;
|
|
}
|
|
|
|
// Better have been exactly 8 hex digits.
|
|
|
|
bGotNum = (FLMBOOL)((uiCharCnt == 8)
|
|
? TRUE
|
|
: FALSE);
|
|
}
|
|
else
|
|
{
|
|
FLMUINT uiLen = f_strlen( pszTmp);
|
|
FLMUINT uiPrefixLen = f_strlen( pszDbPrefix);
|
|
|
|
// Length of base name without the .log extension better
|
|
// be exactly 5 more characters than the length of
|
|
// the prefix.
|
|
|
|
if (uiLen != uiPrefixLen + 5)
|
|
{
|
|
flmAssert( 0);
|
|
goto Exit;
|
|
}
|
|
|
|
// Prefix better match.
|
|
|
|
while (uiPrefixLen)
|
|
{
|
|
if (f_toupper( *pszTmp) != f_toupper( *pszDbPrefix))
|
|
{
|
|
goto Exit;
|
|
}
|
|
uiPrefixLen--;
|
|
pszTmp++;
|
|
pszDbPrefix++;
|
|
}
|
|
|
|
// Rest of the name is the five digits that are a base 36 number.
|
|
|
|
while (*pszTmp)
|
|
{
|
|
(*puiFileNum) *= 36;
|
|
if (*pszTmp >= NATIVE_ZERO && *pszTmp <= NATIVE_NINE)
|
|
{
|
|
*puiFileNum += (FLMUINT)(*pszTmp - NATIVE_ZERO);
|
|
}
|
|
else if (*pszTmp >= NATIVE_LOWER_A && *pszTmp <= NATIVE_LOWER_Z)
|
|
{
|
|
*puiFileNum += ((FLMUINT)(*pszTmp - NATIVE_LOWER_A) + 10);
|
|
}
|
|
else if (*pszTmp >= NATIVE_UPPER_A && *pszTmp <= NATIVE_UPPER_Z)
|
|
{
|
|
*puiFileNum += ((FLMUINT)(*pszTmp - NATIVE_UPPER_A) + 10);
|
|
}
|
|
else
|
|
{
|
|
goto Exit; // Not a base 36 number
|
|
}
|
|
pszTmp++;
|
|
}
|
|
bGotNum = TRUE;
|
|
}
|
|
Exit:
|
|
return( bGotNum);
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Sets up the RFL object - associating with a file, etc.
|
|
*********************************************************************/
|
|
RCODE F_Rfl::setup(
|
|
FFILE * pFile,
|
|
const char * pszRflDir)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
|
|
// Better not already be associated with an FFILE
|
|
|
|
flmAssert( m_pFile == NULL);
|
|
m_pFile = pFile;
|
|
|
|
// Allocate memory for the RFL buffers
|
|
|
|
#ifndef FLM_UNIX
|
|
if (!gv_FlmSysData.bOkToDoAsyncWrites)
|
|
#endif
|
|
{
|
|
m_uiRflWriteBufs = 1;
|
|
m_uiBufferSize =
|
|
DEFAULT_RFL_WRITE_BUFFERS * DEFAULT_RFL_BUFFER_SIZE;
|
|
}
|
|
|
|
if (RC_BAD( rc = f_mutexCreate( &m_hBufMutex)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( (m_Buf1.pBufferMgr = f_new F_IOBufferMgr) == NULL)
|
|
{
|
|
rc = RC_SET( FERR_MEM);
|
|
goto Exit;
|
|
}
|
|
if( (m_Buf2.pBufferMgr = f_new F_IOBufferMgr) == NULL)
|
|
{
|
|
rc = RC_SET( FERR_MEM);
|
|
goto Exit;
|
|
}
|
|
m_Buf1.pBufferMgr->enableKeepBuffer();
|
|
m_Buf1.pBufferMgr->setMaxBuffers( m_uiRflWriteBufs);
|
|
m_Buf1.pBufferMgr->setMaxBytes( m_uiRflWriteBufs * m_uiBufferSize);
|
|
|
|
if( RC_BAD( rc = m_Buf1.pBufferMgr->getBuffer( &m_Buf1.pIOBuffer,
|
|
m_uiBufferSize, m_uiBufferSize)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
m_Buf2.pBufferMgr->enableKeepBuffer();
|
|
m_Buf2.pBufferMgr->setMaxBuffers( m_uiRflWriteBufs);
|
|
m_Buf2.pBufferMgr->setMaxBytes( m_uiRflWriteBufs * m_uiBufferSize);
|
|
|
|
if( RC_BAD( rc = m_Buf2.pBufferMgr->getBuffer( &m_Buf2.pIOBuffer,
|
|
m_uiBufferSize, m_uiBufferSize)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
m_bLoggingOff = FALSE;
|
|
m_pCurrentBuf = &m_Buf1;
|
|
m_pCurrentBuf->uiRflBufBytes = 0;
|
|
|
|
// Set the RFL directory and prefix if necessary.
|
|
|
|
if (RC_BAD( rc = setRflDir( pszRflDir)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Wait for the writes of a buffer to finish. This routine assumes
|
|
that the m_hBufMutex is locked when coming in. It will ALWAYS
|
|
unlock the mutex before exiting.
|
|
*********************************************************************/
|
|
RCODE F_Rfl::waitForWrites(
|
|
RFL_BUFFER * pBuffer,
|
|
FLMBOOL bIsWriter
|
|
)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
RCODE TempRc;
|
|
RFL_WAITER Waiter;
|
|
FLMBOOL bMutexLocked = TRUE;
|
|
|
|
// Put self on the wait queue for the buffer.
|
|
|
|
Waiter.uiThreadId = f_threadId();
|
|
Waiter.bIsWriter = bIsWriter;
|
|
Waiter.hESem = F_SEM_NULL;
|
|
if (RC_BAD( rc = f_semCreate( &Waiter.hESem)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Note: rc better be changed to success or write error
|
|
// by the process that signals us.
|
|
|
|
rc = RC_SET( FERR_FAILURE);
|
|
Waiter.pRc = &rc;
|
|
Waiter.pNext = NULL;
|
|
if (pBuffer->pLastWaiter)
|
|
{
|
|
pBuffer->pLastWaiter->pNext = &Waiter;
|
|
}
|
|
else
|
|
{
|
|
pBuffer->pFirstWaiter = &Waiter;
|
|
}
|
|
pBuffer->pLastWaiter = &Waiter;
|
|
f_mutexUnlock( m_hBufMutex);
|
|
bMutexLocked = FALSE;
|
|
|
|
// Now just wait to be signaled.
|
|
|
|
if (RC_BAD( TempRc = f_semWait( Waiter.hESem, F_SEM_WAITFOREVER)))
|
|
{
|
|
#ifdef FLM_NLM
|
|
EnterDebugger();
|
|
#else
|
|
flmAssert( 0);
|
|
#endif
|
|
rc = TempRc;
|
|
}
|
|
else
|
|
{
|
|
|
|
// Process that signaled us better set the rc to something
|
|
// besides FERR_FAILURE.
|
|
|
|
if (rc == FERR_FAILURE)
|
|
{
|
|
#ifdef FLM_NLM
|
|
EnterDebugger();
|
|
#else
|
|
flmAssert( 0);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
if( Waiter.hESem != F_SEM_NULL)
|
|
{
|
|
f_semDestroy( &Waiter.hESem);
|
|
}
|
|
|
|
if (bMutexLocked)
|
|
{
|
|
f_mutexUnlock( m_hBufMutex);
|
|
}
|
|
return( rc);
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: If a commit is in progress, wait for it to finish.
|
|
*********************************************************************/
|
|
RCODE F_Rfl::waitForCommit( void)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FLMBOOL bMutexLocked = FALSE;
|
|
|
|
// NOTE: If m_pCommitBuf is NULL it cannot be set to something
|
|
// non-NULL except by this thread when this thread ends the
|
|
// transaction. So, there is no need to lock the mutex and
|
|
// re-check if it is NULL.
|
|
|
|
if (m_pCommitBuf)
|
|
{
|
|
f_mutexLock( m_hBufMutex);
|
|
bMutexLocked = TRUE;
|
|
|
|
// Check m_pCommitBuf again after locking mutex - may have
|
|
// finished.
|
|
|
|
if (m_pCommitBuf)
|
|
{
|
|
|
|
// waitForWrites will unlock the mutex.
|
|
|
|
bMutexLocked = FALSE;
|
|
rc = waitForWrites( m_pCommitBuf, FALSE);
|
|
}
|
|
}
|
|
|
|
if (bMutexLocked)
|
|
{
|
|
f_mutexUnlock( m_hBufMutex);
|
|
}
|
|
return( rc);
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Write out the header information for an RFL file.
|
|
*********************************************************************/
|
|
RCODE F_Rfl::writeHeader(
|
|
FLMUINT uiFileNum,
|
|
FLMUINT uiEof,
|
|
FLMBYTE * pucSerialNum,
|
|
FLMBYTE * pucNextSerialNum,
|
|
FLMBOOL bKeepSignature)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FLMBYTE ucBuf [512];
|
|
FLMUINT uiBytesWritten;
|
|
|
|
flmAssert( m_pFile);
|
|
flmAssert( m_pFileHdl);
|
|
|
|
f_memset( ucBuf, 0, sizeof( ucBuf));
|
|
f_memcpy( &ucBuf [RFL_NAME_POS], RFL_NAME, RFL_NAME_LEN);
|
|
f_memcpy( &ucBuf [RFL_VERSION_POS], RFL_VERSION, RFL_VERSION_LEN);
|
|
UD2FBA( (FLMUINT32)uiFileNum, &ucBuf [RFL_FILE_NUMBER_POS]);
|
|
UD2FBA( (FLMUINT32)uiEof, &ucBuf [RFL_EOF_POS]);
|
|
|
|
if (m_pFile->FileHdr.uiVersionNum >= FLM_VER_4_3)
|
|
{
|
|
f_memcpy( &ucBuf [RFL_DB_SERIAL_NUM_POS],
|
|
&m_pFile->ucLastCommittedLogHdr [LOG_DB_SERIAL_NUM],
|
|
F_SERIAL_NUM_SIZE);
|
|
f_memcpy( &ucBuf [RFL_SERIAL_NUM_POS], pucSerialNum,
|
|
F_SERIAL_NUM_SIZE);
|
|
f_memcpy( &ucBuf [RFL_NEXT_FILE_SERIAL_NUM_POS], pucNextSerialNum,
|
|
F_SERIAL_NUM_SIZE);
|
|
f_strcpy( (char *)&ucBuf [RFL_KEEP_SIGNATURE_POS],
|
|
((bKeepSignature)
|
|
? RFL_KEEP_SIGNATURE
|
|
: RFL_NOKEEP_SIGNATURE));
|
|
}
|
|
|
|
// Write out the header
|
|
|
|
if (RC_BAD( rc = m_pFileHdl->SectorWrite( 0L, 512,
|
|
ucBuf, sizeof( ucBuf),
|
|
NULL, &uiBytesWritten)))
|
|
{
|
|
// Remap disk full error
|
|
|
|
if (rc == FERR_IO_DISK_FULL)
|
|
{
|
|
rc = RC_SET( FERR_RFL_DEVICE_FULL);
|
|
m_bRflVolumeFull = TRUE;
|
|
}
|
|
m_bRflVolumeOk = FALSE;
|
|
goto Exit;
|
|
}
|
|
|
|
// Flush the file handle to ensure it is forced to disk.
|
|
|
|
if (RC_BAD( rc = m_pFileHdl->Flush()))
|
|
{
|
|
|
|
// Remap disk full error
|
|
|
|
if (rc == FERR_IO_DISK_FULL)
|
|
{
|
|
rc = RC_SET( FERR_RFL_DEVICE_FULL);
|
|
m_bRflVolumeFull = TRUE;
|
|
}
|
|
m_bRflVolumeOk = FALSE;
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
return( rc);
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Verifies the header of an RFL file.
|
|
*********************************************************************/
|
|
RCODE F_Rfl::verifyHeader(
|
|
FLMBYTE * pucHeader,
|
|
FLMUINT uiFileNum,
|
|
FLMBYTE * pucSerialNum
|
|
)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
|
|
flmAssert( m_pFile);
|
|
|
|
|
|
// Check the RFL name and version number
|
|
|
|
if (f_memcmp( &pucHeader [RFL_NAME_POS], RFL_NAME,
|
|
RFL_NAME_LEN) != 0)
|
|
{
|
|
rc = RC_SET( FERR_NOT_RFL);
|
|
goto Exit;
|
|
}
|
|
if (f_memcmp( &pucHeader [RFL_VERSION_POS], RFL_VERSION,
|
|
RFL_VERSION_LEN) != 0)
|
|
{
|
|
rc = RC_SET( FERR_NOT_RFL);
|
|
goto Exit;
|
|
}
|
|
if (m_pFile->FileHdr.uiVersionNum >= FLM_VER_4_3)
|
|
{
|
|
|
|
// Verify the database serial number
|
|
|
|
if (f_memcmp( &pucHeader [RFL_DB_SERIAL_NUM_POS],
|
|
&m_pFile->ucLastCommittedLogHdr [LOG_DB_SERIAL_NUM],
|
|
F_SERIAL_NUM_SIZE) != 0)
|
|
{
|
|
rc = RC_SET( FERR_BAD_RFL_DB_SERIAL_NUM);
|
|
goto Exit;
|
|
}
|
|
|
|
// Verify the serial number that is expected to be on the
|
|
// RFL file. If pucSerialNum is NULL, we will not verify
|
|
// it. This is generally only done during recovery or restore
|
|
// when we are reading through multiple RFL files and we need
|
|
// to verify their serial numbers.
|
|
|
|
if (pucSerialNum &&
|
|
f_memcmp( &pucHeader [RFL_SERIAL_NUM_POS],
|
|
pucSerialNum, F_SERIAL_NUM_SIZE) != 0)
|
|
{
|
|
rc = RC_SET( FERR_BAD_RFL_SERIAL_NUM);
|
|
goto Exit;
|
|
}
|
|
|
|
// Verify the file number.
|
|
|
|
if (uiFileNum != (FLMUINT)FB2UD( &pucHeader [RFL_FILE_NUMBER_POS]))
|
|
{
|
|
rc = RC_SET( FERR_BAD_RFL_FILE_NUMBER);
|
|
goto Exit;
|
|
}
|
|
|
|
// Save serial numbers from the header.
|
|
|
|
f_memcpy( m_ucCurrSerialNum, &pucHeader [RFL_SERIAL_NUM_POS],
|
|
F_SERIAL_NUM_SIZE);
|
|
f_memcpy( m_ucNextSerialNum, &pucHeader [RFL_NEXT_FILE_SERIAL_NUM_POS],
|
|
F_SERIAL_NUM_SIZE);
|
|
}
|
|
|
|
// Save some things from the header.
|
|
|
|
m_uiFileEOF = (FLMUINT)FB2UD( &pucHeader [RFL_EOF_POS]);
|
|
|
|
Exit:
|
|
return( rc);
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Opens an RFL file. Verifies the serial number for 4.3 dbs.
|
|
*********************************************************************/
|
|
RCODE F_Rfl::openFile(
|
|
FLMUINT uiFileNum,
|
|
FLMBYTE * pucSerialNum
|
|
)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
char szRflFileName [F_PATH_MAX_SIZE];
|
|
FLMBYTE ucBuf [512];
|
|
FLMUINT uiBytesRead;
|
|
|
|
flmAssert( m_pFile);
|
|
|
|
// If we have a file open and it is not the file number
|
|
// passed in, close it.
|
|
|
|
if (m_pFileHdl)
|
|
{
|
|
if (m_pCurrentBuf->uiCurrFileNum != uiFileNum)
|
|
{
|
|
if (RC_BAD( rc = waitForCommit()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
closeFile();
|
|
}
|
|
else
|
|
{
|
|
goto Exit; // Will return FERR_OK
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
// Should not be able to be in the middle of a commit
|
|
// if we don't have a file open!
|
|
|
|
flmAssert( !m_pCommitBuf);
|
|
}
|
|
|
|
// Generate the log file name.
|
|
|
|
if (RC_BAD( rc = getFullRflFileName( uiFileNum, szRflFileName)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Open the file.
|
|
|
|
if (RC_BAD( rc = gv_FlmSysData.pFileSystem->OpenBlockFile( szRflFileName,
|
|
F_IO_RDWR | F_IO_SH_DENYNONE | F_IO_DIRECT,
|
|
512, &m_pFileHdl)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Read the header.
|
|
|
|
if (RC_BAD( rc = m_pFileHdl->SectorRead( 0, 512, ucBuf,
|
|
&uiBytesRead)))
|
|
{
|
|
if (rc == FERR_IO_END_OF_FILE)
|
|
{
|
|
rc = RC_SET( FERR_NOT_RFL);
|
|
}
|
|
else
|
|
{
|
|
m_bRflVolumeOk = FALSE;
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
// If there is not enough data in the buffer, it is not an
|
|
// RFL file.
|
|
|
|
if (uiBytesRead < 512)
|
|
{
|
|
rc = RC_SET( FERR_NOT_RFL);
|
|
goto Exit;
|
|
}
|
|
|
|
// Verify the header information
|
|
|
|
if (RC_BAD( rc = verifyHeader( ucBuf, uiFileNum, pucSerialNum)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
m_pCurrentBuf->uiRflBufBytes = 0;
|
|
m_pCurrentBuf->uiRflFileOffset = 0;
|
|
m_pCurrentBuf->uiCurrFileNum = uiFileNum;
|
|
Exit:
|
|
if (RC_BAD( rc))
|
|
{
|
|
waitForCommit();
|
|
closeFile();
|
|
}
|
|
return( rc);
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Creates a new roll forward log file.
|
|
*********************************************************************/
|
|
RCODE F_Rfl::createFile(
|
|
FLMUINT uiFileNum,
|
|
FLMBYTE * pucSerialNum,
|
|
FLMBYTE * pucNextSerialNum,
|
|
FLMBOOL bKeepSignature)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
char szRflFileName [F_PATH_MAX_SIZE];
|
|
|
|
flmAssert( m_pFile);
|
|
|
|
// Better not be trying to create the current file
|
|
|
|
flmAssert( uiFileNum != m_pCurrentBuf->uiCurrFileNum);
|
|
|
|
// If we have a file open close it.
|
|
|
|
if (RC_BAD( rc = waitForCommit()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
closeFile();
|
|
|
|
// Generate the log file name.
|
|
|
|
if (RC_BAD( rc = getFullRflFileName( uiFileNum, szRflFileName)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Delete the file if it already exists - don't care
|
|
// about return code here
|
|
|
|
(void)gv_FlmSysData.pFileSystem->Delete( szRflFileName);
|
|
|
|
// If DB is 4.3 or greater and we are in the same directory as
|
|
// our database files, see if we need to create the
|
|
// subdirectory. Otherwise, the RFL directory should already
|
|
// have been created. If the directory already exists, it is
|
|
// OK - we only try this the first time after setRflDir is
|
|
// called - to either verify that the directory exists, or if
|
|
// it doesn't, to create it.
|
|
|
|
if (m_bCreateRflDir)
|
|
{
|
|
|
|
// If it already exists, don't attempt to create it.
|
|
|
|
if (RC_BAD( rc = gv_FlmSysData.pFileSystem->Exists( m_szRflDir)))
|
|
{
|
|
if (rc != FERR_IO_PATH_NOT_FOUND && rc != FERR_IO_INVALID_PATH)
|
|
{
|
|
goto Exit;
|
|
}
|
|
else
|
|
{
|
|
if (RC_BAD( rc =
|
|
gv_FlmSysData.pFileSystem->CreateDir( m_szRflDir)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
m_bCreateRflDir = FALSE;
|
|
}
|
|
|
|
// Create the file
|
|
|
|
if (RC_BAD( rc = gv_FlmSysData.pFileSystem->CreateBlockFile( szRflFileName,
|
|
F_IO_RDWR | F_IO_EXCL | F_IO_SH_DENYNONE | F_IO_DIRECT,
|
|
512, &m_pFileHdl)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Initialize the header.
|
|
|
|
if (RC_BAD( rc = writeHeader( uiFileNum, 0,
|
|
pucSerialNum, pucNextSerialNum, bKeepSignature)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
m_pCurrentBuf->uiRflBufBytes = 0;
|
|
m_pCurrentBuf->uiRflFileOffset = 512;
|
|
m_pCurrentBuf->uiCurrFileNum = uiFileNum;
|
|
Exit:
|
|
|
|
// Close the RFL log file AND delete it if we were not successful.
|
|
|
|
if (RC_BAD( rc))
|
|
{
|
|
closeFile();
|
|
(void)gv_FlmSysData.pFileSystem->Delete( szRflFileName);
|
|
}
|
|
return( rc);
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Copy last partial sector of last buffer written (or to be
|
|
written) into a new buffer.
|
|
*********************************************************************/
|
|
void F_Rfl::copyLastSector(
|
|
RFL_BUFFER * pBuffer,
|
|
FLMBYTE * pucOldBuffer,
|
|
FLMBYTE * pucNewBuffer,
|
|
FLMUINT uiCurrPacketLen,
|
|
FLMBOOL bStartingNewFile)
|
|
{
|
|
FLMUINT uiOldBufBytes = pBuffer->uiRflBufBytes;
|
|
|
|
// If we will be starting a new file, no need to keep any of
|
|
// what is in the buffer. Only the current packet needs to
|
|
// be copied - at the beginning of the buffer.
|
|
|
|
// OTHERWISE:
|
|
|
|
// If there are fewer than 512 bytes in the buffer, we simply
|
|
// keep them and keep appending to the buffer the next time
|
|
// we output stuff. The beginning of the buffer must ALWAYS be
|
|
// a 512 byte boundary in the file, because we want to always
|
|
// do our writing on 512 byte boundaries - because of direct IO.
|
|
|
|
// If the number of bytes in the buffer is over 512 and it is
|
|
// evenly divisible by 512, we can clear the buffer. Otherwise,
|
|
// we want to move the extra bytes over the last 512 byte boundary
|
|
// down to the beginning of the buffer and adjust the buffer bytes
|
|
// to reflect just these left-over bytes.
|
|
|
|
if (bStartingNewFile)
|
|
{
|
|
pBuffer->uiRflBufBytes = 0;
|
|
pBuffer->uiRflFileOffset = 512;
|
|
}
|
|
else if (pBuffer->uiRflBufBytes >= 512)
|
|
{
|
|
|
|
// See if the number of bytes in the buffer is an exact
|
|
// multiple of 512.
|
|
|
|
if (pBuffer->uiRflBufBytes & 511) // Not exact multiple
|
|
{
|
|
|
|
// Round m_uiRflBufBytes down to next 512 byte boundary
|
|
|
|
FLMUINT ui512Offset = ROUND_DOWN_TO_NEAREST_512(
|
|
pBuffer->uiRflBufBytes);
|
|
|
|
// Move all bytes above the nearest 512 byte boundary
|
|
// down to the beginning of the buffer and adjust
|
|
// pBuffer->uiRflBufBytes and
|
|
// pBuffer->uiRflFileOffset accordingly.
|
|
|
|
f_memcpy( pucNewBuffer, &pucOldBuffer[ui512Offset],
|
|
pBuffer->uiRflBufBytes - ui512Offset);
|
|
pBuffer->uiRflBufBytes -= ui512Offset;
|
|
pBuffer->uiRflFileOffset += ui512Offset;
|
|
}
|
|
else
|
|
{
|
|
pBuffer->uiRflFileOffset += pBuffer->uiRflBufBytes;
|
|
pBuffer->uiRflBufBytes = 0;
|
|
}
|
|
}
|
|
else if (pucNewBuffer != pucOldBuffer)
|
|
{
|
|
f_memcpy( pucNewBuffer, pucOldBuffer, pBuffer->uiRflBufBytes);
|
|
}
|
|
if (uiCurrPacketLen)
|
|
{
|
|
flmAssert( uiOldBufBytes + uiCurrPacketLen <= m_uiBufferSize);
|
|
f_memmove( &pucNewBuffer [pBuffer->uiRflBufBytes],
|
|
&pucOldBuffer [uiOldBufBytes],
|
|
uiCurrPacketLen);
|
|
}
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Flush the RFL data from the buffer to disk.
|
|
*********************************************************************/
|
|
RCODE F_Rfl::flush(
|
|
RFL_BUFFER * pBuffer,
|
|
FLMBOOL bFinalWrite,
|
|
FLMUINT uiCurrPacketLen,
|
|
FLMBOOL bStartingNewFile
|
|
)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FLMUINT uiBytesWritten;
|
|
F_IOBuffer * pNewBuffer;
|
|
F_IOBuffer * pAsyncBuf = NULL;
|
|
FLMBYTE * pucOldBuffer;
|
|
FLMUINT uiFileOffset;
|
|
FLMUINT uiBufBytes;
|
|
|
|
if (m_pFileHdl && pBuffer->uiRflBufBytes)
|
|
{
|
|
|
|
// Must wait for stuff in committing buffer, if any, before
|
|
// going ahead here.
|
|
|
|
if (pBuffer != m_pCommitBuf)
|
|
{
|
|
if (RC_BAD( rc = waitForCommit()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
if (m_uiRflWriteBufs > 1 && m_pFileHdl->CanDoAsync())
|
|
{
|
|
pAsyncBuf = pBuffer->pIOBuffer;
|
|
}
|
|
|
|
if ((FLMUINT)(-1) - pBuffer->uiRflFileOffset <=
|
|
pBuffer->uiRflBufBytes)
|
|
{
|
|
rc = RC_SET( FERR_DB_FULL);
|
|
goto Exit;
|
|
}
|
|
|
|
pucOldBuffer = pBuffer->pIOBuffer->m_pucBuffer;
|
|
uiFileOffset = pBuffer->uiRflFileOffset;
|
|
uiBufBytes = pBuffer->uiRflBufBytes;
|
|
if (m_uiRflWriteBufs > 1)
|
|
{
|
|
if( RC_BAD( rc = pBuffer->pBufferMgr->getBuffer(
|
|
&pNewBuffer,
|
|
m_uiBufferSize, m_uiBufferSize)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// No need to copy data if it is the final write,
|
|
// because it won't be reused anyway - the data for
|
|
// the next transaction has already been copied to
|
|
// another buffer.
|
|
|
|
if (!bFinalWrite)
|
|
{
|
|
copyLastSector( pBuffer, pucOldBuffer, pNewBuffer->m_pucBuffer,
|
|
uiCurrPacketLen, bStartingNewFile);
|
|
}
|
|
}
|
|
|
|
rc = m_pFileHdl->SectorWrite( uiFileOffset, uiBufBytes,
|
|
pucOldBuffer,
|
|
m_uiBufferSize, pAsyncBuf,
|
|
&uiBytesWritten, FALSE);
|
|
if( m_uiRflWriteBufs == 1)
|
|
{
|
|
|
|
// We are counting on the fact that the write completed.
|
|
// When we only have one buffer, we cannot do async
|
|
// writes.
|
|
|
|
flmAssert( !pAsyncBuf);
|
|
if (RC_OK( rc) && !bFinalWrite)
|
|
{
|
|
copyLastSector( pBuffer, pucOldBuffer, pucOldBuffer,
|
|
uiCurrPacketLen, bStartingNewFile);
|
|
}
|
|
|
|
// DO NOT call notifyComplete - that would put
|
|
// pBuffer->pIOBuffer into the avail list, and we
|
|
// don't want that. We simply want to keep
|
|
// reusing it.
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
// No need to call copyLastSector, because it was called
|
|
// above before calling SectorWrite. The part of the
|
|
// old buffer that needs to be transferred to the new
|
|
// buffer has already been transferred.
|
|
|
|
if (!pAsyncBuf)
|
|
{
|
|
pBuffer->pIOBuffer->notifyComplete( rc);
|
|
}
|
|
pBuffer->pIOBuffer = pNewBuffer;
|
|
}
|
|
|
|
if (RC_BAD( rc))
|
|
{
|
|
// Remap disk full error
|
|
|
|
if (rc == FERR_IO_DISK_FULL)
|
|
{
|
|
rc = RC_SET( FERR_RFL_DEVICE_FULL);
|
|
m_bRflVolumeFull = TRUE;
|
|
}
|
|
m_bRflVolumeOk = FALSE;
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Switch buffers. This routine assumes the m_hBufMutex is locked.
|
|
*********************************************************************/
|
|
void F_Rfl::switchBuffers( void)
|
|
{
|
|
RFL_BUFFER * pOldBuffer = m_pCurrentBuf;
|
|
|
|
if (m_pCurrentBuf == &m_Buf1)
|
|
{
|
|
m_pCurrentBuf = &m_Buf2;
|
|
}
|
|
else
|
|
{
|
|
m_pCurrentBuf = &m_Buf1;
|
|
}
|
|
m_pCurrentBuf->bTransInProgress = pOldBuffer->bTransInProgress;
|
|
m_pCurrentBuf->uiCurrFileNum = pOldBuffer->uiCurrFileNum;
|
|
m_pCurrentBuf->uiRflBufBytes = pOldBuffer->uiRflBufBytes;
|
|
m_pCurrentBuf->uiRflFileOffset = pOldBuffer->uiRflFileOffset;
|
|
if (pOldBuffer->uiRflBufBytes)
|
|
{
|
|
copyLastSector( m_pCurrentBuf, pOldBuffer->pIOBuffer->m_pucBuffer,
|
|
m_pCurrentBuf->pIOBuffer->m_pucBuffer, 0, FALSE);
|
|
}
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Wait for all RFL transaction writes to be finished. The caller
|
|
has the write lock on the database, which will prevent further
|
|
writes to the RFL.
|
|
*********************************************************************/
|
|
FLMBOOL F_Rfl::seeIfRflWritesDone(
|
|
FLMBOOL bForceWait
|
|
)
|
|
{
|
|
FLMBOOL bWritesDone;
|
|
|
|
f_mutexLock( m_hBufMutex);
|
|
|
|
if (!bForceWait)
|
|
{
|
|
bWritesDone = (FLMBOOL)((m_pCurrentBuf->pFirstWaiter || m_pCommitBuf)
|
|
? FALSE
|
|
: TRUE);
|
|
f_mutexUnlock( m_hBufMutex);
|
|
}
|
|
else
|
|
{
|
|
|
|
// If the current buffer has a waiter, add self to that list
|
|
// to wait, because it will be notified after the commit buffer
|
|
// has been notified. Otherwise, if there is a commit in
|
|
// progress, add self to that list to wait.
|
|
|
|
if (m_pCurrentBuf->pFirstWaiter)
|
|
{
|
|
|
|
// If bTransInProgress is TRUE and m_pCommitBuf is NULL
|
|
// then this thread is the current transaction, and
|
|
// nobody is going to wake up the first waiter until we
|
|
// are done! Hence, we must wake him up.
|
|
|
|
if (!m_pCommitBuf)
|
|
{
|
|
|
|
// If m_pCommitBuf is NULL, this could only be possible if
|
|
// there is a transaction in progress. Otherwise, there
|
|
// would not have been a pFirstWaiter, because when
|
|
// the commit buffer finishes writing, if there is a
|
|
// waiter, it will set commitbuf=currentbuf if there
|
|
// is no transaction active.
|
|
|
|
flmAssert( m_pCurrentBuf->bTransInProgress);
|
|
|
|
m_pCommitBuf = m_pCurrentBuf;
|
|
switchBuffers();
|
|
wakeUpWaiter( FERR_OK, TRUE);
|
|
(void)waitForWrites( m_pCommitBuf, FALSE);
|
|
}
|
|
else
|
|
{
|
|
FLMBOOL bSaveTransInProgress = m_pCurrentBuf->bTransInProgress;
|
|
|
|
// Must set bTransInProgress to FALSE so that when the writer
|
|
// of m_pCommitBuf finishes, it will signal the first waiter
|
|
// on m_pCurrentBuf. If we don't do this, m_pCommitBuf will
|
|
// simply be set to NULL, and the first waiter will never
|
|
// be woke up.
|
|
|
|
m_pCurrentBuf->bTransInProgress = FALSE;
|
|
(void)waitForWrites( m_pCurrentBuf, FALSE);
|
|
|
|
// It is OK to restore the trans in progress flag to what it
|
|
// was before, because whoever called this routine has a lock
|
|
// on the database, and it is his trans-in-progress state
|
|
// that should be preserved. No other thread will have been
|
|
// able to change that state because the database is locked.
|
|
|
|
f_mutexLock( m_hBufMutex);
|
|
m_pCurrentBuf->bTransInProgress = bSaveTransInProgress;
|
|
f_mutexUnlock( m_hBufMutex);
|
|
}
|
|
}
|
|
else if (m_pCommitBuf)
|
|
{
|
|
(void)waitForWrites( m_pCommitBuf, FALSE);
|
|
}
|
|
else
|
|
{
|
|
f_mutexUnlock( m_hBufMutex);
|
|
}
|
|
bWritesDone = TRUE;
|
|
}
|
|
return( bWritesDone);
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Wake up the first thread that is waiting on the commit buffer.
|
|
*********************************************************************/
|
|
void F_Rfl::wakeUpWaiter(
|
|
RCODE rc,
|
|
FLMBOOL bIsWriter // Only used for debug
|
|
)
|
|
{
|
|
F_SEM hESem;
|
|
|
|
#ifndef FLM_DEBUG
|
|
F_UNREFERENCED_PARM( bIsWriter);
|
|
#else
|
|
if (bIsWriter)
|
|
{
|
|
flmAssert( m_pCommitBuf->pFirstWaiter->bIsWriter);
|
|
}
|
|
else
|
|
{
|
|
flmAssert( !m_pCommitBuf->pFirstWaiter->bIsWriter);
|
|
}
|
|
#endif
|
|
|
|
*(m_pCommitBuf->pFirstWaiter->pRc) = rc;
|
|
hESem = m_pCommitBuf->pFirstWaiter->hESem;
|
|
if ((m_pCommitBuf->pFirstWaiter =
|
|
m_pCommitBuf->pFirstWaiter->pNext) == NULL)
|
|
{
|
|
m_pCommitBuf->pLastWaiter = NULL;
|
|
}
|
|
f_semSignal( hESem);
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Wait for the transaction writes to be finished.
|
|
*********************************************************************/
|
|
RCODE F_Rfl::completeTransWrites(
|
|
FDB * pDb,
|
|
FLMBOOL bCommitting,
|
|
FLMBOOL bOkToUnlock
|
|
)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
RCODE tmpRc;
|
|
FLMBOOL bMutexLocked = FALSE;
|
|
FLMBOOL bNotifyWaiters = FALSE;
|
|
FLMBOOL bDbUnlocked = FALSE;
|
|
DB_STATS * pDbStats = NULL;
|
|
F_TMSTAMP StartTime;
|
|
|
|
f_mutexLock( m_hBufMutex);
|
|
bMutexLocked = TRUE;
|
|
m_pCurrentBuf->bTransInProgress = FALSE;
|
|
|
|
flmAssert( pDb->uiFlags & FDB_HAS_WRITE_LOCK);
|
|
|
|
// If we are not logging, we are probably recovering or restoring
|
|
// the database. All we need to do in this case is write out the
|
|
// log header.
|
|
|
|
if (pDb->uiFlags & FDB_REPLAYING_RFL)
|
|
{
|
|
if (pDb->bHadUpdOper && m_pCurrentBuf->bOkToWriteHdrs)
|
|
{
|
|
f_mutexUnlock( m_hBufMutex);
|
|
bMutexLocked = FALSE;
|
|
if (RC_BAD( rc = flmWriteLogHdr( pDb->pDbStats,
|
|
pDb->pSFileHdl, pDb->pFile,
|
|
m_pCurrentBuf->ucLogHdr,
|
|
m_pCurrentBuf->ucCPHdr, FALSE)))
|
|
{
|
|
flmSetMustCloseFlags( pDb->pFile, rc, FALSE);
|
|
}
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
// Handle empty transactions differently.
|
|
// These transactions should not do any writing and do not need to
|
|
// wait for all writes to complete, unless the bOkToUnlock flag
|
|
// is set to FALSE. In that case they must wait for all writes
|
|
// to complete before unlocking.
|
|
|
|
if (!pDb->bHadUpdOper)
|
|
{
|
|
|
|
// If the current buffer has a waiter, add self to that list
|
|
// to wait, because it will be notified after the commit buffer
|
|
// has been notified. Otherwise, if there is a commit in
|
|
// progress, add self to that list to wait.
|
|
|
|
if (m_pCurrentBuf->pFirstWaiter)
|
|
{
|
|
|
|
// If m_pCommitBuf is NULL then nobody is going to wake up
|
|
// the first waiter - we must do it.
|
|
|
|
if (!m_pCommitBuf)
|
|
{
|
|
if (bOkToUnlock)
|
|
{
|
|
flmUnlinkDbFromTrans( pDb, bCommitting);
|
|
bDbUnlocked = TRUE;
|
|
}
|
|
m_pCommitBuf = m_pCurrentBuf;
|
|
switchBuffers();
|
|
wakeUpWaiter( FERR_OK, TRUE);
|
|
|
|
if (!bOkToUnlock)
|
|
{
|
|
bMutexLocked = FALSE;
|
|
(void)waitForWrites( m_pCommitBuf, FALSE);
|
|
}
|
|
}
|
|
else if (!bOkToUnlock)
|
|
{
|
|
bMutexLocked = FALSE;
|
|
(void)waitForWrites( m_pCurrentBuf, FALSE);
|
|
}
|
|
}
|
|
else if (m_pCommitBuf)
|
|
{
|
|
if (!bOkToUnlock)
|
|
{
|
|
bMutexLocked = FALSE;
|
|
rc = waitForWrites( m_pCommitBuf, FALSE);
|
|
}
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
// If there is a transaction committing, put self into
|
|
// the wait list on the current buffer. When the committer
|
|
// finishes, he will wake up the first thread in the list
|
|
// and that thread will commit the buffer.
|
|
|
|
if (m_pCommitBuf)
|
|
{
|
|
FLMBOOL bIsWriter;
|
|
|
|
// Another thread has to be doing the writes to m_pCommitBuf,
|
|
// which means that m_pCurrentBuf better not be equal to
|
|
// m_pCommitBuf.
|
|
|
|
flmAssert( m_pCommitBuf != m_pCurrentBuf);
|
|
|
|
// If there are no waiters, we are the first one, so when
|
|
// we get signaled, we should proceed and do the write.
|
|
|
|
bIsWriter = m_pCurrentBuf->pFirstWaiter ? FALSE : TRUE;
|
|
if (bOkToUnlock)
|
|
{
|
|
flmUnlinkDbFromTrans( pDb, bCommitting);
|
|
bDbUnlocked = TRUE;
|
|
}
|
|
bMutexLocked = FALSE;
|
|
rc = waitForWrites( m_pCurrentBuf, bIsWriter);
|
|
|
|
// If we were the first one in the queue, we must now
|
|
// do the write.
|
|
|
|
if (!bIsWriter)
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// First one in the queue, fall through to do the write.
|
|
|
|
// The thread that woke me up better have set m_pCommitBuf
|
|
// See below.
|
|
|
|
flmAssert( m_pCommitBuf);
|
|
}
|
|
else if (m_pCurrentBuf->pFirstWaiter)
|
|
{
|
|
|
|
// Another thread is ready to commit the next set of
|
|
// buffers, but just needs to be woke up.
|
|
|
|
if (bOkToUnlock)
|
|
{
|
|
flmUnlinkDbFromTrans( pDb, bCommitting);
|
|
bDbUnlocked = TRUE;
|
|
}
|
|
|
|
// Need to set things up for that first waiter and get him
|
|
// going.
|
|
|
|
m_pCommitBuf = m_pCurrentBuf;
|
|
switchBuffers();
|
|
wakeUpWaiter( rc, TRUE);
|
|
|
|
// Wait for the write to be completed.
|
|
|
|
bMutexLocked = FALSE;
|
|
rc = waitForWrites( m_pCommitBuf, FALSE);
|
|
goto Exit;
|
|
}
|
|
else
|
|
{
|
|
m_pCommitBuf = m_pCurrentBuf;
|
|
switchBuffers();
|
|
if (bOkToUnlock)
|
|
{
|
|
flmUnlinkDbFromTrans( pDb, bCommitting);
|
|
bDbUnlocked = TRUE;
|
|
}
|
|
f_mutexUnlock( m_hBufMutex);
|
|
bMutexLocked = FALSE;
|
|
}
|
|
|
|
// NOTE: From this point on we use tmpRc because we don't want to
|
|
// lose the rc that may have been set above in the call to
|
|
// waitForWrites
|
|
|
|
// At this point the mutex better not be locked.
|
|
|
|
flmAssert( !bMutexLocked);
|
|
bNotifyWaiters = TRUE;
|
|
|
|
if( (pDbStats = pDb->pDbStats) != NULL)
|
|
{
|
|
f_timeGetTimeStamp( &StartTime);
|
|
}
|
|
|
|
// Must write out whatever we have in the commit buffer before
|
|
// unlocking the database.
|
|
|
|
if (RC_BAD( tmpRc = flush( m_pCommitBuf, TRUE)))
|
|
{
|
|
if (RC_OK( rc))
|
|
{
|
|
rc = tmpRc;
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
// Wait for any pending IO off of the log buffer
|
|
|
|
if (RC_BAD( tmpRc = m_pCommitBuf->pBufferMgr->waitForAllPendingIO()))
|
|
{
|
|
if (RC_OK( rc))
|
|
{
|
|
rc = tmpRc;
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
// Force the RFL writes to disk if necessary.
|
|
// NOTE: It is possible for m_pFileHdl to be NULL at this point if
|
|
// there were no operations actually logged. This happens in
|
|
// FlmDbUpgrade (see flconvrt.cpp). Even though nothing was logged
|
|
// the transaction is not an empty transaction, because it still needs
|
|
// to write out the log header.
|
|
|
|
if (m_pFileHdl)
|
|
{
|
|
if (RC_BAD( tmpRc = m_pFileHdl->Flush()))
|
|
{
|
|
|
|
// Remap disk full error
|
|
|
|
if (tmpRc == FERR_IO_DISK_FULL)
|
|
{
|
|
rc = RC_SET( FERR_RFL_DEVICE_FULL);
|
|
m_bRflVolumeFull = TRUE;
|
|
}
|
|
else if (RC_OK( rc))
|
|
{
|
|
rc = tmpRc;
|
|
}
|
|
m_bRflVolumeOk = FALSE;
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Write the log header
|
|
|
|
if (m_pCommitBuf->bOkToWriteHdrs)
|
|
{
|
|
if (RC_BAD( tmpRc = flmWriteLogHdr( pDb->pDbStats,
|
|
pDb->pSFileHdl, pDb->pFile,
|
|
m_pCommitBuf->ucLogHdr,
|
|
m_pCommitBuf->ucCPHdr, FALSE)))
|
|
{
|
|
if (RC_OK( rc))
|
|
{
|
|
rc = tmpRc;
|
|
}
|
|
flmSetMustCloseFlags( pDb->pFile, tmpRc, FALSE);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
if (!bDbUnlocked && bOkToUnlock)
|
|
{
|
|
flmUnlinkDbFromTrans( pDb, bCommitting);
|
|
}
|
|
|
|
if (bNotifyWaiters)
|
|
{
|
|
FLMUINT uiNumFinished = 1; // For self
|
|
|
|
flmAssert( !bMutexLocked);
|
|
f_mutexLock( m_hBufMutex);
|
|
bMutexLocked = TRUE;
|
|
|
|
// Wake up any waiters
|
|
|
|
while (m_pCommitBuf->pFirstWaiter)
|
|
{
|
|
uiNumFinished++;
|
|
wakeUpWaiter( rc, FALSE);
|
|
}
|
|
|
|
// If there are waiters on the current buffer, the first one
|
|
// should be woke up so it can start the next set of writes.
|
|
|
|
if (m_pCurrentBuf->pFirstWaiter && !m_pCurrentBuf->bTransInProgress)
|
|
{
|
|
flmAssert( m_pCurrentBuf != m_pCommitBuf);
|
|
m_pCommitBuf = m_pCurrentBuf;
|
|
switchBuffers();
|
|
wakeUpWaiter( rc, TRUE);
|
|
}
|
|
else
|
|
{
|
|
m_pCommitBuf = NULL;
|
|
}
|
|
if (pDbStats)
|
|
{
|
|
flmAddElapTime( &StartTime,
|
|
&pDbStats->UpdateTransStats.GroupCompletes.ui64ElapMilli);
|
|
pDbStats->UpdateTransStats.GroupCompletes.ui64Count++;
|
|
pDbStats->bHaveStats = TRUE;
|
|
pDbStats->UpdateTransStats.ui64GroupFinished += uiNumFinished;
|
|
}
|
|
}
|
|
|
|
if (bMutexLocked)
|
|
{
|
|
f_mutexUnlock( m_hBufMutex);
|
|
}
|
|
return( rc);
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Calculate the checksum for a packet.
|
|
*********************************************************************/
|
|
FLMBYTE RflCalcChecksum(
|
|
const FLMBYTE * pucPacket,
|
|
FLMUINT uiPacketBodyLen)
|
|
{
|
|
FLMUINT uiBytesToChecksum;
|
|
FLMUINT uiChecksum = 0;
|
|
FLMBYTE ucTmp;
|
|
const FLMBYTE * pucStart;
|
|
const FLMBYTE * pucEnd;
|
|
const FLMBYTE * pucSectionEnd;
|
|
const FLMBYTE * pucCur;
|
|
|
|
// Checksum is calculated for every byte in the packet that
|
|
// comes after the checksum byte.
|
|
|
|
pucStart = &pucPacket[ RFL_PACKET_CHECKSUM_OFFSET + 1];
|
|
uiBytesToChecksum = (FLMUINT)(uiPacketBodyLen +
|
|
RFL_PACKET_OVERHEAD -
|
|
(RFL_PACKET_CHECKSUM_OFFSET + 1));
|
|
|
|
pucCur = pucStart;
|
|
pucEnd = pucStart + uiBytesToChecksum;
|
|
|
|
#ifdef FLM_64BIT
|
|
pucSectionEnd = pucStart + (sizeof( FLMUINT) - ((FLMUINT)pucStart & 0x7));
|
|
#else
|
|
pucSectionEnd = pucStart + (sizeof( FLMUINT) - ((FLMUINT)pucStart & 0x3));
|
|
#endif
|
|
|
|
if( pucSectionEnd > pucEnd)
|
|
{
|
|
pucSectionEnd = pucEnd;
|
|
}
|
|
|
|
while( pucCur < pucSectionEnd)
|
|
{
|
|
uiChecksum = (uiChecksum << 8) + *pucCur++;
|
|
}
|
|
|
|
#ifdef FLM_64BIT
|
|
pucSectionEnd = (FLMBYTE *)((FLMUINT)pucEnd & 0xFFFFFFFFFFFFFFF8);
|
|
#else
|
|
pucSectionEnd = (FLMBYTE *)((FLMUINT)pucEnd & 0xFFFFFFFC);
|
|
#endif
|
|
|
|
while( pucCur < pucSectionEnd)
|
|
{
|
|
uiChecksum ^= *((FLMUINT *)pucCur);
|
|
pucCur += sizeof( FLMUINT);
|
|
}
|
|
|
|
while( pucCur < pucEnd)
|
|
{
|
|
uiChecksum ^= *pucCur++;
|
|
}
|
|
|
|
ucTmp = (FLMBYTE)uiChecksum;
|
|
|
|
uiChecksum >>= 8;
|
|
ucTmp ^= (FLMBYTE)uiChecksum;
|
|
|
|
uiChecksum >>= 8;
|
|
ucTmp ^= (FLMBYTE)uiChecksum;
|
|
|
|
#ifdef FLM_64BIT
|
|
uiChecksum >>= 8;
|
|
ucTmp ^= (FLMBYTE)uiChecksum;
|
|
|
|
uiChecksum >>= 8;
|
|
ucTmp ^= (FLMBYTE)uiChecksum;
|
|
|
|
uiChecksum >>= 8;
|
|
ucTmp ^= (FLMBYTE)uiChecksum;
|
|
|
|
uiChecksum >>= 8;
|
|
ucTmp ^= (FLMBYTE)uiChecksum;
|
|
#endif
|
|
|
|
ucTmp ^= (FLMBYTE)(uiChecksum >> 8);
|
|
uiChecksum = ucTmp;
|
|
|
|
if( (uiChecksum = ucTmp) == 0)
|
|
{
|
|
uiChecksum = 1;
|
|
}
|
|
|
|
return( (FLMBYTE)uiChecksum);
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Flush all completed packets out of the RFL buffer, and shift
|
|
the new partial packet down. This guarantees that there is
|
|
now room in the buffer for the maximum packet size.
|
|
*********************************************************************/
|
|
RCODE F_Rfl::shiftPacketsDown(
|
|
FLMUINT uiCurrPacketLen,
|
|
FLMBOOL bStartingNewFile
|
|
)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
|
|
// The call to flush will move whatever needs to be moved from
|
|
// the current buffer into a new buffer if multiple buffers are
|
|
// being used. If only one buffer is being used, it will move
|
|
// the part of the packet that needs to be moved down to the
|
|
// beginning of the buffer - AFTER writing out the buffer.
|
|
|
|
if (RC_BAD( rc = flush( m_pCurrentBuf, FALSE,
|
|
uiCurrPacketLen, bStartingNewFile)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// NOTE: If multiple buffers are being used, whatever was moved
|
|
// to the new buffer has not yet been written out
|
|
|
|
if (bStartingNewFile)
|
|
{
|
|
if( RC_BAD( rc = waitPendingWrites()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Determine if we should start a new file. If we are over the
|
|
low limit, and the bDoNewIfOverLowLimit flag is set, we
|
|
will start a new log file. Or, if this packet size would
|
|
put us over the upper limit, we will start a new log file.
|
|
*********************************************************************/
|
|
RCODE F_Rfl::seeIfNeedNewFile(
|
|
FLMUINT uiPacketLen,
|
|
FLMBOOL bDoNewIfOverLowLimit
|
|
)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FLMBYTE ucNextSerialNum [F_SERIAL_NUM_SIZE];
|
|
|
|
flmAssert( m_pFile);
|
|
|
|
// If the keep files flag is FALSE, we won't start
|
|
// a new file. NOTE: This should ALWAYS be false
|
|
// for pre 4.3 databases.
|
|
|
|
if (!m_bKeepRflFiles)
|
|
{
|
|
goto Exit; // Should return FERR_OK;
|
|
}
|
|
|
|
// VERY IMPORTANT NOTE: It is preferrable that we keep transactions
|
|
// entirely contained in the same RFL file if at all possible. Note
|
|
// that it is NOT a hard and fast requirement. The system will work
|
|
// just fine if we don't. However, it would be nice if RFL files
|
|
// always ended with a commit or abort packet. This preferences is
|
|
// due to what happens after a restore operation. After a restore
|
|
// operation, we always need to start a new RFL file, but if possible,
|
|
// we would like that new RFL file to be the next one in the sequence
|
|
// after the last RFL file that was restored. We can only do this if
|
|
// we were able to restore EVERY transaction that was in the last
|
|
// restored RFL file - which we can only do if the last restored RFL
|
|
// file ended with a commit or abort packet.
|
|
// To accomplish this end, we try to roll to new files on the first
|
|
// transaction begin packet that occurs after we have exceeded our
|
|
// low threshold - which is why bDoNewIfOverLowLimit is only set to
|
|
// TRUE on transaction begin packets. It is set to FALSE on other
|
|
// packets so that we will continue logging the transaction in the
|
|
// same file that we started the transaction in - if possible. The
|
|
// only thing that will cause a non-transaction-begin packet to roll
|
|
// to a new file is if we would exceed the high limit.
|
|
|
|
if ((bDoNewIfOverLowLimit &&
|
|
m_pCurrentBuf->uiRflFileOffset + m_pCurrentBuf->uiRflBufBytes >=
|
|
m_uiRflMinFileSize) ||
|
|
(m_pCurrentBuf->uiRflFileOffset + m_pCurrentBuf->uiRflBufBytes +
|
|
uiPacketLen >= m_uiRflMaxFileSize))
|
|
{
|
|
FLMUINT uiCurrFileEOF = m_pCurrentBuf->uiRflFileOffset +
|
|
m_pCurrentBuf->uiRflBufBytes;
|
|
|
|
// Shift the current packet to the beginning of the buffer.
|
|
// Any packets in the buffer before that one will be written
|
|
// out to the current file.
|
|
|
|
if (RC_BAD( rc = shiftPacketsDown( uiPacketLen, TRUE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Update the header of the current file and close it.
|
|
|
|
if (RC_BAD( rc = writeHeader( m_pCurrentBuf->uiCurrFileNum,
|
|
uiCurrFileEOF,
|
|
m_ucCurrSerialNum, m_ucNextSerialNum, TRUE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Truncate the file.
|
|
|
|
if (!ON_512_BYTE_BOUNDARY( uiCurrFileEOF))
|
|
{
|
|
uiCurrFileEOF = ROUND_DOWN_TO_NEAREST_512( uiCurrFileEOF) + 512;
|
|
}
|
|
if (RC_BAD( rc = m_pFileHdl->Truncate( uiCurrFileEOF)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Close the file handle.
|
|
|
|
m_pFileHdl->Close();
|
|
m_pFileHdl->Release();
|
|
m_pFileHdl = NULL;
|
|
|
|
// Get the next serial number that will be used for the RFL
|
|
// file after this one.
|
|
|
|
if (RC_BAD( rc = f_createSerialNumber( ucNextSerialNum)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Create next file in the sequence.
|
|
|
|
// Use the next serial number stored in the FDB's log header
|
|
// for the serial number on this RFL file. Use the serial
|
|
// number we just generated as the next RFL serial number.
|
|
|
|
if (RC_BAD( rc = createFile( m_pCurrentBuf->uiCurrFileNum + 1,
|
|
m_ucNextSerialNum, ucNextSerialNum,
|
|
TRUE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Move the next serial number to the current serial number
|
|
// and the serial number we generated above into the next
|
|
// serial number.
|
|
|
|
f_memcpy( m_ucCurrSerialNum, m_ucNextSerialNum, F_SERIAL_NUM_SIZE);
|
|
f_memcpy( m_ucNextSerialNum, ucNextSerialNum, F_SERIAL_NUM_SIZE);
|
|
}
|
|
|
|
Exit:
|
|
return( rc);
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Finish the current RFL file - set up so that next
|
|
transaction will begin a new RFL file.
|
|
*********************************************************************/
|
|
RCODE F_Rfl::finishCurrFile(
|
|
FDB * pDb,
|
|
FLMBOOL bNewKeepState
|
|
)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FLMBOOL bDbLocked = FALSE;
|
|
FLMUINT uiTransFileNum;
|
|
FLMUINT uiTransOffset;
|
|
FLMUINT uiTruncateSize;
|
|
FLMBYTE * pucUncommittedLogHdr;
|
|
FLMBYTE ucCheckpointLogHdr[ LOG_HEADER_SIZE];
|
|
|
|
// Make sure we don't have a transaction going
|
|
|
|
if (pDb->uiTransType != FLM_NO_TRANS)
|
|
{
|
|
rc = RC_SET( FERR_TRANS_ACTIVE);
|
|
goto Exit;
|
|
}
|
|
|
|
// Make sure there is no active backup running
|
|
|
|
f_mutexLock( gv_FlmSysData.hShareMutex);
|
|
if (m_pFile->bBackupActive)
|
|
{
|
|
f_mutexUnlock( gv_FlmSysData.hShareMutex);
|
|
rc = RC_SET( FERR_BACKUP_ACTIVE);
|
|
goto Exit;
|
|
}
|
|
f_mutexUnlock( gv_FlmSysData.hShareMutex);
|
|
|
|
// Lock the database - need to prevent update
|
|
// transactions and checkpoint thread from running.
|
|
|
|
if (RC_BAD( rc = dbLock( pDb, FLM_NO_TIMEOUT)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
bDbLocked = TRUE;
|
|
|
|
// Must wait for all RFL writes before switching files.
|
|
|
|
(void)seeIfRflWritesDone( TRUE);
|
|
|
|
// Better not be in the middle of logging unknown stuff
|
|
// for the application
|
|
|
|
flmAssert( !m_bLoggingUnknown);
|
|
|
|
// Better not be in the middle of a transaction.
|
|
|
|
flmAssert( !m_uiCurrTransID);
|
|
|
|
// If DB version is less than 4.3 we cannot do this.
|
|
|
|
if (m_pFile->FileHdr.uiVersionNum < FLM_VER_4_3)
|
|
{
|
|
goto Exit; // Will return FERR_OK
|
|
}
|
|
|
|
pucUncommittedLogHdr = &m_pFile->ucUncommittedLogHdr [0];
|
|
|
|
// Don't want to copy last committed log header into
|
|
// uncommitted log header if bNewKeepState is TRUE because
|
|
// the caller has already done it, and has made modifications
|
|
// to the uncommitted log header that we don't want to lose.
|
|
|
|
if (!bNewKeepState)
|
|
{
|
|
f_memcpy( pucUncommittedLogHdr, m_pFile->ucLastCommittedLogHdr,
|
|
LOG_HEADER_SIZE);
|
|
|
|
// If we are in a no-keep state, but we were not told that
|
|
// we have a new keep state, we cannot roll to the next
|
|
// RFL file, because a checkpoint has not been done. This is
|
|
// not an error - it is just the case where FlmDbConfig was
|
|
// asked to roll to the next RFL file when the keep flag was
|
|
// still FALSE.
|
|
|
|
if (!pucUncommittedLogHdr [LOG_KEEP_RFL_FILES])
|
|
{
|
|
goto Exit; // Will return FERR_OK
|
|
}
|
|
}
|
|
|
|
// Get the last committed serial numbers from the file's log header
|
|
// buffer.
|
|
|
|
f_memcpy( m_ucCurrSerialNum,
|
|
&pucUncommittedLogHdr [LOG_LAST_TRANS_RFL_SERIAL_NUM],
|
|
F_SERIAL_NUM_SIZE);
|
|
f_memcpy( m_ucNextSerialNum,
|
|
&pucUncommittedLogHdr [LOG_RFL_NEXT_SERIAL_NUM],
|
|
F_SERIAL_NUM_SIZE);
|
|
uiTransFileNum =
|
|
(FLMUINT)FB2UD( &pucUncommittedLogHdr [LOG_RFL_FILE_NUM]);
|
|
uiTransOffset =
|
|
(FLMUINT)FB2UD( &pucUncommittedLogHdr [LOG_RFL_LAST_TRANS_OFFSET]);
|
|
|
|
// If the LOG_RFL_LAST_TRANS_OFFSET is zero, there is no need to go set
|
|
// up to go to the next file, because we are already poised to do so at
|
|
// the beginning of the next transaction. Just return if this is the
|
|
// case. Same for if the file does not exist.
|
|
|
|
if (!uiTransOffset)
|
|
{
|
|
if (!bNewKeepState)
|
|
{
|
|
goto Exit; // Will return FERR_OK
|
|
}
|
|
}
|
|
else if (RC_BAD( rc = openFile( uiTransFileNum, m_ucCurrSerialNum)))
|
|
{
|
|
if (rc == FERR_IO_PATH_NOT_FOUND || rc == FERR_IO_INVALID_PATH)
|
|
{
|
|
rc = FERR_OK;
|
|
if (!bNewKeepState)
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
// At this point, we know the file exists, so we will update
|
|
// its header and then update the log header. Note that we
|
|
// use the keep RFL state from the last committed log header,
|
|
// not the uncommitted log header - because it will contain
|
|
// the correct keep-state for the current RFL file.
|
|
|
|
if (RC_BAD( rc = writeHeader( m_pCurrentBuf->uiCurrFileNum, uiTransOffset,
|
|
m_ucCurrSerialNum, m_ucNextSerialNum,
|
|
m_pFile->ucLastCommittedLogHdr [LOG_KEEP_RFL_FILES]
|
|
? TRUE
|
|
: FALSE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Truncate the file down to its EOF size - the nearest 512 byte boundary.
|
|
|
|
uiTruncateSize = uiTransOffset;
|
|
if (!ON_512_BYTE_BOUNDARY( uiTruncateSize))
|
|
{
|
|
uiTruncateSize = ROUND_DOWN_TO_NEAREST_512( uiTruncateSize) + 512;
|
|
}
|
|
if (RC_BAD( rc = m_pFileHdl->Truncate( uiTruncateSize)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Close the file handle.
|
|
|
|
m_pFileHdl->Close();
|
|
m_pFileHdl->Release();
|
|
m_pFileHdl = NULL;
|
|
|
|
// Set things up in the log header to go to the next file when
|
|
// we begin the next transaction. NOTE: NO need to lock the
|
|
// mutex, because nobody but an update transaction looks at
|
|
// the uncommitted log header.
|
|
|
|
uiTransFileNum++;
|
|
UD2FBA( (FLMUINT32)uiTransFileNum,
|
|
&pucUncommittedLogHdr [LOG_RFL_FILE_NUM]);
|
|
}
|
|
|
|
// Generate a new current serial number if bNewKeepState is
|
|
// TRUE. Otherwise, move the next serial number into the current
|
|
// serial number.
|
|
|
|
if (bNewKeepState)
|
|
{
|
|
if (RC_BAD( rc = f_createSerialNumber( m_ucCurrSerialNum)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
f_memcpy( m_ucCurrSerialNum, m_ucNextSerialNum, F_SERIAL_NUM_SIZE);
|
|
}
|
|
|
|
// Always generate a new next serial number.
|
|
|
|
if (RC_BAD( rc = f_createSerialNumber( m_ucNextSerialNum)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Set transaction offset to zero. This will force the
|
|
// next RFL file to be created on the next transaction
|
|
// begin. It will be created even if it is already
|
|
// there.
|
|
|
|
UD2FBA( (FLMUINT32)0, &pucUncommittedLogHdr [LOG_RFL_LAST_TRANS_OFFSET]);
|
|
f_memcpy( &pucUncommittedLogHdr [LOG_LAST_TRANS_RFL_SERIAL_NUM],
|
|
m_ucCurrSerialNum, F_SERIAL_NUM_SIZE);
|
|
f_memcpy( &pucUncommittedLogHdr [LOG_RFL_NEXT_SERIAL_NUM],
|
|
m_ucNextSerialNum, F_SERIAL_NUM_SIZE);
|
|
|
|
// Set the CP file number and CP offset to point into the new file.
|
|
// The outer code (FlmDbConfig) has done a checkpoint and the database
|
|
// is still locked. We need to set these values here, otherwise
|
|
// if we crash before the next checkpoint, recovery will start in the
|
|
// old RFL file, causing an FERR_BAD_RFL_SERIAL_NUM to be returned when
|
|
// traversing from the old RFL file to the new RFL file.
|
|
// NOTE: These changes must be made to the uncommitted log header AND
|
|
// the CP log header (so that they will be written out even
|
|
// though we are not forcing a checkpoint).
|
|
|
|
if (bNewKeepState)
|
|
{
|
|
#ifdef FLM_DEBUG
|
|
// Do a quick check to see if it looks like we are in a
|
|
// checkpointed state
|
|
|
|
if( !m_pFile->ucLastCommittedLogHdr[ LOG_KEEP_RFL_FILES] &&
|
|
(FLMUINT)FB2UD( &m_pFile->ucLastCommittedLogHdr[
|
|
LOG_RFL_LAST_TRANS_OFFSET]) > 512)
|
|
{
|
|
flmAssert( 0);
|
|
}
|
|
#endif
|
|
|
|
f_memcpy( ucCheckpointLogHdr,
|
|
m_pFile->ucCheckpointLogHdr, LOG_HEADER_SIZE);
|
|
UD2FBA( (FLMUINT32)uiTransFileNum,
|
|
&ucCheckpointLogHdr [LOG_RFL_LAST_CP_FILE_NUM]);
|
|
UD2FBA( (FLMUINT32)uiTransFileNum,
|
|
&pucUncommittedLogHdr [LOG_RFL_LAST_CP_FILE_NUM]);
|
|
UD2FBA( (FLMUINT32)512,
|
|
&ucCheckpointLogHdr [LOG_RFL_LAST_CP_OFFSET]);
|
|
UD2FBA( (FLMUINT32)512,
|
|
&pucUncommittedLogHdr [LOG_RFL_LAST_CP_OFFSET]);
|
|
}
|
|
|
|
// Write out the log header to disk.
|
|
|
|
if (RC_BAD( rc = flmWriteLogHdr( pDb->pDbStats, pDb->pSFileHdl,
|
|
m_pFile, pucUncommittedLogHdr,
|
|
bNewKeepState
|
|
? ucCheckpointLogHdr
|
|
: m_pFile->ucCheckpointLogHdr, FALSE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Copy the uncommitted log header back to the committed log header and
|
|
// copy the CP log header (if changed above).
|
|
|
|
f_mutexLock( gv_FlmSysData.hShareMutex);
|
|
f_memcpy( m_pFile->ucLastCommittedLogHdr, pucUncommittedLogHdr,
|
|
LOG_HEADER_SIZE);
|
|
|
|
if( bNewKeepState)
|
|
{
|
|
f_memcpy( m_pFile->ucCheckpointLogHdr,
|
|
ucCheckpointLogHdr, LOG_HEADER_SIZE);
|
|
}
|
|
f_mutexUnlock( gv_FlmSysData.hShareMutex);
|
|
|
|
Exit:
|
|
|
|
if (bDbLocked)
|
|
{
|
|
(void)dbUnlock( pDb);
|
|
}
|
|
return( rc);
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Finish packet by outputting header information for it.
|
|
*********************************************************************/
|
|
RCODE F_Rfl::finishPacket(
|
|
FLMUINT uiPacketType,
|
|
FLMUINT uiPacketBodyLen,
|
|
FLMBOOL bDoNewIfOverLowLimit
|
|
)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FLMUINT uiEncryptPacketBodyLen;
|
|
FLMUINT uiPacketLen;
|
|
FLMBYTE * pucPacket;
|
|
|
|
// Encrypt the packet body, if requested.
|
|
|
|
uiEncryptPacketBodyLen = getEncryptPacketBodyLen( uiPacketType,
|
|
uiPacketBodyLen);
|
|
uiPacketLen = uiEncryptPacketBodyLen + RFL_PACKET_OVERHEAD;
|
|
|
|
// See if this packet will cause us to overflow the limits on
|
|
// the current file. If so, create a new file.
|
|
|
|
if (RC_BAD( rc = seeIfNeedNewFile( uiPacketLen, bDoNewIfOverLowLimit)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Get a pointer to packet header.
|
|
|
|
pucPacket = &(m_pCurrentBuf->pIOBuffer->m_pucBuffer[
|
|
m_pCurrentBuf->uiRflBufBytes]);
|
|
|
|
// Set the packet address in the packet header.
|
|
|
|
m_uiPacketAddress = m_pCurrentBuf->uiRflFileOffset +
|
|
m_pCurrentBuf->uiRflBufBytes;
|
|
UD2FBA( (FLMUINT32)m_uiPacketAddress, &pucPacket [RFL_PACKET_ADDRESS_OFFSET]);
|
|
|
|
// Set the packet type and packet body length.
|
|
|
|
pucPacket [RFL_PACKET_TYPE_OFFSET] = (FLMBYTE)uiPacketType;
|
|
UW2FBA( (FLMUINT16)uiPacketBodyLen,
|
|
&pucPacket [RFL_PACKET_BODY_LENGTH_OFFSET]);
|
|
|
|
// Set the checksum for the packet.
|
|
|
|
pucPacket [RFL_PACKET_CHECKSUM_OFFSET] = RflCalcChecksum( pucPacket,
|
|
uiEncryptPacketBodyLen);
|
|
|
|
// Increment bytes in the buffer to reflect the fact that this packet
|
|
// is now complete.
|
|
|
|
m_pCurrentBuf->uiRflBufBytes += uiPacketLen;
|
|
Exit:
|
|
return( rc);
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Truncate roll-forward log file to a certain size - only
|
|
do if not keeping RFL files.
|
|
*********************************************************************/
|
|
RCODE F_Rfl::truncate(
|
|
FLMUINT uiTruncateSize
|
|
)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FLMUINT uiFileNum;
|
|
|
|
flmAssert( uiTruncateSize >= 512);
|
|
|
|
// Keeping of log files better not be enabled.
|
|
|
|
flmAssert( !m_pFile->ucLastCommittedLogHdr [LOG_KEEP_RFL_FILES]);
|
|
|
|
// Better not be in the middle of logging unknown stuff
|
|
// for the application
|
|
|
|
flmAssert( !m_bLoggingUnknown);
|
|
|
|
// Better not be in the middle of a transaction.
|
|
|
|
flmAssert( !m_uiCurrTransID);
|
|
|
|
// Open the current RFL file. If it does not exist, it is OK - there
|
|
// is nothing to truncate.
|
|
|
|
uiFileNum = (FLMUINT)FB2UD(
|
|
&m_pFile->ucLastCommittedLogHdr [LOG_RFL_FILE_NUM]);
|
|
if (RC_BAD( rc = openFile( uiFileNum,
|
|
&m_pFile->ucLastCommittedLogHdr [LOG_LAST_TRANS_RFL_SERIAL_NUM])))
|
|
{
|
|
if (rc == FERR_IO_PATH_NOT_FOUND || rc == FERR_IO_INVALID_PATH)
|
|
{
|
|
rc = FERR_OK;
|
|
}
|
|
goto Exit;
|
|
}
|
|
if (RC_BAD( rc = m_pFileHdl->Truncate( uiTruncateSize)))
|
|
{
|
|
m_bRflVolumeOk = FALSE;
|
|
goto Exit;
|
|
}
|
|
Exit:
|
|
return( rc);
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Setup to begin a transaction
|
|
*********************************************************************/
|
|
RCODE F_Rfl::setupTransaction( void)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FLMUINT uiFileNum;
|
|
FLMUINT uiLastTransOffset;
|
|
FLMBOOL bCreateFile;
|
|
|
|
f_mutexLock( m_hBufMutex);
|
|
m_pCurrentBuf->bTransInProgress = TRUE;
|
|
f_mutexUnlock( m_hBufMutex);
|
|
|
|
// Get the last committed serial numbers from the file's log header
|
|
// buffer.
|
|
|
|
f_memcpy( m_ucCurrSerialNum,
|
|
&m_pFile->ucLastCommittedLogHdr [LOG_LAST_TRANS_RFL_SERIAL_NUM],
|
|
F_SERIAL_NUM_SIZE);
|
|
f_memcpy( m_ucNextSerialNum,
|
|
&m_pFile->ucLastCommittedLogHdr [LOG_RFL_NEXT_SERIAL_NUM],
|
|
F_SERIAL_NUM_SIZE);
|
|
uiFileNum = (FLMUINT)FB2UD(
|
|
&m_pFile->ucLastCommittedLogHdr [LOG_RFL_FILE_NUM]);
|
|
uiLastTransOffset = (FLMUINT)FB2UD(
|
|
&m_pFile->ucLastCommittedLogHdr [LOG_RFL_LAST_TRANS_OFFSET]);
|
|
|
|
// If the LOG_RFL_LAST_TRANS_OFFSET is zero, we need to create the
|
|
// next RFL file number no matter what. There are two cases where
|
|
// this happens: 1) when the database is first created, and 2) after
|
|
// a restore operation.
|
|
|
|
if (!uiLastTransOffset)
|
|
{
|
|
bCreateFile = TRUE;
|
|
|
|
// Close the current file, just in case we had opened it before.
|
|
// At this point, it doesn't matter because we are going to
|
|
// overwrite it.
|
|
|
|
if (RC_BAD( rc = waitForCommit()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
closeFile();
|
|
}
|
|
else if (RC_BAD( rc = openFile( uiFileNum, m_ucCurrSerialNum)))
|
|
{
|
|
if (rc != FERR_IO_PATH_NOT_FOUND && rc != FERR_IO_INVALID_PATH)
|
|
{
|
|
goto Exit;
|
|
}
|
|
bCreateFile = TRUE;
|
|
}
|
|
else
|
|
{
|
|
bCreateFile = FALSE;
|
|
}
|
|
|
|
if (bCreateFile)
|
|
{
|
|
|
|
// If the log header indicates that data has already been logged
|
|
// to the file, we need to return the I/O error rather than just
|
|
// re-creating the file. This may mean that someone changed the
|
|
// RFL directory without moving the RFL files properly.
|
|
|
|
if (uiLastTransOffset > 512)
|
|
{
|
|
rc = RC_SET( FERR_RFL_FILE_NOT_FOUND);
|
|
goto Exit;
|
|
}
|
|
|
|
// Create the RFL file if not found.
|
|
|
|
// Use the next serial number stored in the FDB's log header
|
|
// for the serial number on this RFL file. Use the serial
|
|
// number we just generated as the next RFL serial number.
|
|
|
|
if (RC_BAD( rc = createFile( uiFileNum,
|
|
m_ucCurrSerialNum, m_ucNextSerialNum,
|
|
m_pFile->ucLastCommittedLogHdr [LOG_KEEP_RFL_FILES]
|
|
? TRUE
|
|
: FALSE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
// Read in enough of the buffer from the RFL file so that
|
|
// we are positioned on a 512 byte boundary.
|
|
|
|
if (RC_BAD( positionTo( uiLastTransOffset)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// These can only be changed when starting a transaction.
|
|
|
|
if (m_pFile->FileHdr.uiVersionNum >= FLM_VER_4_3)
|
|
{
|
|
m_bKeepRflFiles = m_pFile->ucLastCommittedLogHdr [LOG_KEEP_RFL_FILES]
|
|
? TRUE
|
|
: FALSE;
|
|
m_uiRflMaxFileSize = (FLMUINT)FB2UD(
|
|
&m_pFile->ucLastCommittedLogHdr [LOG_RFL_MAX_FILE_SIZE]);
|
|
|
|
// Round maximum down to nearest 512 boundary. This is necessary
|
|
// because we always write a minimum of 512 byte units in direct IO
|
|
// mode. If we did not round the maximum down, our last packet could
|
|
// end at an offset that is less than the maximum, but greater than
|
|
// the nearest 512 byte boundary - technically within the user-specified
|
|
// size limit. However, because we always write a full 512 bytes of data
|
|
// to fill out the last sector when we are in direct IO mode, we would
|
|
// end up with a file that was slightly larger than the user-specified
|
|
// limit. The EOF in the header of the file would be below the limit,
|
|
// but the actual file size would not be. Thus, the need to round down.
|
|
|
|
m_uiRflMaxFileSize = ROUND_DOWN_TO_NEAREST_512( m_uiRflMaxFileSize);
|
|
|
|
// The maximum cannot go below a certain threshold - must have room for
|
|
// least one packet plus the header.
|
|
|
|
if (m_uiRflMaxFileSize < RFL_MAX_PACKET_SIZE + 512)
|
|
{
|
|
m_uiRflMaxFileSize = RFL_MAX_PACKET_SIZE + 512;
|
|
}
|
|
else if (m_uiRflMaxFileSize > gv_FlmSysData.uiMaxFileSize)
|
|
{
|
|
m_uiRflMaxFileSize = gv_FlmSysData.uiMaxFileSize;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_bKeepRflFiles = FALSE;
|
|
m_uiRflMaxFileSize = gv_FlmSysData.uiMaxFileSize;
|
|
}
|
|
|
|
m_uiRflMinFileSize = (FLMUINT)FB2UD(
|
|
&m_pFile->ucLastCommittedLogHdr [LOG_RFL_MIN_FILE_SIZE]);
|
|
|
|
// Minimum RFL file size should not be allowed to be larger than
|
|
// maximum!
|
|
|
|
if (m_uiRflMinFileSize > m_uiRflMaxFileSize)
|
|
{
|
|
m_uiRflMinFileSize = m_uiRflMaxFileSize;
|
|
}
|
|
|
|
// Set the operation count to zero.
|
|
|
|
m_uiOperCount = 0;
|
|
|
|
m_pFileHdl->setMaxAutoExtendSize( m_uiRflMaxFileSize);
|
|
m_pFileHdl->setExtendSize( m_pFile->uiFileExtendSize);
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Log transaction begin. This routine will also make sure
|
|
we have opened an RFL file.
|
|
NOTE: The prior version of FLAIM (before 4.3) would log
|
|
a time and set the RFL_TIME_LOGGED_FLAG bit in the packet
|
|
type. This is no longer done. Old code should be
|
|
compatible because it reads the flag.
|
|
*********************************************************************/
|
|
RCODE F_Rfl::logBeginTransaction(
|
|
FDB * pDb
|
|
)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FLMUINT uiDbVersion = pDb->pFile->FileHdr.uiVersionNum;
|
|
FLMBYTE * pucPacketBody;
|
|
FLMUINT uiPacketBodyLen;
|
|
FLMUINT uiGMTTime;
|
|
|
|
// Do nothing if logging is disabled.
|
|
|
|
if (m_bLoggingOff)
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Better not be in the middle of logging unknown stuff
|
|
// for the application
|
|
|
|
flmAssert( !m_bLoggingUnknown);
|
|
|
|
// Better not be in the middle of a transaction.
|
|
|
|
flmAssert( !m_uiCurrTransID);
|
|
|
|
if( RC_BAD( rc = setupTransaction()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
uiPacketBodyLen = uiDbVersion >= FLM_VER_4_31 ? 12 : 8;
|
|
|
|
// Make sure we have space in the RFL buffer for a complete packet.
|
|
|
|
if (!haveBuffSpace( uiPacketBodyLen + RFL_PACKET_OVERHEAD))
|
|
{
|
|
if (RC_BAD( rc = flush( m_pCurrentBuf)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Get a pointer to where we will be laying down the packet body.
|
|
|
|
pucPacketBody = getPacketBodyPtr();
|
|
|
|
// Output the transaction ID.
|
|
|
|
UD2FBA( (FLMUINT32)pDb->LogHdr.uiCurrTransID, pucPacketBody);
|
|
pucPacketBody += 4;
|
|
|
|
// This used to be a FLM_GET_TIMER() value in pre-4.3 code, but
|
|
// it was never really used. Set it to GMT time now.
|
|
|
|
f_timeGetSeconds( &uiGMTTime);
|
|
UD2FBA( (FLMUINT32)uiGMTTime, pucPacketBody);
|
|
pucPacketBody += 4;
|
|
|
|
// NOTE: In the pre-4.3 code the next four bytes would be
|
|
// zero, but that is really unnecessary. We will simply
|
|
// no longer set the RFL_TIME_LOGGED_FLAG bit in the
|
|
// packet type. Pre-4.3 code should be compatible.
|
|
|
|
if( uiDbVersion >= FLM_VER_4_31)
|
|
{
|
|
FLMUINT uiLastLoggedCommitID;
|
|
|
|
uiLastLoggedCommitID = FB2UD(
|
|
&m_pFile->ucLastCommittedLogHdr [LOG_LAST_RFL_COMMIT_ID]);
|
|
|
|
UD2FBA( (FLMUINT32)uiLastLoggedCommitID, pucPacketBody);
|
|
pucPacketBody += 4;
|
|
|
|
if (RC_BAD( rc = finishPacket(
|
|
RFL_TRNS_BEGIN_EX_PACKET, uiPacketBodyLen, TRUE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (RC_BAD( rc = finishPacket(
|
|
RFL_TRNS_BEGIN_PACKET, uiPacketBodyLen, TRUE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Save the file offset for the start transaction packet.
|
|
|
|
m_uiTransStartFile = m_pCurrentBuf->uiCurrFileNum;
|
|
m_uiTransStartAddr = m_pCurrentBuf->uiRflFileOffset +
|
|
m_pCurrentBuf->uiRflBufBytes -
|
|
uiPacketBodyLen - RFL_PACKET_OVERHEAD;
|
|
m_uiCurrTransID = pDb->LogHdr.uiCurrTransID;
|
|
|
|
Exit:
|
|
return( rc);
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Flushes the RFL and sets some things in the log header.
|
|
*********************************************************************/
|
|
void F_Rfl::finalizeTransaction( void)
|
|
{
|
|
FLMUINT uiRflTransEndOffset;
|
|
FLMBYTE * pucLogHdr = &m_pFile->ucUncommittedLogHdr [0];
|
|
|
|
// Save the serial numbers and file numbers into the file's
|
|
// uncommitted log header.
|
|
|
|
UD2FBA( (FLMUINT32)m_pCurrentBuf->uiCurrFileNum,
|
|
&pucLogHdr [LOG_RFL_FILE_NUM]);
|
|
|
|
uiRflTransEndOffset = getCurrWriteOffset();
|
|
UD2FBA( (FLMUINT32)uiRflTransEndOffset,
|
|
&pucLogHdr[LOG_RFL_LAST_TRANS_OFFSET]);
|
|
|
|
f_memcpy( &pucLogHdr [LOG_LAST_TRANS_RFL_SERIAL_NUM],
|
|
m_ucCurrSerialNum, F_SERIAL_NUM_SIZE);
|
|
|
|
f_memcpy( &pucLogHdr [LOG_RFL_NEXT_SERIAL_NUM],
|
|
m_ucNextSerialNum, F_SERIAL_NUM_SIZE);
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Handles the commit and abort log operations. If aborting
|
|
the transaction, or if the transaction was empty, we will
|
|
simply throw away the entire transaction and not bother
|
|
to log it. In that case we will reset transaction pointers,
|
|
etc. back to the file and offset where the transaction began.
|
|
We will also delete RFL files that were created during the
|
|
transaction if necessary. NOTE: It is not essential that
|
|
the RFL files be deleted. If they are not successfully
|
|
deleted, they will be overwritten if need be when creating
|
|
new ones.
|
|
NOTE: The prior version of FLAIM (before 4.3) would log
|
|
a time and set the RFL_TIME_LOGGED_FLAG bit in the packet
|
|
type. This is no longer done. Old code should be
|
|
compatible because it reads the flag.
|
|
*********************************************************************/
|
|
RCODE F_Rfl::logEndTransaction(
|
|
FLMUINT uiPacketType,
|
|
FLMBOOL bThrowLogAway,
|
|
FLMBOOL * pbLoggedTransEnd)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
RCODE rc2 = FERR_OK;
|
|
FLMUINT uiLowFileNum;
|
|
FLMUINT uiHighFileNum;
|
|
char szRflFileName [F_PATH_MAX_SIZE];
|
|
FLMBYTE * pucPacketBody;
|
|
FLMUINT uiPacketBodyLen;
|
|
|
|
// Better not be in the middle of logging unknown stuff
|
|
// for the application
|
|
|
|
flmAssert( !m_bLoggingUnknown);
|
|
|
|
// Initialize the "logged trans end" flag
|
|
|
|
if( pbLoggedTransEnd)
|
|
{
|
|
*pbLoggedTransEnd = FALSE;
|
|
}
|
|
|
|
// Do nothing if logging is disabled.
|
|
|
|
if (m_bLoggingOff)
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
flmAssert( m_pFileHdl);
|
|
flmAssert( m_pFile);
|
|
|
|
// If the transaction had no operations, throw it away - don't
|
|
// even log the packet. An abort operation may also
|
|
// elect to throw the log away even if there were
|
|
// operations. That is determined by the bThrowLogAway flag.
|
|
// The bThrowLogAway flag may be TRUE when doing a commit if
|
|
// the caller knows that nothing happened during the transction.
|
|
|
|
if (bThrowLogAway || !m_uiOperCount)
|
|
{
|
|
Throw_Away_Transaction:
|
|
|
|
// If we have switched files, delete all but the file we
|
|
// started in.
|
|
|
|
if (m_pCurrentBuf->uiCurrFileNum != m_uiTransStartFile)
|
|
{
|
|
flmAssert( m_pCurrentBuf->uiCurrFileNum > m_uiTransStartFile);
|
|
|
|
// File number in uncommitted log header better not
|
|
// have been changed yet. It is only supposed to
|
|
// be changed when the transaction finishes - i.e., in
|
|
// this routine. Up until this point, it should only
|
|
// be changed in m_pCurrentBuf->uiCurrFileNum.
|
|
|
|
flmAssert( m_uiTransStartFile ==
|
|
(FLMUINT)FB2UD( &m_pFile->ucUncommittedLogHdr [LOG_RFL_FILE_NUM]));
|
|
|
|
uiLowFileNum = m_uiTransStartFile + 1;
|
|
uiHighFileNum = m_pCurrentBuf->uiCurrFileNum;
|
|
|
|
// Close the current file so it can be deleted.
|
|
|
|
if (RC_BAD( rc = waitForCommit()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
closeFile();
|
|
|
|
// Delete as many of the files as possible. Don't worry
|
|
// about errors here.
|
|
|
|
while (uiLowFileNum <= uiHighFileNum)
|
|
{
|
|
if (RC_OK( getFullRflFileName( uiLowFileNum, szRflFileName)))
|
|
{
|
|
(void)gv_FlmSysData.pFileSystem->Delete( szRflFileName);
|
|
}
|
|
uiLowFileNum++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
// If we are in the file the transaction started in, simply
|
|
// reset to where the transaction started.
|
|
|
|
if (RC_BAD( rc2 = positionTo( m_uiTransStartAddr)))
|
|
{
|
|
// If we got to this point because of a
|
|
// "goto Throw_Away_Transaction", we don't want to
|
|
// clobber the original error code. So, we use rc2
|
|
// temporarily and then determine if its value should
|
|
// be set into rc.
|
|
|
|
if( RC_OK( rc))
|
|
{
|
|
rc = rc2;
|
|
}
|
|
rc2 = FERR_OK;
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
// Log a commit or abort packet.
|
|
|
|
uiPacketBodyLen = 8;
|
|
|
|
// Make sure we have space in the RFL buffer for a complete packet.
|
|
|
|
if (!haveBuffSpace( uiPacketBodyLen + RFL_PACKET_OVERHEAD))
|
|
{
|
|
if (RC_BAD( rc = flush( m_pCurrentBuf)))
|
|
{
|
|
goto Throw_Away_Transaction;
|
|
}
|
|
}
|
|
|
|
// Get a pointer to where we will be laying down the packet body.
|
|
|
|
pucPacketBody = getPacketBodyPtr();
|
|
|
|
// Output the transaction ID.
|
|
|
|
UD2FBA( (FLMUINT32)m_uiCurrTransID, pucPacketBody);
|
|
pucPacketBody += 4;
|
|
UD2FBA( (FLMUINT32)m_uiTransStartAddr, pucPacketBody);
|
|
pucPacketBody += 4;
|
|
if (RC_BAD( rc = finishPacket( uiPacketType, uiPacketBodyLen,
|
|
FALSE)))
|
|
{
|
|
goto Throw_Away_Transaction;
|
|
}
|
|
|
|
finalizeTransaction();
|
|
|
|
if( pbLoggedTransEnd)
|
|
{
|
|
*pbLoggedTransEnd = TRUE;
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
if (!m_bLoggingOff)
|
|
{
|
|
m_uiCurrTransID = 0;
|
|
}
|
|
|
|
return( RC_BAD( rc) ? rc : rc2);
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Log add, modify, delete, and reserve DRN packets
|
|
*********************************************************************/
|
|
RCODE F_Rfl::logUpdatePacket(
|
|
FLMUINT uiPacketType,
|
|
FLMUINT uiContainer,
|
|
FLMUINT uiDrn,
|
|
FLMUINT uiAutoTrans)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FLMBYTE * pucPacketBody;
|
|
FLMUINT uiPacketBodyLen;
|
|
|
|
// Do nothing if logging is disabled.
|
|
|
|
if (m_bLoggingOff)
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Better not be in the middle of logging unknown stuff
|
|
// for the application
|
|
|
|
flmAssert( !m_bLoggingUnknown);
|
|
|
|
// Better be in the middle of a transaction.
|
|
|
|
flmAssert( m_uiCurrTransID);
|
|
|
|
m_uiOperCount++;
|
|
|
|
if( uiPacketType == RFL_ADD_RECORD_PACKET_VER_2 ||
|
|
uiPacketType == RFL_MODIFY_RECORD_PACKET_VER_2 ||
|
|
uiPacketType == RFL_DELETE_RECORD_PACKET_VER_2)
|
|
{
|
|
uiPacketBodyLen = 11;
|
|
}
|
|
else
|
|
{
|
|
uiPacketBodyLen = 10;
|
|
}
|
|
|
|
// Make sure we have space in the RFL buffer for a complete packet.
|
|
|
|
if (!haveBuffSpace( uiPacketBodyLen + RFL_PACKET_OVERHEAD))
|
|
{
|
|
if (RC_BAD( rc = flush( m_pCurrentBuf)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Get a pointer to where we will be laying down the packet body.
|
|
|
|
pucPacketBody = getPacketBodyPtr();
|
|
|
|
// Output the transaction ID.
|
|
|
|
UD2FBA( (FLMUINT32)m_uiCurrTransID, pucPacketBody);
|
|
pucPacketBody += 4;
|
|
|
|
// Output the container number.
|
|
|
|
UW2FBA( (FLMUINT16)uiContainer, pucPacketBody);
|
|
pucPacketBody += 2;
|
|
|
|
// Output the DRN.
|
|
|
|
UD2FBA( (FLMUINT32)uiDrn, pucPacketBody);
|
|
pucPacketBody += 4;
|
|
|
|
// Output the flags
|
|
|
|
if( uiPacketType == RFL_ADD_RECORD_PACKET_VER_2 ||
|
|
uiPacketType == RFL_MODIFY_RECORD_PACKET_VER_2 ||
|
|
uiPacketType == RFL_DELETE_RECORD_PACKET_VER_2)
|
|
{
|
|
FLMUINT uiFlags = 0;
|
|
|
|
// For now, these are the only flags we log
|
|
|
|
if( uiAutoTrans & FLM_DO_IN_BACKGROUND)
|
|
{
|
|
uiFlags |= RFL_UPDATE_BACKGROUND;
|
|
}
|
|
|
|
if( uiAutoTrans & FLM_SUSPENDED)
|
|
{
|
|
uiFlags |= RFL_UPDATE_SUSPENDED;
|
|
}
|
|
|
|
*pucPacketBody++ = (FLMBYTE)uiFlags;
|
|
}
|
|
|
|
// Finish the packet
|
|
|
|
if (RC_BAD( rc = finishPacket( uiPacketType, uiPacketBodyLen,
|
|
FALSE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Log index suspend and resume packets
|
|
*********************************************************************/
|
|
RCODE F_Rfl::logIndexSuspendOrResume(
|
|
FLMUINT uiIndexNum,
|
|
FLMUINT uiPacketType)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FLMBYTE * pucPacketBody;
|
|
FLMUINT uiPacketBodyLen;
|
|
|
|
// This call is new with 4.51 databases - not supported in older
|
|
// versions, so don't log it.
|
|
|
|
if (m_pFile->FileHdr.uiVersionNum < FLM_VER_4_51)
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Do nothing if logging is disabled.
|
|
|
|
if (m_bLoggingOff)
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Better not be in the middle of logging unknown stuff
|
|
// for the application
|
|
|
|
flmAssert( !m_bLoggingUnknown);
|
|
|
|
// Better be in the middle of a transaction.
|
|
|
|
flmAssert( m_uiCurrTransID);
|
|
|
|
m_uiOperCount++;
|
|
uiPacketBodyLen = 6;
|
|
|
|
// Make sure we have space in the RFL buffer for a complete packet.
|
|
|
|
if (!haveBuffSpace( uiPacketBodyLen + RFL_PACKET_OVERHEAD))
|
|
{
|
|
if (RC_BAD( rc = flush( m_pCurrentBuf)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Get a pointer to where we will be laying down the packet body.
|
|
|
|
pucPacketBody = getPacketBodyPtr();
|
|
|
|
// Output the transaction ID.
|
|
|
|
UD2FBA( (FLMUINT32)m_uiCurrTransID, pucPacketBody);
|
|
pucPacketBody += 4;
|
|
|
|
// Output the index number.
|
|
|
|
UW2FBA( (FLMUINT16)uiIndexNum, pucPacketBody);
|
|
pucPacketBody += 2;
|
|
|
|
// Finish the packet
|
|
|
|
if (RC_BAD( rc = finishPacket( uiPacketType, uiPacketBodyLen,
|
|
FALSE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Make room in the RFL buffer for the additional bytes.
|
|
This is done by flushing the log buffer and shifting down
|
|
the bytes already used in the current packet. If that doesn't
|
|
make room, the current packet will be finished and a new one
|
|
started.
|
|
*********************************************************************/
|
|
RCODE F_Rfl::makeRoom(
|
|
FLMUINT uiAdditionalBytesNeeded,
|
|
FLMUINT * puiCurrPacketLenRV,
|
|
FLMUINT uiPacketType,
|
|
FLMUINT * puiBytesAvailableRV,
|
|
FLMUINT * puiPacketCountRV
|
|
)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FLMUINT uiBytesNeeded;
|
|
|
|
// Must account for encryption, so round bytes needed to nearest
|
|
// four byte boundary.
|
|
|
|
uiBytesNeeded = *puiCurrPacketLenRV + uiAdditionalBytesNeeded;
|
|
if (uiBytesNeeded & 0x3)
|
|
{
|
|
uiBytesNeeded += (4 - (uiBytesNeeded & 0x3));
|
|
}
|
|
|
|
if (uiBytesNeeded <= (FLMUINT)RFL_MAX_PACKET_SIZE)
|
|
{
|
|
FLMUINT uiTmp = uiBytesNeeded;
|
|
|
|
if (haveBuffSpace( uiTmp))
|
|
{
|
|
if (puiBytesAvailableRV)
|
|
{
|
|
*puiBytesAvailableRV = uiAdditionalBytesNeeded;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
// Bytes requested will fit into a packet, but not the
|
|
// buffer, so we need to shift the packets in the buffer
|
|
// down. The shiftPacketsDown guarantees that there
|
|
// is room in the buffer for a full size packet.
|
|
|
|
if (RC_BAD( rc = shiftPacketsDown( *puiCurrPacketLenRV, FALSE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// If a non-NULL puwBytesAvailableRV is passed in it means that we
|
|
// are to return the number of bytes that we can actually output.
|
|
// Since we know there is enough for the bytes needed, we simply return
|
|
// the number of bytes that were requested.
|
|
|
|
if (puiBytesAvailableRV)
|
|
{
|
|
*puiBytesAvailableRV = uiAdditionalBytesNeeded;
|
|
}
|
|
}
|
|
}
|
|
else // (uiBytesNeeded > RFL_MAX_PACKET_SIZE)
|
|
{
|
|
|
|
// This is the case where the bytes needed would overflow the
|
|
// maximum packet size.
|
|
// If puwBytesAvailableRV is NULL, it means that all of the
|
|
// requested additional bytes must fit into the packet. In that
|
|
// case, since the requested bytes would put us over the packet
|
|
// size limit, we must finish the current packet and then
|
|
// flush the packets out of the buffer so we can start a
|
|
// new packet.
|
|
|
|
if (!puiBytesAvailableRV)
|
|
{
|
|
|
|
// Finish the current packet and start a new one.
|
|
|
|
if (puiPacketCountRV)
|
|
{
|
|
(*puiPacketCountRV)++;
|
|
}
|
|
if (RC_BAD( rc = finishPacket( uiPacketType,
|
|
*puiCurrPacketLenRV - RFL_PACKET_OVERHEAD,
|
|
FALSE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
if (RC_BAD( rc = flush( m_pCurrentBuf)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
*puiCurrPacketLenRV = RFL_PACKET_OVERHEAD;
|
|
}
|
|
else
|
|
{
|
|
|
|
// When puiBytesAvailableRV is non-NULL, it means we can fill up
|
|
// the rest of the packet with part of the bytes. In this case
|
|
// we return the number of bytes available and then shift the
|
|
// packets down in the buffer to make sure there is room for
|
|
// a full-size packet.
|
|
|
|
*puiBytesAvailableRV = RFL_MAX_PACKET_SIZE - *puiCurrPacketLenRV;
|
|
if (RC_BAD( rc = shiftPacketsDown( *puiCurrPacketLenRV, FALSE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
Exit:
|
|
return( rc);
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Log a chunk of data to the RFL log - typically used to log
|
|
field data. Will spill over into multiple packets if
|
|
necessary.
|
|
*********************************************************************/
|
|
RCODE F_Rfl::logData(
|
|
FLMUINT uiDataLen,
|
|
const FLMBYTE * pucData,
|
|
FLMUINT uiPacketType,
|
|
FLMUINT * puiPacketLenRV,
|
|
FLMUINT * puiPacketCountRV,
|
|
FLMUINT * puiMaxLogBytesNeededRV,
|
|
FLMUINT * puiTotalBytesLoggedRV)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FLMUINT uiBytesAvail;
|
|
FLMBYTE * pucDest;
|
|
|
|
while (uiDataLen)
|
|
{
|
|
if (RC_BAD( rc = makeRoom( uiDataLen,
|
|
puiPacketLenRV, uiPacketType, &uiBytesAvail,
|
|
puiPacketCountRV)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
if (uiBytesAvail)
|
|
{
|
|
if (puiMaxLogBytesNeededRV)
|
|
{
|
|
if (RC_BAD( rc = RflCheckMaxLogged(
|
|
puiMaxLogBytesNeededRV,
|
|
*puiPacketCountRV,
|
|
puiTotalBytesLoggedRV,
|
|
uiBytesAvail)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
pucDest = getPacketPtr() + (*puiPacketLenRV);
|
|
f_memcpy( pucDest, pucData, uiBytesAvail);
|
|
uiDataLen -= uiBytesAvail;
|
|
pucData += uiBytesAvail;
|
|
(*puiPacketLenRV) += uiBytesAvail;
|
|
}
|
|
|
|
// If we didn't get all of the data into the RFL buffer,
|
|
// finish and flush the current packet.
|
|
|
|
if (uiDataLen)
|
|
{
|
|
if (puiPacketCountRV)
|
|
{
|
|
(*puiPacketCountRV)++;
|
|
}
|
|
if (RC_BAD( rc = finishPacket( uiPacketType,
|
|
*puiPacketLenRV - RFL_PACKET_OVERHEAD,
|
|
FALSE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
if (RC_BAD( rc = flush( m_pCurrentBuf)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
*puiPacketLenRV = RFL_PACKET_OVERHEAD;
|
|
if (puiMaxLogBytesNeededRV)
|
|
{
|
|
if (RC_BAD( rc = RflCheckMaxLogged(
|
|
puiMaxLogBytesNeededRV,
|
|
*puiPacketCountRV,
|
|
puiTotalBytesLoggedRV,
|
|
RFL_PACKET_OVERHEAD)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Exit:
|
|
return( rc);
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Check to see if by logging the requested number of bytes we
|
|
will end up exceeding the maximum bytes needed. If so, and
|
|
we have not yet actually logged a packet, return
|
|
FERR_FAILURE so that we will discard this packet that is
|
|
being built. If we have already logged a packet, it is
|
|
too late to discard what has been done.
|
|
*********************************************************************/
|
|
FSTATIC RCODE RflCheckMaxLogged(
|
|
FLMUINT * puiMaxBytesNeededRV,
|
|
FLMUINT uiPacketsLogged,
|
|
FLMUINT * puiCurrTotalLoggedRV,
|
|
FLMUINT uiBytesToLog)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
|
|
*puiCurrTotalLoggedRV += uiBytesToLog;
|
|
|
|
if ((!uiPacketsLogged) &&
|
|
(*puiCurrTotalLoggedRV > *puiMaxBytesNeededRV))
|
|
{
|
|
rc = RC_SET( FERR_FAILURE);
|
|
goto Exit;
|
|
}
|
|
Exit:
|
|
return( rc);
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Callback function that captures the changes being logged by
|
|
the call to flmRecordDifference.
|
|
*********************************************************************/
|
|
FSTATIC void RflChangeCallback(
|
|
GRD_DifferenceData & DiffData,
|
|
void * CallbackData
|
|
)
|
|
{
|
|
RFL_CHANGE_DATA * pRflChangeData = (RFL_CHANGE_DATA *)CallbackData;
|
|
F_Rfl * pRfl = pRflChangeData->pRfl;
|
|
void * pvField;
|
|
const FLMBYTE * pucExportPtr;
|
|
FLMBYTE * pucTmp;
|
|
FLMUINT uiOverhead = 0;
|
|
FLMUINT uiBytesToLog;
|
|
FLMUINT uiPos;
|
|
FLMUINT uiTagNum;
|
|
FLMUINT uiDataLen;
|
|
FLMBOOL bEncrypted = FALSE;
|
|
FLMUINT uiEncId;
|
|
|
|
// If we had an error before this callback, do nothing.
|
|
|
|
if (RC_BAD( pRflChangeData->rc))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if ( DiffData.pvAfterField)
|
|
{
|
|
flmAssert( DiffData.pAfterRecord);
|
|
bEncrypted = DiffData.pAfterRecord->isEncryptedField(
|
|
DiffData.pvAfterField);
|
|
}
|
|
|
|
switch (DiffData.type)
|
|
{
|
|
case GRD_Inserted:
|
|
uiOverhead = (bEncrypted ? 13 : 9);
|
|
break;
|
|
case GRD_Deleted:
|
|
// Ignore these for versions of the database >= 4.60
|
|
if (pRflChangeData->uiVersionNum >= FLM_VER_4_60)
|
|
{
|
|
goto Exit;
|
|
}
|
|
uiOverhead = 3;
|
|
break;
|
|
case GRD_DeletedSubtree:
|
|
// Ignore these for versions of the database < 4.60
|
|
if (pRflChangeData->uiVersionNum < FLM_VER_4_60)
|
|
{
|
|
goto Exit;
|
|
}
|
|
uiOverhead = 3;
|
|
break;
|
|
case GRD_Modified:
|
|
uiOverhead = (bEncrypted ? 10 : 6);
|
|
break;
|
|
default:
|
|
flmAssert( 0);
|
|
break;
|
|
}
|
|
|
|
// Determine the number of bytes that will actually be logged with this
|
|
// overhead. If it won't fit in the current packet, we will have to
|
|
// create a new packet - hence, we add RFL_PACKET_OVERHEAD to the amount
|
|
// that will be logged.
|
|
|
|
uiBytesToLog = uiOverhead;
|
|
if (RFL_MAX_PACKET_SIZE - uiOverhead < pRflChangeData->uiCurrPacketLen)
|
|
{
|
|
uiBytesToLog += RFL_PACKET_OVERHEAD;
|
|
}
|
|
|
|
// See if the bytes we are going log will exceed the maximum bytes needed.
|
|
|
|
if (RC_BAD( pRflChangeData->rc = RflCheckMaxLogged(
|
|
&pRflChangeData->uiMaxLogBytesNeeded,
|
|
pRflChangeData->uiPacketCount,
|
|
&pRflChangeData->uiTotalBytesLogged,
|
|
uiBytesToLog)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Make room to log the overhead
|
|
|
|
if (RC_BAD( pRflChangeData->rc = pRfl->makeRoom( uiOverhead,
|
|
&pRflChangeData->uiCurrPacketLen,
|
|
RFL_CHANGE_FIELDS_PACKET, NULL,
|
|
&pRflChangeData->uiPacketCount)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
pucTmp = pRfl->getPacketPtr() + pRflChangeData->uiCurrPacketLen;
|
|
uiPos = DiffData.uiAbsolutePosition;
|
|
UW2FBA( (FLMUINT16)uiPos, &pucTmp [1]);
|
|
pRflChangeData->uiCurrPacketLen += uiOverhead;
|
|
pvField = DiffData.pvAfterField;
|
|
|
|
switch (DiffData.type)
|
|
{
|
|
case GRD_Inserted:
|
|
*pucTmp = (bEncrypted ? RFL_INSERT_ENC_FIELD : RFL_INSERT_FIELD);
|
|
pucTmp += 3;
|
|
uiTagNum = DiffData.pAfterRecord->getFieldID( pvField);
|
|
UW2FBA( (FLMUINT16)uiTagNum, pucTmp);
|
|
pucTmp += 2;
|
|
*pucTmp++ = (FLMBYTE)DiffData.pAfterRecord->getDataType( pvField);
|
|
*pucTmp++ = (FLMBYTE)DiffData.pAfterRecord->getLevel( pvField);
|
|
uiDataLen = DiffData.pAfterRecord->getDataLength( pvField);
|
|
UW2FBA( (FLMUINT16)uiDataLen, pucTmp);
|
|
pucTmp += 2;
|
|
|
|
if (bEncrypted)
|
|
{
|
|
uiEncId = DiffData.pAfterRecord->getEncryptionID( pvField);
|
|
flmAssert( uiEncId);
|
|
UW2FBA( (FLMUINT16)uiEncId, pucTmp);
|
|
pucTmp += 2;
|
|
|
|
uiDataLen = DiffData.pAfterRecord->getEncryptedDataLength(pvField);
|
|
UW2FBA( uiDataLen, pucTmp);
|
|
pucTmp += 2;
|
|
}
|
|
|
|
// Log the data, if any.
|
|
|
|
if (uiDataLen)
|
|
{
|
|
if (bEncrypted)
|
|
{
|
|
pucExportPtr =
|
|
DiffData.pAfterRecord->getEncryptionDataPtr(pvField);
|
|
}
|
|
else
|
|
{
|
|
pucExportPtr = DiffData.pAfterRecord->getDataPtr( pvField);
|
|
}
|
|
if( !pucExportPtr)
|
|
{
|
|
pRflChangeData->rc = RC_SET( FERR_MEM);
|
|
goto Exit;
|
|
}
|
|
|
|
if( RC_BAD( pRflChangeData->rc = pRfl->logData(
|
|
uiDataLen,
|
|
pucExportPtr,
|
|
RFL_CHANGE_FIELDS_PACKET,
|
|
&pRflChangeData->uiCurrPacketLen,
|
|
&pRflChangeData->uiPacketCount,
|
|
&pRflChangeData->uiMaxLogBytesNeeded,
|
|
&pRflChangeData->uiTotalBytesLogged)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
break;
|
|
case GRD_Deleted:
|
|
case GRD_DeletedSubtree:
|
|
*pucTmp = RFL_DELETE_FIELD;
|
|
break;
|
|
case GRD_Modified:
|
|
*pucTmp = (bEncrypted ? RFL_MODIFY_ENC_FIELD : RFL_MODIFY_FIELD);
|
|
pucTmp += 3;
|
|
|
|
// For now, just log the new bytes using RFL_REPLACE_BYTES option
|
|
*pucTmp++ = RFL_REPLACE_BYTES;
|
|
uiDataLen = DiffData.pAfterRecord->getDataLength( pvField);
|
|
UW2FBA( (FLMUINT16)uiDataLen, pucTmp);
|
|
pucTmp += 2;
|
|
|
|
if (bEncrypted)
|
|
{
|
|
uiEncId = DiffData.pAfterRecord->getEncryptionID( pvField);
|
|
flmAssert( uiEncId);
|
|
UW2FBA( (FLMUINT16)uiEncId, pucTmp);
|
|
pucTmp += 2;
|
|
|
|
uiDataLen = DiffData.pAfterRecord->getEncryptedDataLength( pvField);
|
|
UW2FBA( uiDataLen, pucTmp);
|
|
pucTmp += 2;
|
|
}
|
|
|
|
// Log the data, if any.
|
|
|
|
if (uiDataLen)
|
|
{
|
|
if (bEncrypted)
|
|
{
|
|
pucExportPtr =
|
|
DiffData.pAfterRecord->getEncryptionDataPtr(pvField);
|
|
}
|
|
else
|
|
{
|
|
pucExportPtr = DiffData.pAfterRecord->getDataPtr( pvField);
|
|
}
|
|
if (pucExportPtr == NULL)
|
|
{
|
|
pRflChangeData->rc = RC_SET( FERR_MEM);
|
|
goto Exit;
|
|
}
|
|
|
|
if( RC_BAD( pRflChangeData->rc = pRfl->logData(
|
|
uiDataLen,
|
|
pucExportPtr,
|
|
RFL_CHANGE_FIELDS_PACKET,
|
|
&pRflChangeData->uiCurrPacketLen,
|
|
&pRflChangeData->uiPacketCount,
|
|
&pRflChangeData->uiMaxLogBytesNeeded,
|
|
&pRflChangeData->uiTotalBytesLogged)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
break;
|
|
default:
|
|
flmAssert( 0);
|
|
break;
|
|
}
|
|
Exit:
|
|
return;
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Log change fields for a record modify operation.
|
|
*********************************************************************/
|
|
RCODE F_Rfl::logChangeFields(
|
|
FlmRecord * pOldRecord,
|
|
FlmRecord * pNewRecord
|
|
)
|
|
{
|
|
RFL_CHANGE_DATA RflChangeData;
|
|
FLMUINT uiTmpBodyLen;
|
|
FLMUINT uiDataLen;
|
|
void * pvNewField;
|
|
FLMBOOL bEncrypted;
|
|
FLMUINT uiOverhead;
|
|
|
|
RflChangeData.rc = FERR_OK;
|
|
RflChangeData.pRfl = this;
|
|
RflChangeData.uiVersionNum = m_pFile->FileHdr.uiVersionNum;
|
|
|
|
// Determine the total amount that would have to be logged if
|
|
// we just logged the new record.
|
|
|
|
RflChangeData.uiMaxLogBytesNeeded = RFL_PACKET_OVERHEAD;
|
|
uiTmpBodyLen = 0;
|
|
pvNewField = pNewRecord->root();
|
|
for (; pvNewField; pvNewField = pNewRecord->next( pvNewField) )
|
|
{
|
|
bEncrypted = pNewRecord->isEncryptedField( pvNewField);
|
|
uiOverhead = (bEncrypted ? 10 : 6);
|
|
if (uiTmpBodyLen + uiOverhead <= RFL_MAX_PACKET_BODY_SIZE)
|
|
{
|
|
uiTmpBodyLen += uiOverhead;
|
|
}
|
|
else
|
|
{
|
|
uiTmpBodyLen = uiOverhead;
|
|
RflChangeData.uiMaxLogBytesNeeded += RFL_PACKET_OVERHEAD;
|
|
}
|
|
RflChangeData.uiMaxLogBytesNeeded += uiOverhead;
|
|
if (bEncrypted)
|
|
{
|
|
uiDataLen = pNewRecord->getEncryptedDataLength( pvNewField);
|
|
}
|
|
else
|
|
{
|
|
uiDataLen = pNewRecord->getDataLength( pvNewField);
|
|
}
|
|
while (uiDataLen)
|
|
{
|
|
FLMUINT uiTmp;
|
|
|
|
uiTmp = RFL_MAX_PACKET_BODY_SIZE - uiTmpBodyLen;
|
|
if (uiTmp >= uiDataLen)
|
|
{
|
|
uiTmp = uiDataLen;
|
|
uiTmpBodyLen += uiDataLen;
|
|
}
|
|
else
|
|
{
|
|
uiTmpBodyLen = 0;
|
|
RflChangeData.uiMaxLogBytesNeeded += RFL_PACKET_OVERHEAD;
|
|
}
|
|
RflChangeData.uiMaxLogBytesNeeded += uiTmp;
|
|
uiDataLen -= uiTmp;
|
|
}
|
|
}
|
|
|
|
// Account for terminating 0 at the end.
|
|
|
|
if (uiTmpBodyLen + 2 > RFL_MAX_PACKET_BODY_SIZE)
|
|
{
|
|
RflChangeData.uiMaxLogBytesNeeded += RFL_PACKET_OVERHEAD;
|
|
}
|
|
RflChangeData.uiMaxLogBytesNeeded += 2;
|
|
|
|
RflChangeData.uiPacketCount = 0;
|
|
RflChangeData.uiTotalBytesLogged = RFL_PACKET_OVERHEAD;
|
|
RflChangeData.uiCurrPacketLen = RFL_PACKET_OVERHEAD;
|
|
|
|
if (!haveBuffSpace( RFL_PACKET_OVERHEAD))
|
|
{
|
|
if (RC_BAD( RflChangeData.rc = flush( m_pCurrentBuf)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
flmRecordDifference( pOldRecord, pNewRecord, RflChangeCallback,
|
|
(void *)&RflChangeData);
|
|
|
|
// See if we exceeded the maximum log bytes. If so, just log
|
|
// the changed record in its entirety.
|
|
|
|
if (RC_BAD( RflChangeData.rc))
|
|
{
|
|
if (RflChangeData.rc == FERR_FAILURE)
|
|
{
|
|
RflChangeData.rc = logRecord( pNewRecord);
|
|
}
|
|
goto Exit;
|
|
}
|
|
else
|
|
{
|
|
FLMBYTE * pucTmp;
|
|
|
|
// Make room to log the 3 bytes of terminator
|
|
|
|
if (RC_BAD( RflChangeData.rc = makeRoom( 3,
|
|
&RflChangeData.uiCurrPacketLen,
|
|
RFL_CHANGE_FIELDS_PACKET, NULL,
|
|
&RflChangeData.uiPacketCount)))
|
|
{
|
|
if (RflChangeData.rc == FERR_FAILURE)
|
|
{
|
|
RflChangeData.rc = logRecord( pNewRecord);
|
|
}
|
|
goto Exit;
|
|
}
|
|
pucTmp = getPacketPtr() + RflChangeData.uiCurrPacketLen;
|
|
*pucTmp++ = RFL_END_FIELD_CHANGES;
|
|
UW2FBA( (FLMUINT16)0, pucTmp);
|
|
RflChangeData.uiCurrPacketLen += 3;
|
|
|
|
if (RC_BAD( RflChangeData.rc = finishPacket( RFL_CHANGE_FIELDS_PACKET,
|
|
RflChangeData.uiCurrPacketLen - RFL_PACKET_OVERHEAD,
|
|
FALSE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
return( RflChangeData.rc);
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Log a record for the record add or modify operations.
|
|
*********************************************************************/
|
|
RCODE F_Rfl::logRecord(
|
|
FlmRecord * pRecord)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FLMUINT uiPacketLen = RFL_PACKET_OVERHEAD;
|
|
void * pvField;
|
|
FLMBYTE * pucTmp;
|
|
FLMUINT uiTagNum;
|
|
FLMUINT uiDataLen;
|
|
FLMBOOL bEncrypted;
|
|
FLMUINT uiEncId;
|
|
FLMUINT uiPacketType;
|
|
FLMUINT uiOverhead;
|
|
|
|
if( !haveBuffSpace( RFL_PACKET_OVERHEAD))
|
|
{
|
|
if (RC_BAD( rc = flush( m_pCurrentBuf)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
if (m_pFile->FileHdr.uiVersionNum < FLM_VER_4_60)
|
|
{
|
|
uiPacketType = RFL_DATA_RECORD_PACKET;
|
|
}
|
|
else
|
|
{
|
|
uiPacketType = RFL_ENC_DATA_RECORD_PACKET;
|
|
}
|
|
|
|
pvField = pRecord->root();
|
|
for (; pvField; pvField = pRecord->next( pvField) )
|
|
{
|
|
if (uiPacketType == RFL_DATA_RECORD_PACKET)
|
|
{
|
|
bEncrypted = FALSE;
|
|
uiOverhead = 6;
|
|
}
|
|
else
|
|
{
|
|
bEncrypted = pRecord->isEncryptedField( pvField);
|
|
uiOverhead = (bEncrypted ? 11 : 7);
|
|
}
|
|
|
|
if (RC_BAD( rc = makeRoom( uiOverhead, &uiPacketLen,
|
|
uiPacketType, NULL, NULL)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
pucTmp = getPacketPtr() + uiPacketLen;
|
|
uiPacketLen += uiOverhead;
|
|
|
|
uiTagNum = pRecord->getFieldID( pvField);
|
|
UW2FBA( (FLMUINT16)uiTagNum, pucTmp);
|
|
pucTmp += 2;
|
|
*pucTmp++ = (FLMBYTE)pRecord->getDataType( pvField);
|
|
*pucTmp++ = (FLMBYTE)pRecord->getLevel( pvField);
|
|
uiDataLen = pRecord->getDataLength( pvField);
|
|
UW2FBA( (FLMUINT16)uiDataLen, pucTmp);
|
|
pucTmp += 2;
|
|
// Record if this field is encrypted. If it is, then there will
|
|
// be more data to follow.
|
|
if (uiPacketType == RFL_ENC_DATA_RECORD_PACKET)
|
|
{
|
|
*pucTmp = (bEncrypted ? (FLMBYTE)1 : (FLMBYTE)0);
|
|
pucTmp++;
|
|
|
|
// Check for encrypted field and add the results.
|
|
if (bEncrypted)
|
|
{
|
|
uiEncId = pRecord->getEncryptionID( pvField);
|
|
flmAssert( uiEncId);
|
|
UW2FBA( (FLMUINT16)uiEncId, pucTmp);
|
|
pucTmp += 2;
|
|
|
|
uiDataLen = pRecord->getEncryptedDataLength(pvField);
|
|
UW2FBA( uiDataLen, pucTmp);
|
|
pucTmp += 2;
|
|
}
|
|
}
|
|
|
|
|
|
// Log the data, if any.
|
|
|
|
if (uiDataLen)
|
|
{
|
|
const FLMBYTE * pucExportPtr;
|
|
|
|
if (bEncrypted)
|
|
{
|
|
pucExportPtr = pRecord->getEncryptionDataPtr( pvField);
|
|
}
|
|
else
|
|
{
|
|
pucExportPtr = pRecord->getDataPtr( pvField);
|
|
}
|
|
if (pucExportPtr == NULL)
|
|
{
|
|
rc = RC_SET( FERR_MEM);
|
|
goto Exit;
|
|
}
|
|
|
|
if( RC_BAD( rc = logData( uiDataLen, pucExportPtr,
|
|
uiPacketType, &uiPacketLen, NULL, NULL, NULL)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add null to terminate the record.
|
|
|
|
if (RC_BAD( rc = makeRoom( 2, &uiPacketLen, uiPacketType,
|
|
NULL, NULL)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
pucTmp = getPacketPtr() + uiPacketLen;
|
|
uiPacketLen += 2;
|
|
UW2FBA( 0, pucTmp);
|
|
pucTmp += 2;
|
|
|
|
// Finish the packet.
|
|
|
|
if (RC_BAD( rc = finishPacket( uiPacketType,
|
|
uiPacketLen - RFL_PACKET_OVERHEAD, FALSE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Log record add, modify, or delete operation
|
|
*********************************************************************/
|
|
RCODE F_Rfl::logUpdate(
|
|
FLMUINT uiContainer,
|
|
FLMUINT uiDrn,
|
|
FLMUINT uiAutoTrans,
|
|
FlmRecord * pOldRecord,
|
|
FlmRecord * pNewRecord)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FLMUINT uiPacketType;
|
|
|
|
// Do nothing if logging is disabled.
|
|
|
|
if (m_bLoggingOff)
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Better not be in the middle of logging unknown stuff
|
|
// for the application
|
|
|
|
flmAssert( !m_bLoggingUnknown);
|
|
|
|
// Better be in the middle of a transaction.
|
|
|
|
flmAssert( m_uiCurrTransID);
|
|
|
|
if (pOldRecord && pNewRecord)
|
|
{
|
|
if( m_pFile->FileHdr.uiVersionNum >= FLM_VER_4_60)
|
|
{
|
|
uiPacketType = RFL_MODIFY_RECORD_PACKET_VER_2;
|
|
}
|
|
else
|
|
{
|
|
uiPacketType = RFL_MODIFY_RECORD_PACKET;
|
|
}
|
|
}
|
|
else if (pNewRecord)
|
|
{
|
|
if( m_pFile->FileHdr.uiVersionNum >= FLM_VER_4_60)
|
|
{
|
|
uiPacketType = RFL_ADD_RECORD_PACKET_VER_2;
|
|
}
|
|
else
|
|
{
|
|
uiPacketType = RFL_ADD_RECORD_PACKET;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( m_pFile->FileHdr.uiVersionNum >= FLM_VER_4_60)
|
|
{
|
|
uiPacketType = RFL_DELETE_RECORD_PACKET_VER_2;
|
|
}
|
|
else
|
|
{
|
|
uiPacketType = RFL_DELETE_RECORD_PACKET;
|
|
}
|
|
}
|
|
|
|
if (RC_BAD( rc = logUpdatePacket( uiPacketType,
|
|
uiContainer, uiDrn, uiAutoTrans)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// If it is a record modify, log the change fields.
|
|
// If it is a record add, log the new record.
|
|
|
|
if (pOldRecord && pNewRecord)
|
|
{
|
|
if (RC_BAD( rc = logChangeFields( pOldRecord, pNewRecord)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
else if (pNewRecord)
|
|
{
|
|
if (RC_BAD( rc = logRecord( pNewRecord)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
Exit:
|
|
return( rc);
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Log a set of records that is indexed for a specific index.
|
|
*********************************************************************/
|
|
RCODE F_Rfl::logIndexSet(
|
|
FLMUINT uiIndex,
|
|
FLMUINT uiContainerNum,
|
|
FLMUINT uiStartDrn,
|
|
FLMUINT uiEndDrn)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FLMBYTE * pucPacketBody;
|
|
FLMUINT uiPacketBodyLen;
|
|
|
|
// This call is a new database version. Database better have
|
|
// been upgraded.
|
|
|
|
flmAssert( m_pFile->FileHdr.uiVersionNum >= FLM_VER_3_02);
|
|
|
|
// Do nothing if logging is disabled.
|
|
|
|
if (m_bLoggingOff)
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Better not be in the middle of logging unknown stuff
|
|
// for the application
|
|
|
|
flmAssert( !m_bLoggingUnknown);
|
|
|
|
// Better be in the middle of a transaction.
|
|
|
|
flmAssert( m_uiCurrTransID);
|
|
|
|
m_uiOperCount++;
|
|
uiPacketBodyLen = (FLMUINT)((m_pFile->FileHdr.uiVersionNum >= FLM_VER_4_50)
|
|
? (FLMUINT)16
|
|
: (FLMUINT)14);
|
|
|
|
// Make sure we have space in the RFL buffer for a complete packet.
|
|
|
|
if (!haveBuffSpace( uiPacketBodyLen + RFL_PACKET_OVERHEAD))
|
|
{
|
|
if (RC_BAD( rc = flush( m_pCurrentBuf)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Get a pointer to where we will be laying down the packet body.
|
|
|
|
pucPacketBody = getPacketBodyPtr();
|
|
|
|
// Output the transaction ID.
|
|
|
|
UD2FBA( (FLMUINT32)m_uiCurrTransID, pucPacketBody);
|
|
pucPacketBody += 4;
|
|
|
|
// Output the container number, if db version is >= 4.50
|
|
|
|
if (m_pFile->FileHdr.uiVersionNum >= FLM_VER_4_50)
|
|
{
|
|
UW2FBA( (FLMUINT16)uiContainerNum, pucPacketBody);
|
|
pucPacketBody += 2;
|
|
}
|
|
|
|
// Output the index number.
|
|
|
|
UW2FBA( (FLMUINT16)uiIndex, pucPacketBody);
|
|
pucPacketBody += 2;
|
|
|
|
// Output the starting DRN.
|
|
|
|
UD2FBA( (FLMUINT32)(uiStartDrn), pucPacketBody);
|
|
pucPacketBody += 4;
|
|
|
|
// Output the ending DRN.
|
|
|
|
UD2FBA( (FLMUINT32)(uiEndDrn), pucPacketBody);
|
|
pucPacketBody += 4;
|
|
|
|
// Finish the packet
|
|
|
|
if (RC_BAD( rc = finishPacket(
|
|
(FLMUINT)((m_pFile->FileHdr.uiVersionNum >= FLM_VER_4_50)
|
|
? (FLMUINT)RFL_INDEX_SET_PACKET_VER_2
|
|
: (FLMUINT)RFL_INDEX_SET_PACKET), uiPacketBodyLen,
|
|
FALSE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
return( rc);
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Start logging unknown packets.
|
|
*********************************************************************/
|
|
RCODE F_Rfl::startLoggingUnknown( void)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FLMBYTE * pucPacketBody;
|
|
FLMUINT uiPacketBodyLen;
|
|
|
|
flmAssert( m_pFile);
|
|
|
|
// Do nothing if logging is disabled. Also, ignore
|
|
// these packets if we are operating on a pre-4.3
|
|
// database.
|
|
|
|
if (m_bLoggingOff ||
|
|
m_pFile->FileHdr.uiVersionNum < FLM_VER_4_3)
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Better not already be in the middle of logging unknown stuff
|
|
// for the application
|
|
|
|
flmAssert( !m_bLoggingUnknown);
|
|
|
|
// Better be inside a transaction.
|
|
|
|
flmAssert( m_uiCurrTransID);
|
|
|
|
m_uiOperCount++;
|
|
uiPacketBodyLen = 4;
|
|
|
|
// Make sure we have space in the RFL buffer for a complete packet.
|
|
|
|
if (!haveBuffSpace( uiPacketBodyLen + RFL_PACKET_OVERHEAD))
|
|
{
|
|
if (RC_BAD( rc = flush( m_pCurrentBuf)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Get a pointer to where we will be laying down the packet body.
|
|
|
|
pucPacketBody = getPacketBodyPtr();
|
|
|
|
// Output the transaction ID.
|
|
|
|
UD2FBA( (FLMUINT32)m_uiCurrTransID, pucPacketBody);
|
|
pucPacketBody += 4;
|
|
|
|
// Finish the packet
|
|
|
|
if (RC_BAD( rc = finishPacket( RFL_START_UNKNOWN_PACKET, uiPacketBodyLen,
|
|
FALSE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
m_bLoggingUnknown = TRUE;
|
|
m_uiUnknownPacketLen = RFL_PACKET_OVERHEAD;
|
|
Exit:
|
|
return( rc);
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Log unknown data.
|
|
*********************************************************************/
|
|
RCODE F_Rfl::logUnknown(
|
|
FLMBYTE * pucUnknown,
|
|
FLMUINT uiLen)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
|
|
// Do nothing if logging is disabled. Also, ignore
|
|
// these packets if we are operating on a pre-4.3
|
|
// database.
|
|
|
|
if (m_bLoggingOff ||
|
|
m_pFile->FileHdr.uiVersionNum < FLM_VER_4_3)
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
flmAssert( m_bLoggingUnknown);
|
|
if (RC_BAD( rc = logData( uiLen, pucUnknown,
|
|
RFL_UNKNOWN_PACKET,
|
|
&m_uiUnknownPacketLen,
|
|
NULL, NULL, NULL)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: End logging unknown packets.
|
|
*********************************************************************/
|
|
RCODE F_Rfl::endLoggingUnknown( void)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
|
|
flmAssert( m_pFile);
|
|
|
|
// Do nothing if logging is disabled. Also, ignore
|
|
// these packets if we are operating on a pre-4.3
|
|
// database.
|
|
|
|
if (m_bLoggingOff ||
|
|
m_pFile->FileHdr.uiVersionNum < FLM_VER_4_3)
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Better be in the middle of logging unknown stuff
|
|
// for the application
|
|
|
|
flmAssert( m_bLoggingUnknown);
|
|
if (m_uiUnknownPacketLen > RFL_PACKET_OVERHEAD)
|
|
{
|
|
if (RC_BAD( rc = finishPacket( RFL_UNKNOWN_PACKET,
|
|
m_uiUnknownPacketLen - RFL_PACKET_OVERHEAD,
|
|
FALSE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
m_bLoggingUnknown = FALSE;
|
|
m_uiUnknownPacketLen = RFL_PACKET_OVERHEAD;
|
|
return( rc);
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Log a reduce packet
|
|
*********************************************************************/
|
|
RCODE F_Rfl::logReduce(
|
|
FLMUINT uiTransID,
|
|
FLMUINT uiCount)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FLMBYTE * pucPacketBody;
|
|
FLMUINT uiPacketBodyLen;
|
|
|
|
// This call is new with 4.3 databases - not supported in older
|
|
// versions, so don't log it.
|
|
|
|
if (m_pFile->FileHdr.uiVersionNum < FLM_VER_4_3)
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Do nothing if logging is disabled.
|
|
|
|
if (m_bLoggingOff)
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Better not be in the middle of logging unknown stuff
|
|
// for the application
|
|
|
|
flmAssert( !m_bLoggingUnknown);
|
|
|
|
// We need to set up to log this packet as if we
|
|
// were logging a transaction. The only difference
|
|
// is that we don't log the begin transaction packet.
|
|
|
|
if( RC_BAD( rc = setupTransaction()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
uiPacketBodyLen = 8;
|
|
|
|
// Make sure we have space in the RFL buffer for a complete packet.
|
|
|
|
if (!haveBuffSpace( uiPacketBodyLen + RFL_PACKET_OVERHEAD))
|
|
{
|
|
if (RC_BAD( rc = flush( m_pCurrentBuf)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Get a pointer to where we will be laying down the packet body.
|
|
|
|
pucPacketBody = getPacketBodyPtr();
|
|
|
|
// Output the transaction ID.
|
|
|
|
UD2FBA( (FLMUINT32)uiTransID, pucPacketBody);
|
|
pucPacketBody += 4;
|
|
|
|
// Output the count
|
|
|
|
UD2FBA( (FLMUINT32)uiCount, pucPacketBody);
|
|
pucPacketBody += 4;
|
|
|
|
// Finish the packet
|
|
|
|
if (RC_BAD( rc = finishPacket( RFL_REDUCE_PACKET, uiPacketBodyLen,
|
|
TRUE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Finalize the transaction (as if we were committing a transaction)
|
|
|
|
finalizeTransaction();
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Log a database conversion packet
|
|
Note: This routine performs most of the setup for logging a full
|
|
transaction, but it does not cause begin and commit packets
|
|
to be logged. It is a "standalone" transaction.
|
|
*********************************************************************/
|
|
RCODE F_Rfl::logUpgrade(
|
|
FLMUINT uiTransID,
|
|
FLMUINT uiOldVersion,
|
|
FLMBYTE * pucDBKey,
|
|
FLMUINT32 ui32DBKeyLen)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FLMBYTE * pucPacketBody;
|
|
FLMUINT uiPacketBodyLen;
|
|
|
|
// Do nothing if logging is disabled.
|
|
|
|
if (m_bLoggingOff)
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Better not be in the middle of logging unknown stuff
|
|
// for the application
|
|
|
|
flmAssert( !m_bLoggingUnknown);
|
|
|
|
// We need to set up to log this packet as if we
|
|
// were logging a transaction. The only difference
|
|
// is that we don't log the begin transaction packet.
|
|
|
|
if( RC_BAD( rc = setupTransaction()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
uiPacketBodyLen = 14 + ui32DBKeyLen;
|
|
|
|
// Make sure we have space in the RFL buffer for a complete packet.
|
|
|
|
if (!haveBuffSpace( uiPacketBodyLen + RFL_PACKET_OVERHEAD))
|
|
{
|
|
if (RC_BAD( rc = flush( m_pCurrentBuf)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Get a pointer to where we will be laying down the packet body.
|
|
|
|
pucPacketBody = getPacketBodyPtr();
|
|
|
|
// Output the transaction ID
|
|
|
|
UD2FBA( (FLMUINT32)uiTransID, pucPacketBody);
|
|
pucPacketBody += 4;
|
|
|
|
// Output the old database version
|
|
|
|
UD2FBA( (FLMUINT32)uiOldVersion, pucPacketBody);
|
|
pucPacketBody += 4;
|
|
|
|
// Output the new database version
|
|
|
|
UD2FBA( (FLMUINT32)FLM_CURRENT_VERSION_NUM, pucPacketBody);
|
|
pucPacketBody += 4;
|
|
|
|
// For versions >= 4.60, the next two bytes will give the length of the DB Key.
|
|
|
|
flmAssert( ui32DBKeyLen <= 0xFFFF);
|
|
UW2FBA( (FLMUINT16)ui32DBKeyLen, pucPacketBody);
|
|
pucPacketBody += 2;
|
|
|
|
// If we were built without encryption, the key length will be zero, so no need to
|
|
// store the key.
|
|
|
|
if (ui32DBKeyLen)
|
|
{
|
|
f_memcpy( pucPacketBody, pucDBKey, ui32DBKeyLen);
|
|
pucPacketBody += ui32DBKeyLen;
|
|
}
|
|
|
|
// Finish the packet
|
|
|
|
if (RC_BAD( rc = finishPacket( RFL_UPGRADE_PACKET, uiPacketBodyLen,
|
|
TRUE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Finalize the transaction (as if we were committing a transaction)
|
|
|
|
finalizeTransaction();
|
|
|
|
Exit:
|
|
|
|
if( !m_bLoggingOff)
|
|
{
|
|
m_uiCurrTransID = 0;
|
|
}
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/********************************************************************
|
|
Public: logWrappedKey
|
|
Desc: Log the wrapped database key
|
|
*********************************************************************/
|
|
RCODE F_Rfl::logWrappedKey(
|
|
FLMUINT uiTransID,
|
|
FLMBYTE * pucDBKey,
|
|
FLMUINT32 ui32DBKeyLen
|
|
)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FLMBYTE * pucPacketBody;
|
|
FLMUINT uiPacketBodyLen;
|
|
|
|
// Do nothing if logging is disabled.
|
|
|
|
if (m_bLoggingOff)
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Better not be in the middle of logging unknown stuff
|
|
// for the application
|
|
|
|
flmAssert( !m_bLoggingUnknown);
|
|
|
|
if ( RC_BAD( rc = setupTransaction()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
uiPacketBodyLen = 6 + ui32DBKeyLen;
|
|
|
|
// Make sure we have space in the RFL buffer for a complete packet.
|
|
|
|
if (!haveBuffSpace( uiPacketBodyLen + RFL_PACKET_OVERHEAD))
|
|
{
|
|
if (RC_BAD( rc = flush( m_pCurrentBuf)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Get a pointer to where we will be laying down the packet body.
|
|
|
|
pucPacketBody = getPacketBodyPtr();
|
|
|
|
// Output the transaction ID
|
|
|
|
UD2FBA( (FLMUINT32)uiTransID, pucPacketBody);
|
|
pucPacketBody += 4;
|
|
|
|
// The next two bytes will give the length of the DB Key.
|
|
|
|
flmAssert( ui32DBKeyLen <= 0xFFFF);
|
|
UW2FBA( (FLMUINT16)ui32DBKeyLen, pucPacketBody);
|
|
pucPacketBody += 2;
|
|
|
|
// If we were built without encryption, the key length will be zero, so no need to
|
|
// store the key.
|
|
|
|
if (ui32DBKeyLen)
|
|
{
|
|
f_memcpy( pucPacketBody, pucDBKey, ui32DBKeyLen);
|
|
pucPacketBody += ui32DBKeyLen;
|
|
}
|
|
|
|
// Finish the packet
|
|
|
|
if (RC_BAD( rc = finishPacket( RFL_WRAP_KEY_PACKET,
|
|
uiPacketBodyLen,
|
|
TRUE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
finalizeTransaction();
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/********************************************************************
|
|
Public: logEnableEncryption
|
|
Desc: Log that we have enabled encryption
|
|
*********************************************************************/
|
|
RCODE F_Rfl::logEnableEncryption(
|
|
FLMUINT uiTransID,
|
|
FLMBYTE * pucDBKey,
|
|
FLMUINT32 ui32DBKeyLen
|
|
)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FLMBYTE * pucPacketBody;
|
|
FLMUINT uiPacketBodyLen;
|
|
|
|
// Do nothing if logging is disabled.
|
|
|
|
if (m_bLoggingOff)
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Better not be in the middle of logging unknown stuff
|
|
// for the application
|
|
|
|
flmAssert( !m_bLoggingUnknown);
|
|
|
|
if ( RC_BAD( rc = setupTransaction()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
uiPacketBodyLen = 6 + ui32DBKeyLen;
|
|
|
|
// Make sure we have space in the RFL buffer for a complete packet.
|
|
|
|
if (!haveBuffSpace( uiPacketBodyLen + RFL_PACKET_OVERHEAD))
|
|
{
|
|
if (RC_BAD( rc = flush( m_pCurrentBuf)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Get a pointer to where we will be laying down the packet body.
|
|
|
|
pucPacketBody = getPacketBodyPtr();
|
|
|
|
// Output the transaction ID
|
|
|
|
UD2FBA( (FLMUINT32)uiTransID, pucPacketBody);
|
|
pucPacketBody += 4;
|
|
|
|
// The next two bytes will give the length of the DB Key.
|
|
|
|
flmAssert( ui32DBKeyLen <= 0xFFFF);
|
|
UW2FBA( (FLMUINT16)ui32DBKeyLen, pucPacketBody);
|
|
pucPacketBody += 2;
|
|
|
|
// If we were built without encryption, the key length will be zero, so no need to
|
|
// store the key.
|
|
|
|
if (ui32DBKeyLen)
|
|
{
|
|
f_memcpy( pucPacketBody, pucDBKey, ui32DBKeyLen);
|
|
pucPacketBody += ui32DBKeyLen;
|
|
}
|
|
|
|
// Finish the packet
|
|
|
|
if (RC_BAD( rc = finishPacket( RFL_ENABLE_ENCRYPTION_PACKET,
|
|
uiPacketBodyLen,
|
|
TRUE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
finalizeTransaction();
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Reads a full packet, based on what file offset and read
|
|
offset are currently set to.
|
|
*********************************************************************/
|
|
RCODE F_Rfl::readPacket(
|
|
FLMUINT uiMinBytesNeeded
|
|
)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FLMUINT uiTmpOffset;
|
|
FLMUINT uiReadLen;
|
|
FLMUINT uiBytesRead;
|
|
|
|
// If we have enough bytes in the buffer for the minimum bytes
|
|
// needed, we don't need to retrieve any more bytes.
|
|
|
|
if (m_pCurrentBuf->uiRflBufBytes - m_uiRflReadOffset >= uiMinBytesNeeded)
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// If we are doing restore, we have to do only sequential
|
|
// reads - cannot depend on doing reads on 512 byte boundaries.
|
|
// Otherwise, we read directly from disk on 512 byte boundaries.
|
|
|
|
if (m_pRestore)
|
|
{
|
|
FLMUINT uiCurrFilePos = m_pCurrentBuf->uiRflFileOffset +
|
|
m_pCurrentBuf->uiRflBufBytes;
|
|
|
|
if (m_uiRflReadOffset > 0)
|
|
{
|
|
|
|
// Move the bytes left in the buffer down to the beginning
|
|
// of the buffer.
|
|
|
|
f_memmove( m_pCurrentBuf->pIOBuffer->m_pucBuffer,
|
|
&(m_pCurrentBuf->pIOBuffer->m_pucBuffer[ m_uiRflReadOffset]),
|
|
m_pCurrentBuf->uiRflBufBytes - m_uiRflReadOffset);
|
|
m_pCurrentBuf->uiRflBufBytes -= m_uiRflReadOffset;
|
|
m_pCurrentBuf->uiRflFileOffset += m_uiRflReadOffset;
|
|
m_uiRflReadOffset = 0;
|
|
}
|
|
uiReadLen = m_uiBufferSize - m_pCurrentBuf->uiRflBufBytes;
|
|
|
|
// Read enough to fill the rest of the buffer, which is
|
|
// guaranteed to hold at least one full packet.
|
|
|
|
if (!m_uiFileEOF)
|
|
{
|
|
if (uiCurrFilePos > (FLMUINT)(-1) - uiReadLen)
|
|
{
|
|
uiReadLen = (FLMUINT)(-1) - uiCurrFilePos;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (uiCurrFilePos + uiReadLen > m_uiFileEOF)
|
|
{
|
|
uiReadLen = m_uiFileEOF - uiCurrFilePos;
|
|
}
|
|
}
|
|
|
|
// If reading will not give us the minimum bytes needed,
|
|
// we cannot satisfy this request from the current file.
|
|
|
|
if (uiReadLen + m_pCurrentBuf->uiRflBufBytes < uiMinBytesNeeded)
|
|
{
|
|
rc = RC_SET( FERR_BAD_RFL_PACKET);
|
|
goto Exit;
|
|
}
|
|
|
|
// Read enough to get the entire packet.
|
|
|
|
if (RC_BAD( rc = m_pRestore->read( uiReadLen,
|
|
&(m_pCurrentBuf->pIOBuffer->m_pucBuffer[ m_pCurrentBuf->uiRflBufBytes]),
|
|
&uiBytesRead)))
|
|
{
|
|
if (rc == FERR_IO_END_OF_FILE)
|
|
{
|
|
rc = FERR_OK;
|
|
}
|
|
else
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// If we didn't read enough to satisfy the minimum bytes needed,
|
|
// we cannot satisfy this request from the current file.
|
|
|
|
if (uiBytesRead + m_pCurrentBuf->uiRflBufBytes < uiMinBytesNeeded)
|
|
{
|
|
rc = RC_SET( FERR_BAD_RFL_PACKET);
|
|
goto Exit;
|
|
}
|
|
m_pCurrentBuf->uiRflBufBytes += uiBytesRead;
|
|
}
|
|
else
|
|
{
|
|
|
|
// Set offsets so we are on a 512 byte boundary for our
|
|
// next read. No need to move data, since we will be
|
|
// re-reading it anyway.
|
|
|
|
if (m_uiRflReadOffset > 0)
|
|
{
|
|
uiTmpOffset = m_uiRflReadOffset - (m_uiRflReadOffset & 511);
|
|
m_pCurrentBuf->uiRflFileOffset += uiTmpOffset;
|
|
m_uiRflReadOffset -= uiTmpOffset;
|
|
}
|
|
else if (m_pCurrentBuf->uiRflFileOffset & 511)
|
|
{
|
|
m_uiRflReadOffset = m_pCurrentBuf->uiRflFileOffset & 511;
|
|
m_pCurrentBuf->uiRflFileOffset -= m_uiRflReadOffset;
|
|
}
|
|
m_pCurrentBuf->uiRflBufBytes = 0;
|
|
|
|
// Read enough to fill the rest of the buffer, which is
|
|
// guaranteed to hold at least one full packet.
|
|
|
|
uiReadLen = m_uiBufferSize;
|
|
|
|
// m_uiFileEOF better not be zero at this point - we should
|
|
// always know precisely where the RFL file ends when we
|
|
// are doing recovery as opposed to doing a restore.
|
|
|
|
flmAssert( m_uiFileEOF >= 512);
|
|
if (m_pCurrentBuf->uiRflFileOffset + uiReadLen > m_uiFileEOF)
|
|
{
|
|
uiReadLen = m_uiFileEOF - m_pCurrentBuf->uiRflFileOffset;
|
|
}
|
|
|
|
// If reading will not give us the minimum number of bytes
|
|
// needed, we have a bad packet.
|
|
|
|
if (uiReadLen < m_uiRflReadOffset ||
|
|
uiReadLen - m_uiRflReadOffset < uiMinBytesNeeded)
|
|
{
|
|
rc = RC_SET( FERR_BAD_RFL_PACKET);
|
|
goto Exit;
|
|
}
|
|
|
|
// Read to get the entire packet.
|
|
|
|
if (RC_BAD( rc = m_pFileHdl->SectorRead( m_pCurrentBuf->uiRflFileOffset,
|
|
uiReadLen, m_pCurrentBuf->pIOBuffer->m_pucBuffer,
|
|
&uiBytesRead)))
|
|
{
|
|
if (rc == FERR_IO_END_OF_FILE)
|
|
{
|
|
rc = FERR_OK;
|
|
}
|
|
else
|
|
{
|
|
m_bRflVolumeOk = FALSE;
|
|
goto Exit;
|
|
}
|
|
}
|
|
if (uiBytesRead < uiReadLen)
|
|
{
|
|
rc = RC_SET( FERR_BAD_RFL_PACKET);
|
|
goto Exit;
|
|
}
|
|
m_pCurrentBuf->uiRflBufBytes = uiReadLen;
|
|
}
|
|
Exit:
|
|
return( rc);
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Gets and verifies the next packet from the roll-forward
|
|
log file. Packet checksum will be verified.
|
|
*********************************************************************/
|
|
RCODE F_Rfl::getPacket(
|
|
FLMBOOL bForceNextFile,
|
|
FLMUINT * puiPacketTypeRV,
|
|
FLMBYTE ** ppucPacketBodyRV,
|
|
FLMUINT * puiPacketBodyLenRV,
|
|
FLMBOOL * pbLoggedTimes
|
|
)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FLMBYTE * pucPacket;
|
|
FLMBYTE * pucPacketBody;
|
|
FLMUINT uiPacketType;
|
|
FLMUINT uiEncryptPacketBodyLen;
|
|
FLMBYTE ucHdr [512];
|
|
FLMUINT uiBytesRead;
|
|
|
|
// See if we need to go to the next file. Note that we only
|
|
// check for this exactly on packet boundaries. We do not expect
|
|
// packets to be split across files. If we are not at the end
|
|
// of processing what is in the buffer, we should be able to
|
|
// read the rest of the packet from the current file.
|
|
|
|
Get_Next_File:
|
|
if (bForceNextFile ||
|
|
(m_uiFileEOF && m_uiRflReadOffset == m_pCurrentBuf->uiRflBufBytes &&
|
|
m_pCurrentBuf->uiRflFileOffset + m_pCurrentBuf->uiRflBufBytes ==
|
|
m_uiFileEOF))
|
|
{
|
|
if( m_bKeepRflFiles)
|
|
{
|
|
if (!m_pRestore)
|
|
{
|
|
|
|
// Only doing recovery after a failure, see if we are at
|
|
// the last file already.
|
|
|
|
if (m_pCurrentBuf->uiCurrFileNum == m_uiLastRecoverFileNum)
|
|
{
|
|
rc = RC_SET( FERR_END);
|
|
goto Exit;
|
|
}
|
|
else if( (m_pCurrentBuf->uiCurrFileNum + 1 ) ==
|
|
m_uiLastRecoverFileNum &&
|
|
!(FLMUINT)FB2UD( &m_pFile->ucLastCommittedLogHdr[
|
|
LOG_RFL_LAST_TRANS_OFFSET]))
|
|
{
|
|
// We are going to try to open the last file. Since the log header
|
|
// shows a current offset of 0, the file may have been created but
|
|
// nothing was logged to it. We don't want to try to open
|
|
// it here because it may not have been initialized fully
|
|
// at the time of the server crash.
|
|
|
|
m_pCurrentBuf->uiCurrFileNum = m_uiLastRecoverFileNum;
|
|
rc = RC_SET( FERR_END);
|
|
goto Exit;
|
|
}
|
|
|
|
// Open the next file in the sequence.
|
|
|
|
if (RC_BAD( rc = openFile( m_pCurrentBuf->uiCurrFileNum + 1,
|
|
m_ucNextSerialNum)))
|
|
{
|
|
if (rc == FERR_IO_PATH_NOT_FOUND || rc == FERR_IO_INVALID_PATH)
|
|
{
|
|
rc = RC_SET( FERR_END);
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
// If this is the last RFL file, the EOF is contained
|
|
// in the log header. Otherwise, it will be in the RFL
|
|
// file's header, and openFile will already have retrieved it.
|
|
|
|
if (m_pCurrentBuf->uiCurrFileNum == m_uiLastRecoverFileNum)
|
|
{
|
|
m_uiFileEOF = (FLMUINT)FB2UD(
|
|
&m_pFile->ucLastCommittedLogHdr [LOG_RFL_LAST_TRANS_OFFSET]);
|
|
|
|
// Could be zero if RFL file wasn't created yet.
|
|
|
|
if (!m_uiFileEOF)
|
|
{
|
|
m_uiFileEOF = 512;
|
|
}
|
|
}
|
|
|
|
// By this point, the EOF better be greater than or equal to 512.
|
|
|
|
flmAssert( m_uiFileEOF >= 512);
|
|
}
|
|
else
|
|
{
|
|
if( RC_BAD( rc = m_pRestore->close()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Ask the recovery object to open the file.
|
|
|
|
if (RC_BAD( rc = m_pRestore->openRflFile(
|
|
m_pCurrentBuf->uiCurrFileNum + 1)))
|
|
{
|
|
if (rc == FERR_IO_PATH_NOT_FOUND)
|
|
{
|
|
rc = RC_SET( FERR_END);
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
// Get the first 512 bytes from the file and verify the header.
|
|
|
|
if (RC_BAD( rc = m_pRestore->read( 512, ucHdr, &uiBytesRead)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
if (uiBytesRead < 512)
|
|
{
|
|
rc = RC_SET( FERR_NOT_RFL);
|
|
goto Exit;
|
|
}
|
|
if (RC_BAD( rc = verifyHeader( ucHdr,
|
|
m_pCurrentBuf->uiCurrFileNum + 1,
|
|
m_ucNextSerialNum)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// We may not know the actual EOF of files during restore operations.
|
|
// m_uiFileEOF could be zero here.
|
|
|
|
m_uiFileEOF = (FLMUINT)FB2UD( &ucHdr [RFL_EOF_POS]);
|
|
|
|
// File EOF may be zero or >= 512 at this point.
|
|
|
|
flmAssert( !m_uiFileEOF || m_uiFileEOF >= 512);
|
|
|
|
// Need to increment current file number.
|
|
|
|
m_pCurrentBuf->uiCurrFileNum++;
|
|
}
|
|
m_pCurrentBuf->uiRflFileOffset = 512;
|
|
m_uiRflReadOffset = 0;
|
|
m_pCurrentBuf->uiRflBufBytes = 0;
|
|
|
|
// Get the next packet from the new file.
|
|
|
|
if (RC_BAD( rc = readPacket( RFL_PACKET_OVERHEAD)))
|
|
{
|
|
if (m_uiFileEOF == 512 && m_bKeepRflFiles)
|
|
{
|
|
|
|
// File was empty, try to go to the next file.
|
|
|
|
bForceNextFile = TRUE;
|
|
goto Get_Next_File;
|
|
}
|
|
goto Exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// This is the case where we are not keeping the RFL files.
|
|
// So, there is no next file to go to. If we get to this
|
|
// point, we had better not be doing a restore.
|
|
|
|
flmAssert( m_pRestore == NULL && !bForceNextFile);
|
|
rc = RC_SET( FERR_END);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Make sure we at least have a packet header in the buffer.
|
|
|
|
if (RC_BAD( rc = readPacket( RFL_PACKET_OVERHEAD)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Verify the packet address.
|
|
|
|
m_uiPacketAddress = m_pCurrentBuf->uiRflFileOffset + m_uiRflReadOffset;
|
|
pucPacket = &(m_pCurrentBuf->pIOBuffer->m_pucBuffer[m_uiRflReadOffset]);
|
|
if ((FLMUINT)FB2UD( &pucPacket [RFL_PACKET_ADDRESS_OFFSET]) !=
|
|
m_uiPacketAddress)
|
|
{
|
|
rc = RC_SET( FERR_BAD_RFL_PACKET);
|
|
goto Exit;
|
|
}
|
|
|
|
// Get packet type, time flag, and packet body length
|
|
|
|
*puiPacketTypeRV =
|
|
uiPacketType = RFL_GET_PACKET_TYPE( pucPacket [RFL_PACKET_TYPE_OFFSET]);
|
|
|
|
if( pbLoggedTimes)
|
|
{
|
|
*pbLoggedTimes =
|
|
(pucPacket [RFL_PACKET_TYPE_OFFSET] & RFL_TIME_LOGGED_FLAG)
|
|
? TRUE
|
|
: FALSE;
|
|
}
|
|
|
|
*puiPacketBodyLenRV =
|
|
(FLMUINT)FB2UW( &pucPacket [RFL_PACKET_BODY_LENGTH_OFFSET]);
|
|
|
|
// Adjust the packet body length for encryption if necessary.
|
|
// NOTE: This adjusted length is NOT returned to the caller.
|
|
// The actual body length is what will be returned.
|
|
|
|
uiEncryptPacketBodyLen = getEncryptPacketBodyLen( uiPacketType,
|
|
*puiPacketBodyLenRV);
|
|
|
|
// Make sure we have the entire packet in the buffer.
|
|
|
|
if (RC_BAD( rc = readPacket( uiEncryptPacketBodyLen + RFL_PACKET_OVERHEAD)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
pucPacket = &(m_pCurrentBuf->pIOBuffer->m_pucBuffer[m_uiRflReadOffset]);
|
|
|
|
// At this point, we are guaranteed to have the entire packet
|
|
// in the buffer.
|
|
|
|
*ppucPacketBodyRV =
|
|
pucPacketBody = &pucPacket [RFL_PACKET_OVERHEAD];
|
|
|
|
// Validate the packet checksum
|
|
|
|
if (RflCalcChecksum( pucPacket, uiEncryptPacketBodyLen) !=
|
|
pucPacket [RFL_PACKET_CHECKSUM_OFFSET])
|
|
{
|
|
rc = RC_SET( FERR_BAD_RFL_PACKET);
|
|
goto Exit;
|
|
}
|
|
|
|
if (uiPacketType == RFL_TRNS_BEGIN_PACKET ||
|
|
uiPacketType == RFL_TRNS_BEGIN_EX_PACKET ||
|
|
uiPacketType == RFL_UPGRADE_PACKET ||
|
|
uiPacketType == RFL_REDUCE_PACKET ||
|
|
uiPacketType == RFL_WRAP_KEY_PACKET ||
|
|
uiPacketType == RFL_ENABLE_ENCRYPTION_PACKET)
|
|
{
|
|
// Current transaction ID better be zero, otherwise, we
|
|
// have two or more begin packets in a row.
|
|
|
|
if (m_uiCurrTransID)
|
|
{
|
|
rc = RC_SET( FERR_BAD_RFL_PACKET);
|
|
goto Exit;
|
|
}
|
|
m_uiCurrTransID = (FLMUINT)FB2UD( pucPacketBody);
|
|
pucPacketBody += 4;
|
|
|
|
// Make sure the transaction numbers are ascending
|
|
|
|
if (m_uiCurrTransID <= m_uiLastTransID)
|
|
{
|
|
rc = RC_SET( FERR_BAD_RFL_PACKET);
|
|
goto Exit;
|
|
}
|
|
|
|
if( uiPacketType == RFL_TRNS_BEGIN_EX_PACKET)
|
|
{
|
|
FLMUINT uiLastLoggedCommitTransID;
|
|
|
|
// Skip past seconds
|
|
|
|
pucPacketBody += 4;
|
|
|
|
uiLastLoggedCommitTransID = (FLMUINT)FB2UD( pucPacketBody);
|
|
pucPacketBody += 4;
|
|
|
|
if( m_pFile->FileHdr.uiVersionNum >= FLM_VER_4_31 &&
|
|
m_uiLastLoggedCommitTransID != uiLastLoggedCommitTransID)
|
|
{
|
|
rc = RC_SET( FERR_RFL_TRANS_GAP);
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
// If transaction ID is not zero, we are not inside a
|
|
// transaction, and it is likely that we have a corrupt
|
|
// packet.
|
|
|
|
if (!m_uiCurrTransID)
|
|
{
|
|
rc = RC_SET( FERR_BAD_RFL_PACKET);
|
|
goto Exit;
|
|
}
|
|
|
|
// Decrypt the packet if it is a packet type that was
|
|
// encrypted.
|
|
|
|
if (uiPacketType == RFL_TRNS_COMMIT_PACKET ||
|
|
uiPacketType == RFL_TRNS_ABORT_PACKET)
|
|
{
|
|
if( (FLMUINT)FB2UD( pucPacketBody) != m_uiCurrTransID)
|
|
{
|
|
rc = RC_SET( FERR_BAD_RFL_PACKET);
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set read offset to beginning of next packet.
|
|
|
|
m_uiRflReadOffset += (RFL_PACKET_OVERHEAD + uiEncryptPacketBodyLen);
|
|
Exit:
|
|
return( rc);
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Get a record from the packets in the roll-forward log.
|
|
This expects a series of RFL_DATA_RECORD_PACKETs.
|
|
*********************************************************************/
|
|
RCODE F_Rfl::getRecord(
|
|
FDB * pDb,
|
|
FLMUINT uiPacketType,
|
|
FLMBYTE * pucPacketBody,
|
|
FLMUINT uiPacketBodyLen,
|
|
FlmRecord * pRecord)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FLMUINT uiTagNum;
|
|
FLMUINT uiDataType;
|
|
FLMUINT uiLevel;
|
|
FLMUINT uiDataLen;
|
|
FLMBYTE * pucFieldData = NULL;
|
|
void * pvField;
|
|
FLMBOOL bEncrypted = FALSE;
|
|
FLMUINT uiEncId = 0;
|
|
FLMUINT uiEncDataLen = 0;
|
|
POOL pool;
|
|
|
|
|
|
GedPoolInit( &pool, 512);
|
|
|
|
// Go into a loop processing packets until we have retrieved
|
|
// all of the fields of the record. At that point, we
|
|
// had better be at the end of the record.
|
|
|
|
for (;;)
|
|
{
|
|
|
|
// If we don't currently have a packet, get one
|
|
// Packet type had better be RFL_DATA_RECORD_PACKET.
|
|
|
|
if (!uiPacketBodyLen)
|
|
{
|
|
if (RC_BAD( rc = getPacket( FALSE, &uiPacketType, &pucPacketBody,
|
|
&uiPacketBodyLen, NULL)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
if (uiPacketType != RFL_DATA_RECORD_PACKET &&
|
|
uiPacketType != RFL_ENC_DATA_RECORD_PACKET)
|
|
{
|
|
rc = RC_SET( FERR_BAD_RFL_PACKET);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Packet body length better be at least two or we
|
|
// have an incomplete packet - we need to at least
|
|
// be able to get the tag number at this point.
|
|
|
|
if (uiPacketBodyLen < 2)
|
|
{
|
|
rc = RC_SET( FERR_BAD_RFL_PACKET);
|
|
goto Exit;
|
|
}
|
|
else if (uiPacketBodyLen == 2)
|
|
{
|
|
|
|
// If the packet body length is only two, we
|
|
// had better be at the end of the record with
|
|
// a tag number of zero. Otherwise, we have
|
|
// an incomplete packet.
|
|
|
|
if ((uiTagNum = (FLMUINT)FB2UW( pucPacketBody))!= 0)
|
|
{
|
|
rc = RC_SET( FERR_BAD_RFL_PACKET);
|
|
goto Exit;
|
|
}
|
|
break;
|
|
}
|
|
else if (uiPacketType == RFL_DATA_RECORD_PACKET)
|
|
{
|
|
if (uiPacketBodyLen < 6)
|
|
{
|
|
// If we have a packet body length less than
|
|
// six (for RFL_DATA_RECORD_PACKETs),
|
|
// we have an incomplete field header.
|
|
rc = RC_SET( FERR_BAD_RFL_PACKET);
|
|
goto Exit;
|
|
}
|
|
}
|
|
else if (uiPacketType == RFL_ENC_DATA_RECORD_PACKET)
|
|
{
|
|
// This type of packet is only valid with versions of flaim >= 4.60
|
|
flmAssert( m_pFile->FileHdr.uiVersionNum >= FLM_VER_4_60);
|
|
|
|
if (uiPacketBodyLen < 7)
|
|
{
|
|
// If we have a packet body length less than
|
|
// seven we have an incomplete field header.
|
|
rc = RC_SET( FERR_BAD_RFL_PACKET);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// At this point, we have a packet body length that
|
|
// is greater than or equal to seven (or six), meaning we could
|
|
// not possibly be on the last field of the record.
|
|
// Hence, a zero tag number is invalid here.
|
|
|
|
if ((uiTagNum = (FLMUINT)FB2UW( pucPacketBody)) == 0)
|
|
{
|
|
rc = RC_SET( FERR_BAD_RFL_PACKET);
|
|
goto Exit;
|
|
}
|
|
pucPacketBody += 2;
|
|
uiDataType = *pucPacketBody++;
|
|
uiLevel = *pucPacketBody++;
|
|
uiDataLen = (FLMUINT)FB2UW( pucPacketBody);
|
|
pucPacketBody += 2;
|
|
uiPacketBodyLen -= 6;
|
|
|
|
// If the database version supports encryption,
|
|
// we need to check for it.
|
|
if (uiPacketType == RFL_ENC_DATA_RECORD_PACKET)
|
|
{
|
|
bEncrypted = (FLMBOOL)*pucPacketBody++;
|
|
--uiPacketBodyLen;
|
|
|
|
if (bEncrypted)
|
|
{
|
|
if (uiPacketBodyLen < 4)
|
|
{
|
|
flmAssert( 0);
|
|
rc = RC_SET( FERR_BAD_RFL_PACKET);
|
|
goto Exit;
|
|
}
|
|
|
|
// Extract the encryption ID and the encrypted length.
|
|
uiEncId = FB2UW( pucPacketBody);
|
|
pucPacketBody += 2;
|
|
|
|
uiEncDataLen = FB2UW( pucPacketBody);
|
|
pucPacketBody += 2;
|
|
|
|
uiPacketBodyLen -= 4;
|
|
}
|
|
}
|
|
|
|
// Create a new field.
|
|
|
|
if (RC_BAD( rc = pRecord->insertLast( uiLevel, uiTagNum,
|
|
uiDataType, &pvField)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (!bEncrypted && uiDataLen)
|
|
{
|
|
if (RC_BAD( rc = pRecord->allocStorageSpace( pvField,
|
|
uiDataType,
|
|
uiDataLen,
|
|
0, 0, 0,
|
|
&pucFieldData,
|
|
NULL)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
while (uiDataLen)
|
|
{
|
|
if (uiDataLen > uiPacketBodyLen)
|
|
{
|
|
f_memcpy( pucFieldData, pucPacketBody, uiPacketBodyLen);
|
|
pucFieldData += uiPacketBodyLen;
|
|
pucPacketBody += uiPacketBodyLen;
|
|
uiDataLen -= uiPacketBodyLen;
|
|
|
|
uiPacketBodyLen = 0;
|
|
|
|
// Get the next packet. Packet type had better
|
|
// be RFL_DATA_RECORD_PACKET.
|
|
|
|
if (RC_BAD( rc = getPacket( FALSE, &uiPacketType, &pucPacketBody,
|
|
&uiPacketBodyLen, NULL)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
if (uiPacketType != RFL_DATA_RECORD_PACKET)
|
|
{
|
|
rc = RC_SET( FERR_BAD_RFL_PACKET);
|
|
goto Exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
f_memcpy( pucFieldData, pucPacketBody, uiDataLen);
|
|
pucFieldData += uiDataLen;
|
|
uiPacketBodyLen -= uiDataLen;
|
|
pucPacketBody += uiDataLen;
|
|
uiDataLen = 0;
|
|
}
|
|
}
|
|
pucFieldData = NULL;
|
|
}
|
|
else if (bEncrypted)
|
|
{
|
|
FLMBYTE * pucEncFieldData;
|
|
|
|
if (uiEncDataLen)
|
|
{
|
|
if (RC_BAD( rc = pRecord->allocStorageSpace( pvField,
|
|
uiDataType,
|
|
uiDataLen,
|
|
uiEncDataLen,
|
|
uiEncId,
|
|
FLD_HAVE_ENCRYPTED_DATA,
|
|
&pucFieldData,
|
|
&pucEncFieldData)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
while (uiEncDataLen)
|
|
{
|
|
if (uiEncDataLen > uiPacketBodyLen)
|
|
{
|
|
f_memcpy( pucEncFieldData, pucPacketBody, uiPacketBodyLen);
|
|
pucEncFieldData += uiPacketBodyLen;
|
|
pucPacketBody += uiPacketBodyLen;
|
|
uiEncDataLen -= uiPacketBodyLen;
|
|
|
|
uiPacketBodyLen = 0;
|
|
|
|
// Get the next packet. Packet type had better
|
|
// be RFL_ENC_DATA_RECORD_PACKET.
|
|
|
|
if (RC_BAD( rc = getPacket( FALSE, &uiPacketType, &pucPacketBody,
|
|
&uiPacketBodyLen, NULL)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
if (uiPacketType != RFL_ENC_DATA_RECORD_PACKET)
|
|
{
|
|
rc = RC_SET( FERR_BAD_RFL_PACKET);
|
|
goto Exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
f_memcpy( pucEncFieldData, pucPacketBody, uiEncDataLen);
|
|
pucEncFieldData += uiEncDataLen;
|
|
uiPacketBodyLen -= uiEncDataLen;
|
|
pucPacketBody += uiEncDataLen;
|
|
uiEncDataLen = 0;
|
|
}
|
|
}
|
|
pucEncFieldData = NULL;
|
|
|
|
if (!m_pFile->bInLimitedMode)
|
|
{
|
|
if (RC_BAD( rc = flmDecryptField( pDb->pDict, pRecord,
|
|
pvField, uiEncId, &pool)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
GedPoolFree( &pool);
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Modify a record using RFL_DATA_RECORD_PACKETs or
|
|
RFL_CHANGE_FIELD_PACKETs.
|
|
*********************************************************************/
|
|
RCODE F_Rfl::modifyRecord(
|
|
HFDB hDb,
|
|
FlmRecord * pRecord)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FLMUINT uiPacketType;
|
|
FLMBYTE * pucPacketBody;
|
|
FLMUINT uiPacketBodyLen;
|
|
|
|
FLMUINT uiChangeType;
|
|
FLMUINT uiPosition;
|
|
FLMUINT uiTagNum = 0;
|
|
FLMUINT uiDataType = 0;
|
|
FLMUINT uiLevel = 0;
|
|
FLMUINT uiDataLen = 0;
|
|
FLMBYTE * pucData;
|
|
FLMBYTE * pucEncData;
|
|
FDB * pDb = (FDB *)hDb;
|
|
FLMBOOL bEncrypted = FALSE;
|
|
FLMUINT uiEncDataLen;
|
|
FLMUINT uiEncId;
|
|
FLMUINT uiFlags;
|
|
FlmField * pField;
|
|
FLMUINT uiCurPos = 1;
|
|
void * pvField;
|
|
|
|
// Get the first packet and see what it is.
|
|
// If it is an RFL_DATA_RECORD_PACKET, just
|
|
// call Rfl3GetRecord to get the entire new
|
|
// record.
|
|
|
|
if (RC_BAD( rc = getPacket( FALSE, &uiPacketType, &pucPacketBody,
|
|
&uiPacketBodyLen, NULL)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
if (uiPacketType == RFL_DATA_RECORD_PACKET ||
|
|
uiPacketType == RFL_ENC_DATA_RECORD_PACKET)
|
|
{
|
|
pRecord->clear();
|
|
rc = getRecord( pDb, uiPacketType, pucPacketBody,
|
|
uiPacketBodyLen, pRecord);
|
|
goto Exit;
|
|
}
|
|
else if (uiPacketType != RFL_CHANGE_FIELDS_PACKET)
|
|
{
|
|
rc = RC_SET( FERR_BAD_RFL_PACKET);
|
|
goto Exit;
|
|
}
|
|
|
|
// Go into a loop processing packets until we have processed
|
|
// all of the changed fields for the record.
|
|
|
|
pField = pRecord->getFieldPointer( pRecord->root());
|
|
|
|
flmAssert( pField);
|
|
|
|
for (;;)
|
|
{
|
|
|
|
uiEncDataLen = 0;
|
|
uiEncId = 0;
|
|
|
|
// If we don't currently have a packet, get one
|
|
// Packet type had better be RFL_CHANGE_FIELDS_PACKET.
|
|
|
|
if (!uiPacketBodyLen)
|
|
{
|
|
if (RC_BAD( rc = getPacket( FALSE, &uiPacketType, &pucPacketBody,
|
|
&uiPacketBodyLen, NULL)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
if (uiPacketType != RFL_CHANGE_FIELDS_PACKET)
|
|
{
|
|
rc = RC_SET( FERR_BAD_RFL_PACKET);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Packet body length better be at least three or we
|
|
// have an incomplete packet - we need to at least
|
|
// be able to get the type of change and the absolute
|
|
// position of the change.
|
|
|
|
if (uiPacketBodyLen < 3)
|
|
{
|
|
rc = RC_SET( FERR_BAD_RFL_PACKET);
|
|
goto Exit;
|
|
}
|
|
|
|
// Get the change type and the absolute position where
|
|
// the change is to be put. A position of zero is
|
|
// illegal.
|
|
|
|
uiChangeType = *pucPacketBody++;
|
|
uiPosition = (FLMUINT)FB2UW( pucPacketBody);
|
|
pucPacketBody += 2;
|
|
uiPacketBodyLen -= 3;
|
|
|
|
if (uiChangeType == RFL_END_FIELD_CHANGES)
|
|
{
|
|
|
|
// If we are not at the end of the packet, it must
|
|
// be a bad packet. Also, uiPosition should be
|
|
// a zero for this packet.
|
|
|
|
if (uiPacketBodyLen || uiPosition)
|
|
{
|
|
rc = RC_SET( FERR_BAD_RFL_PACKET);
|
|
goto Exit;
|
|
}
|
|
break;
|
|
}
|
|
|
|
// If not RFL_END_FIELD_CHANGES, a position of
|
|
// zero is illegal.
|
|
|
|
if (!uiPosition)
|
|
{
|
|
rc = RC_SET( FERR_BAD_RFL_PACKET);
|
|
goto Exit;
|
|
}
|
|
|
|
switch (uiChangeType)
|
|
{
|
|
case RFL_INSERT_FIELD:
|
|
case RFL_INSERT_ENC_FIELD:
|
|
{
|
|
if (uiPacketBodyLen < 6)
|
|
{
|
|
|
|
// If the change type is insert field and there are
|
|
// not at least six bytes in the packet, we have
|
|
// a problem.
|
|
|
|
rc = RC_SET( FERR_BAD_RFL_PACKET);
|
|
goto Exit;
|
|
}
|
|
|
|
uiTagNum = (FLMUINT)FB2UW( pucPacketBody);
|
|
pucPacketBody += 2;
|
|
uiDataType = *pucPacketBody++;
|
|
uiLevel = *pucPacketBody++;
|
|
uiDataLen = (FLMUINT)FB2UW( pucPacketBody);
|
|
pucPacketBody += 2;
|
|
uiPacketBodyLen -= 6;
|
|
bEncrypted = (uiChangeType == RFL_INSERT_FIELD ? FALSE : TRUE);
|
|
if (bEncrypted)
|
|
{
|
|
if (uiPacketBodyLen < 4)
|
|
{
|
|
rc = RC_SET( FERR_BAD_RFL_PACKET);
|
|
goto Exit;
|
|
}
|
|
uiEncId = FB2UW(pucPacketBody);
|
|
pucPacketBody += 2;
|
|
|
|
uiEncDataLen = FB2UW( pucPacketBody);
|
|
pucPacketBody += 2;
|
|
|
|
uiPacketBodyLen -= 4;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case RFL_MODIFY_FIELD:
|
|
case RFL_MODIFY_ENC_FIELD:
|
|
{
|
|
// Packet better have at least three bytes and the first
|
|
// byte had better be RFL_REPLACE_BYTES.
|
|
|
|
if (uiPacketBodyLen < 3 ||
|
|
*pucPacketBody != RFL_REPLACE_BYTES)
|
|
{
|
|
rc = RC_SET( FERR_BAD_RFL_PACKET);
|
|
goto Exit;
|
|
}
|
|
pucPacketBody++;
|
|
uiDataLen = (FLMUINT)FB2UW( pucPacketBody);
|
|
pucPacketBody += 2;
|
|
|
|
bEncrypted = (uiChangeType == RFL_MODIFY_FIELD ? FALSE : TRUE);
|
|
uiPacketBodyLen -= 3;
|
|
|
|
if (bEncrypted)
|
|
{
|
|
if (uiPacketBodyLen < 4)
|
|
{
|
|
rc = RC_SET( FERR_BAD_RFL_PACKET);
|
|
goto Exit;
|
|
}
|
|
uiEncId = FB2UW(pucPacketBody);
|
|
pucPacketBody += 2;
|
|
|
|
uiEncDataLen = FB2UW( pucPacketBody);
|
|
pucPacketBody += 2;
|
|
|
|
uiPacketBodyLen -= 4;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case RFL_DELETE_FIELD:
|
|
{
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
// Bad change type in packet.
|
|
|
|
rc = RC_SET( FERR_BAD_RFL_PACKET);
|
|
goto Exit;
|
|
}
|
|
} // Switch
|
|
|
|
// Now position to the target field.
|
|
|
|
switch (uiChangeType)
|
|
{
|
|
case RFL_DELETE_FIELD:
|
|
case RFL_MODIFY_FIELD:
|
|
case RFL_MODIFY_ENC_FIELD:
|
|
{
|
|
while ( uiCurPos != uiPosition)
|
|
{
|
|
if (uiPosition < uiCurPos)
|
|
{
|
|
flmAssert( pField->uiPrev);
|
|
pField = pRecord->prevField( pField);
|
|
--uiCurPos;
|
|
}
|
|
else
|
|
{
|
|
flmAssert( pField->uiNext);
|
|
pField = pRecord->nextField( pField);
|
|
uiCurPos++;
|
|
}
|
|
}
|
|
|
|
if (uiChangeType != RFL_DELETE_FIELD)
|
|
{
|
|
// Get the data type ... not supplied in the modify field packet.
|
|
uiDataType = pRecord->getFieldDataType( pField);
|
|
uiTagNum = pField->ui16FieldID;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case RFL_INSERT_FIELD:
|
|
case RFL_INSERT_ENC_FIELD:
|
|
{
|
|
FlmField * pNewField;
|
|
|
|
// On insert, we may be trying to position to a field that does not exist yet.
|
|
// Therefore we need to position to the field prior to the field position we want to insert.
|
|
|
|
flmAssert( uiPosition > 1); // cannot insert at the root position.
|
|
|
|
while ( uiCurPos != uiPosition - 1)
|
|
{
|
|
if (uiPosition - 1 < uiCurPos)
|
|
{
|
|
flmAssert( pField->uiPrev);
|
|
pField = pRecord->prevField( pField);
|
|
--uiCurPos;
|
|
}
|
|
else
|
|
{
|
|
flmAssert( pField->uiNext);
|
|
pField = pRecord->nextField( pField);
|
|
uiCurPos++;
|
|
}
|
|
}
|
|
|
|
// Insert the new field at the specified position and
|
|
// get back a new field to use later.
|
|
|
|
if( RC_BAD( rc = pRecord->createField( pField, &pNewField)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( RC_BAD( rc = pRecord->setFieldLevel( pNewField, uiLevel)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
pField = pNewField;
|
|
pField->ui16FieldID = (FLMUINT16)uiTagNum;
|
|
uiCurPos++; // Bump the position as we have just added a new field and
|
|
// we are positioned on it.
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (uiChangeType == RFL_DELETE_FIELD)
|
|
{
|
|
// Remove the specified field or subtree
|
|
pvField = (void *)((FLMUINT)(pField->uiPrev));
|
|
--uiCurPos;
|
|
if (!pvField)
|
|
{
|
|
pvField = pRecord->root();
|
|
uiCurPos = 1;
|
|
}
|
|
|
|
// For versions 4.60 and greater, the interpretation for
|
|
// RFL_DELETE_FIELD is to delete the entire sub-tree.
|
|
// Prior to that, it is to delete only a single field.
|
|
|
|
if (m_pFile->FileHdr.uiVersionNum >= FLM_VER_4_60)
|
|
{
|
|
if (RC_BAD( rc = pRecord->remove( pField)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
// Passing in the same pointer to removeFields will effectively
|
|
// delete just pField.
|
|
|
|
if (RC_BAD( rc = pRecord->removeFields( pField, pField)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// We need to reset our pField.
|
|
pField = pRecord->getFieldPointer( pvField);
|
|
|
|
continue; // Next field...
|
|
}
|
|
|
|
// Both insert & modify need to have space allocated.
|
|
|
|
if (bEncrypted)
|
|
{
|
|
uiFlags = FLD_HAVE_ENCRYPTED_DATA;
|
|
}
|
|
else
|
|
{
|
|
uiFlags = 0;
|
|
}
|
|
|
|
flmAssert( pField);
|
|
|
|
// Allocate space for the data. We call this even if uiDataLen is
|
|
// zero so that the appropriate data type will be set in the node
|
|
// as well.
|
|
|
|
// Before we allocate storage space, save the field offset. The
|
|
// field buffer may get reallocated, resulting in a nwe address
|
|
// for pField.
|
|
pvField = pRecord->getFieldVoid( pField);
|
|
if (RC_BAD( rc = pRecord->getNewDataPtr(
|
|
pField,
|
|
uiDataType,
|
|
uiDataLen,
|
|
uiEncDataLen,
|
|
uiEncId,
|
|
uiFlags,
|
|
&pucData,
|
|
&pucEncData)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
pField = pRecord->getFieldPointer( pvField);
|
|
|
|
// Get the data for insert or modify, if any
|
|
|
|
if (bEncrypted)
|
|
{
|
|
while (uiEncDataLen)
|
|
{
|
|
if (uiEncDataLen > uiPacketBodyLen)
|
|
{
|
|
f_memcpy( pucEncData, pucPacketBody, uiPacketBodyLen);
|
|
pucEncData += uiPacketBodyLen;
|
|
uiEncDataLen -= uiPacketBodyLen;
|
|
uiPacketBodyLen = 0;
|
|
|
|
// Get the next packet. Packet type had better
|
|
// be RFL_CHANGE_FIELDS_PACKET.
|
|
|
|
if (RC_BAD( rc = getPacket( FALSE, &uiPacketType, &pucPacketBody,
|
|
&uiPacketBodyLen, NULL)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (uiPacketType != RFL_CHANGE_FIELDS_PACKET)
|
|
{
|
|
rc = RC_SET( FERR_BAD_RFL_PACKET);
|
|
goto Exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
f_memcpy( pucEncData, pucPacketBody, uiEncDataLen);
|
|
pucEncData += uiEncDataLen;
|
|
uiPacketBodyLen -= uiEncDataLen;
|
|
pucPacketBody += uiEncDataLen;
|
|
uiEncDataLen = 0;
|
|
}
|
|
}
|
|
}
|
|
else // Not encrypted
|
|
{
|
|
while (uiDataLen)
|
|
{
|
|
if (uiDataLen > uiPacketBodyLen)
|
|
{
|
|
f_memcpy( pucData, pucPacketBody, uiPacketBodyLen);
|
|
pucData += uiPacketBodyLen;
|
|
uiDataLen -= uiPacketBodyLen;
|
|
uiPacketBodyLen = 0;
|
|
|
|
// Get the next packet. Packet type had better
|
|
// be RFL_CHANGE_FIELDS_PACKET.
|
|
|
|
if (RC_BAD( rc = getPacket( FALSE, &uiPacketType, &pucPacketBody,
|
|
&uiPacketBodyLen, NULL)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (uiPacketType != RFL_CHANGE_FIELDS_PACKET)
|
|
{
|
|
rc = RC_SET( FERR_BAD_RFL_PACKET);
|
|
goto Exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
f_memcpy( pucData, pucPacketBody, uiDataLen);
|
|
pucData += uiDataLen;
|
|
uiPacketBodyLen -= uiDataLen;
|
|
pucPacketBody += uiDataLen;
|
|
uiDataLen = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// If this field is involved in an index, and it is encrypted, we need to
|
|
// make sure we decrypt it too.
|
|
// If it is not encrypted, we don't care if it involved in an index.
|
|
|
|
if (bEncrypted && !(pDb->pFile->bInLimitedMode))
|
|
{
|
|
IFD * pIfd;
|
|
|
|
if( RC_BAD( rc = fdictGetField( pDb->pDict, uiTagNum, NULL,
|
|
&pIfd, NULL)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( pIfd)
|
|
{
|
|
if( RC_BAD( rc = flmDecryptField( pDb->pDict, pRecord,
|
|
pRecord->getFieldVoid(pField), uiEncId, &pDb->TempPool)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Read the next operation from the roll-forward log.
|
|
*********************************************************************/
|
|
RCODE F_Rfl::readOp(
|
|
FDB * pDb,
|
|
FLMBOOL bForceNextFile,
|
|
FLMUINT * puiOpRV,
|
|
FLMUINT * puiContainerRV,
|
|
FLMUINT * puiDrnRV,
|
|
FLMUINT * puiIndexRV,
|
|
FLMUINT * puiEndDrnRV,
|
|
FlmRecord * pRecord,
|
|
FLMUINT * puiTransIDRV,
|
|
FLMUINT * puiStartTimeRV,
|
|
FLMUINT * puiLastLoggedCommitTransIDRV,
|
|
FLMUINT * puiFlagsRV)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FLMUINT uiPacketType;
|
|
FLMBYTE * pucPacketBody;
|
|
FLMUINT uiPacketBodyLen;
|
|
FLMUINT uiExpectedBodyLen;
|
|
FLMUINT uiLastLoggedCommitTransID = 0;
|
|
FLMUINT uiTransId;
|
|
FLMUINT uiStartTime;
|
|
FLMUINT uiContainer;
|
|
FLMUINT uiDrn;
|
|
FLMUINT uiIndex;
|
|
FLMUINT uiEndDrn;
|
|
FLMUINT uiFlags = 0;
|
|
FLMBOOL bLoggedTimes;
|
|
|
|
// Get the next packet.
|
|
|
|
if (RC_BAD( rc = getPacket( bForceNextFile, &uiPacketType, &pucPacketBody,
|
|
&uiPacketBodyLen, &bLoggedTimes)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Must be one of our packet types that represents
|
|
// an operation.
|
|
|
|
uiTransId = 0;
|
|
uiStartTime = 0;
|
|
uiContainer = 0;
|
|
uiDrn = 0;
|
|
uiIndex = 0;
|
|
uiEndDrn = 0;
|
|
switch (uiPacketType)
|
|
{
|
|
case RFL_TRNS_BEGIN_PACKET:
|
|
{
|
|
uiExpectedBodyLen = 8;
|
|
if( bLoggedTimes)
|
|
{
|
|
uiExpectedBodyLen += 4;
|
|
}
|
|
if (uiExpectedBodyLen != uiPacketBodyLen)
|
|
{
|
|
rc = RC_SET( FERR_BAD_RFL_PACKET);
|
|
goto Exit;
|
|
}
|
|
uiTransId = (FLMUINT)FB2UD( pucPacketBody);
|
|
pucPacketBody += 4;
|
|
uiStartTime = (FLMUINT)FB2UD( pucPacketBody);
|
|
pucPacketBody += 4;
|
|
break;
|
|
}
|
|
|
|
case RFL_TRNS_BEGIN_EX_PACKET:
|
|
{
|
|
uiExpectedBodyLen = 12;
|
|
if (uiExpectedBodyLen != uiPacketBodyLen)
|
|
{
|
|
rc = RC_SET( FERR_BAD_RFL_PACKET);
|
|
goto Exit;
|
|
}
|
|
uiTransId = (FLMUINT)FB2UD( pucPacketBody);
|
|
pucPacketBody += 4;
|
|
uiStartTime = (FLMUINT)FB2UD( pucPacketBody);
|
|
pucPacketBody += 4;
|
|
uiLastLoggedCommitTransID = (FLMUINT)FB2UD( pucPacketBody);
|
|
pucPacketBody += 4;
|
|
break;
|
|
}
|
|
|
|
case RFL_TRNS_COMMIT_PACKET:
|
|
case RFL_TRNS_ABORT_PACKET:
|
|
{
|
|
uiExpectedBodyLen = 8;
|
|
if( bLoggedTimes)
|
|
{
|
|
uiExpectedBodyLen += 8;
|
|
}
|
|
|
|
if (uiExpectedBodyLen != uiPacketBodyLen)
|
|
{
|
|
rc = RC_SET( FERR_BAD_RFL_PACKET);
|
|
goto Exit;
|
|
}
|
|
uiTransId = (FLMUINT)FB2UD( pucPacketBody);
|
|
pucPacketBody += 8;
|
|
break;
|
|
}
|
|
|
|
case RFL_ADD_RECORD_PACKET:
|
|
case RFL_MODIFY_RECORD_PACKET:
|
|
case RFL_DELETE_RECORD_PACKET:
|
|
case RFL_RESERVE_DRN_PACKET:
|
|
{
|
|
uiExpectedBodyLen = 10;
|
|
if( bLoggedTimes)
|
|
{
|
|
uiExpectedBodyLen += 16;
|
|
}
|
|
if (uiExpectedBodyLen != uiPacketBodyLen)
|
|
{
|
|
rc = RC_SET( FERR_BAD_RFL_PACKET);
|
|
goto Exit;
|
|
}
|
|
if ((uiTransId = (FLMUINT)FB2UD( pucPacketBody)) != m_uiCurrTransID)
|
|
{
|
|
rc = RC_SET( FERR_BAD_RFL_PACKET);
|
|
goto Exit;
|
|
}
|
|
pucPacketBody += 4;
|
|
uiContainer = (FLMUINT)FB2UW( pucPacketBody);
|
|
pucPacketBody += 2;
|
|
uiDrn = (FLMUINT)FB2UD( pucPacketBody);
|
|
pucPacketBody += 4;
|
|
if( uiPacketType == RFL_ADD_RECORD_PACKET)
|
|
{
|
|
if (RC_BAD( rc = getRecord( pDb, 0, NULL, 0, pRecord)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case RFL_ADD_RECORD_PACKET_VER_2:
|
|
case RFL_MODIFY_RECORD_PACKET_VER_2:
|
|
case RFL_DELETE_RECORD_PACKET_VER_2:
|
|
{
|
|
uiExpectedBodyLen = 11;
|
|
if( bLoggedTimes)
|
|
{
|
|
uiExpectedBodyLen += 16;
|
|
}
|
|
|
|
if (uiExpectedBodyLen != uiPacketBodyLen)
|
|
{
|
|
rc = RC_SET( FERR_BAD_RFL_PACKET);
|
|
goto Exit;
|
|
}
|
|
|
|
if ((uiTransId = (FLMUINT)FB2UD( pucPacketBody)) != m_uiCurrTransID)
|
|
{
|
|
rc = RC_SET( FERR_BAD_RFL_PACKET);
|
|
goto Exit;
|
|
}
|
|
pucPacketBody += 4;
|
|
|
|
uiContainer = (FLMUINT)FB2UW( pucPacketBody);
|
|
pucPacketBody += 2;
|
|
|
|
uiDrn = (FLMUINT)FB2UD( pucPacketBody);
|
|
pucPacketBody += 4;
|
|
|
|
uiFlags = *pucPacketBody;
|
|
pucPacketBody++;
|
|
|
|
// Translate the flags
|
|
|
|
if( uiFlags)
|
|
{
|
|
FLMUINT uiTmp = 0;
|
|
|
|
if( uiFlags & RFL_UPDATE_BACKGROUND)
|
|
{
|
|
uiTmp |= FLM_DO_IN_BACKGROUND;
|
|
}
|
|
|
|
if( uiFlags & RFL_UPDATE_SUSPENDED)
|
|
{
|
|
uiTmp |= FLM_SUSPENDED;
|
|
}
|
|
|
|
uiFlags = uiTmp;
|
|
}
|
|
|
|
if( uiPacketType == RFL_ADD_RECORD_PACKET_VER_2)
|
|
{
|
|
if (RC_BAD( rc = getRecord( pDb, 0, NULL, 0, pRecord)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case RFL_INDEX_SET_PACKET:
|
|
case RFL_INDEX_SET_PACKET_VER_2:
|
|
{
|
|
uiExpectedBodyLen =
|
|
(FLMUINT)((uiPacketType == RFL_INDEX_SET_PACKET_VER_2)
|
|
? (FLMUINT)16
|
|
: (FLMUINT)14);
|
|
|
|
if( bLoggedTimes)
|
|
{
|
|
uiExpectedBodyLen += 16;
|
|
}
|
|
if (uiExpectedBodyLen != uiPacketBodyLen)
|
|
{
|
|
rc = RC_SET( FERR_BAD_RFL_PACKET);
|
|
goto Exit;
|
|
}
|
|
if ((uiTransId = (FLMUINT)FB2UD( pucPacketBody)) != m_uiCurrTransID)
|
|
{
|
|
rc = RC_SET( FERR_BAD_RFL_PACKET);
|
|
goto Exit;
|
|
}
|
|
pucPacketBody += 4;
|
|
if (uiPacketType == RFL_INDEX_SET_PACKET_VER_2)
|
|
{
|
|
uiContainer = (FLMUINT)FB2UW( pucPacketBody);
|
|
pucPacketBody += 2;
|
|
}
|
|
uiIndex = (FLMUINT)FB2UW( pucPacketBody);
|
|
pucPacketBody += 2;
|
|
uiDrn = (FLMUINT)FB2UD( pucPacketBody);
|
|
pucPacketBody += 4;
|
|
uiEndDrn = (FLMUINT)FB2UD( pucPacketBody);
|
|
pucPacketBody += 4;
|
|
break;
|
|
}
|
|
|
|
case RFL_START_UNKNOWN_PACKET:
|
|
{
|
|
uiExpectedBodyLen = 4;
|
|
if (uiExpectedBodyLen != uiPacketBodyLen)
|
|
{
|
|
rc = RC_SET( FERR_BAD_RFL_PACKET);
|
|
goto Exit;
|
|
}
|
|
if ((uiTransId = (FLMUINT)FB2UD( pucPacketBody)) != m_uiCurrTransID)
|
|
{
|
|
rc = RC_SET( FERR_BAD_RFL_PACKET);
|
|
goto Exit;
|
|
}
|
|
pucPacketBody += 4;
|
|
break;
|
|
}
|
|
|
|
case RFL_REDUCE_PACKET:
|
|
{
|
|
uiExpectedBodyLen = 8;
|
|
if (uiExpectedBodyLen != uiPacketBodyLen)
|
|
{
|
|
rc = RC_SET( FERR_BAD_RFL_PACKET);
|
|
goto Exit;
|
|
}
|
|
uiTransId = (FLMUINT)FB2UD( pucPacketBody);
|
|
uiLastLoggedCommitTransID = uiTransId;
|
|
pucPacketBody += 4;
|
|
|
|
// We will return the count in the uiDrn parameter
|
|
|
|
uiDrn = (FLMUINT)FB2UD( pucPacketBody);
|
|
pucPacketBody += 4;
|
|
break;
|
|
}
|
|
|
|
case RFL_BLK_CHAIN_FREE_PACKET:
|
|
{
|
|
uiExpectedBodyLen = 16;
|
|
|
|
if( bLoggedTimes)
|
|
{
|
|
uiExpectedBodyLen += 16;
|
|
}
|
|
|
|
if( uiExpectedBodyLen != uiPacketBodyLen)
|
|
{
|
|
rc = RC_SET( FERR_BAD_RFL_PACKET);
|
|
goto Exit;
|
|
}
|
|
|
|
if( (uiTransId = (FLMUINT)FB2UD( pucPacketBody)) != m_uiCurrTransID)
|
|
{
|
|
rc = RC_SET( FERR_BAD_RFL_PACKET);
|
|
goto Exit;
|
|
}
|
|
pucPacketBody += 4;
|
|
|
|
// Tracker record ID
|
|
|
|
uiDrn = (FLMUINT)FB2UD( pucPacketBody);
|
|
pucPacketBody += 4;
|
|
|
|
// Count
|
|
|
|
uiFlags = (FLMUINT)FB2UD( pucPacketBody);
|
|
pucPacketBody += 4;
|
|
|
|
// Ending block address
|
|
|
|
uiEndDrn = (FLMUINT)FB2UD( pucPacketBody);
|
|
pucPacketBody += 4;
|
|
|
|
break;
|
|
}
|
|
|
|
case RFL_INDEX_SUSPEND_PACKET:
|
|
case RFL_INDEX_RESUME_PACKET:
|
|
{
|
|
uiExpectedBodyLen = 6;
|
|
if (uiExpectedBodyLen != uiPacketBodyLen)
|
|
{
|
|
rc = RC_SET( FERR_BAD_RFL_PACKET);
|
|
goto Exit;
|
|
}
|
|
|
|
if ((uiTransId = (FLMUINT)FB2UD( pucPacketBody)) != m_uiCurrTransID)
|
|
{
|
|
rc = RC_SET( FERR_BAD_RFL_PACKET);
|
|
goto Exit;
|
|
}
|
|
pucPacketBody += 4;
|
|
|
|
uiIndex = (FLMUINT)FB2UW( pucPacketBody);
|
|
pucPacketBody += 2;
|
|
break;
|
|
}
|
|
|
|
case RFL_UPGRADE_PACKET:
|
|
{
|
|
FLMUINT uiDBKeyLen;
|
|
|
|
uiExpectedBodyLen = 12;
|
|
if (uiExpectedBodyLen > uiPacketBodyLen)
|
|
{
|
|
rc = RC_SET( FERR_BAD_RFL_PACKET);
|
|
goto Exit;
|
|
}
|
|
|
|
// We will return the current DB version in the uiDrn parameter
|
|
// and the new DB version will be returned in the uiEndDrn parameter
|
|
|
|
uiTransId = (FLMUINT)FB2UD( pucPacketBody);
|
|
pucPacketBody += 4;
|
|
uiPacketBodyLen -= 4;
|
|
uiDrn = (FLMUINT)FB2UD( pucPacketBody);
|
|
pucPacketBody += 4;
|
|
uiPacketBodyLen -= 4;
|
|
uiEndDrn = (FLMUINT)FB2UD( pucPacketBody);
|
|
pucPacketBody += 4;
|
|
uiPacketBodyLen -= 4;
|
|
|
|
// Only look for the wrapping key if the new database version
|
|
// is greater than 4.60 and there isn't already a key.
|
|
if (uiEndDrn >= FLM_VER_4_60 && !m_pFile->pDbWrappingKey)
|
|
{
|
|
if (uiPacketBodyLen < 2)
|
|
{
|
|
rc = RC_SET( FERR_BAD_RFL_PACKET);
|
|
goto Exit;
|
|
}
|
|
uiDBKeyLen = FB2UW( pucPacketBody);
|
|
pucPacketBody += 2;
|
|
uiPacketBodyLen -= 2;
|
|
if ( uiDBKeyLen)
|
|
{
|
|
if ( uiPacketBodyLen != uiDBKeyLen)
|
|
{
|
|
rc = RC_SET( FERR_BAD_RFL_PACKET);
|
|
goto Exit;
|
|
}
|
|
|
|
if ((m_pFile->pDbWrappingKey = f_new F_CCS) == NULL)
|
|
{
|
|
rc = RC_SET( FERR_MEM);
|
|
goto Exit;
|
|
}
|
|
|
|
if (RC_BAD( rc = m_pFile->pDbWrappingKey->init( TRUE,
|
|
FLM_NICI_AES)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( RC_BAD( rc = m_pFile->pDbWrappingKey->setKeyFromStore(
|
|
pucPacketBody, (FLMUINT32)uiDBKeyLen, NULL, NULL, FALSE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
pucPacketBody += uiDBKeyLen;
|
|
uiPacketBodyLen -= uiDBKeyLen;
|
|
flmAssert( !uiPacketBodyLen);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case RFL_WRAP_KEY_PACKET:
|
|
case RFL_ENABLE_ENCRYPTION_PACKET:
|
|
{
|
|
FLMUINT uiDBKeyLen;
|
|
FLMBYTE * pucUncommittedLogHdr = &m_pFile->ucUncommittedLogHdr [0];
|
|
eRestoreActionType eRestoreAction;
|
|
|
|
uiExpectedBodyLen = 6;
|
|
if (uiExpectedBodyLen >= uiPacketBodyLen)
|
|
{
|
|
rc = RC_SET( FERR_BAD_RFL_PACKET);
|
|
goto Exit;
|
|
}
|
|
|
|
uiTransId = (FLMUINT)FB2UD( pucPacketBody);
|
|
uiLastLoggedCommitTransID = uiTransId;
|
|
pucPacketBody += 4;
|
|
uiPacketBodyLen -= 4;
|
|
|
|
if (uiPacketBodyLen < 2)
|
|
{
|
|
rc = RC_SET( FERR_BAD_RFL_PACKET);
|
|
goto Exit;
|
|
}
|
|
uiDBKeyLen = FB2UW( pucPacketBody);
|
|
pucPacketBody += 2;
|
|
uiPacketBodyLen -= 2;
|
|
|
|
if (m_pRestore)
|
|
{
|
|
if (RC_BAD( rc = m_pRestore->status(
|
|
uiPacketType == RFL_WRAP_KEY_PACKET
|
|
? RESTORE_WRAP_KEY
|
|
: RESTORE_ENABLE_ENCRYPTION,
|
|
uiTransId,
|
|
(void *)uiDBKeyLen,
|
|
(void *)0,
|
|
(void *)0,
|
|
&eRestoreAction)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (eRestoreAction == RESTORE_ACTION_STOP)
|
|
{
|
|
m_uiCurrTransID = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( uiDBKeyLen)
|
|
{
|
|
if ( uiPacketBodyLen != uiDBKeyLen)
|
|
{
|
|
rc = RC_SET( FERR_BAD_RFL_PACKET);
|
|
goto Exit;
|
|
}
|
|
|
|
// We cannot directly set the key at this point as it may be
|
|
// encrypted using a password, which we do not have here. We will
|
|
// write the key out to the log header and trust the user to know whether
|
|
// or not a password is needed to open the database.
|
|
|
|
if (RC_BAD(rc = flmBeginDbTrans( pDb, FLM_UPDATE_TRANS,
|
|
0, 0)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
f_memcpy( &pucUncommittedLogHdr[ LOG_DATABASE_KEY], pucPacketBody, uiDBKeyLen);
|
|
UW2FBA( uiDBKeyLen, &pucUncommittedLogHdr[ LOG_DATABASE_KEY_LEN]);
|
|
|
|
if ( RC_BAD( rc = flmCommitDbTrans( pDb, 0, TRUE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
pucPacketBody += uiDBKeyLen;
|
|
uiPacketBodyLen -= uiDBKeyLen;
|
|
flmAssert( !uiPacketBodyLen);
|
|
}
|
|
m_uiCurrTransID = 0;
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
flmAssert( 0);
|
|
rc = RC_SET( FERR_BAD_RFL_PACKET);
|
|
break;
|
|
}
|
|
}
|
|
|
|
*puiOpRV = uiPacketType;
|
|
*puiContainerRV = uiContainer;
|
|
*puiDrnRV = uiDrn;
|
|
*puiIndexRV = uiIndex;
|
|
*puiEndDrnRV = uiEndDrn;
|
|
*puiTransIDRV = uiTransId;
|
|
*puiStartTimeRV = uiStartTime;
|
|
*puiLastLoggedCommitTransIDRV = uiLastLoggedCommitTransID;
|
|
*puiFlagsRV = uiFlags;
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Reads through unknown packets.
|
|
*********************************************************************/
|
|
RCODE F_Rfl::readUnknown(
|
|
FLMUINT uiLenToRead,
|
|
FLMBYTE * pucBuffer,
|
|
FLMUINT * puiBytesRead)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FLMUINT uiPacketType;
|
|
FLMUINT uiBytesRead = 0;
|
|
FLMUINT uiBytesToCopy;
|
|
|
|
// If we have read through all of the unknown packets,
|
|
// return FERR_EOF_HIT.
|
|
|
|
if (!m_bReadingUnknown)
|
|
{
|
|
rc = RC_SET( FERR_EOF_HIT);
|
|
goto Exit;
|
|
}
|
|
|
|
// Process packets until we have satisfied the read request or
|
|
// until we run out of unknown packets.
|
|
|
|
while (uiLenToRead)
|
|
{
|
|
|
|
// Get a packet, if we don't have one.
|
|
|
|
if (!m_uiUnknownPacketBodyLen)
|
|
{
|
|
if (RC_BAD( rc = getPacket( FALSE, &uiPacketType,
|
|
&m_pucUnknownPacketBody,
|
|
&m_uiUnknownPacketBodyLen, NULL)))
|
|
{
|
|
m_bReadingUnknown = FALSE;
|
|
m_uiUnknownPacketRc = rc;
|
|
goto Exit;
|
|
}
|
|
if (uiPacketType != RFL_UNKNOWN_PACKET)
|
|
{
|
|
if (!uiBytesRead)
|
|
{
|
|
rc = RC_SET( FERR_EOF_HIT);
|
|
}
|
|
m_bReadingUnknown = FALSE;
|
|
|
|
// At this point, we know that the entire packet is
|
|
// inside our memory buffer, so it is safe to reset
|
|
// m_uiRflReadOffset back to the beginning of the
|
|
// packet. The call to readOp() will call
|
|
// getPacket again, which will get this exact same
|
|
// packet for processing.
|
|
|
|
m_uiRflReadOffset -= (RFL_PACKET_OVERHEAD +
|
|
m_uiUnknownPacketBodyLen);
|
|
goto Exit;
|
|
}
|
|
m_uiUnknownBodyLenProcessed = 0;
|
|
}
|
|
|
|
uiBytesToCopy = uiLenToRead;
|
|
if (uiBytesToCopy > m_uiUnknownPacketBodyLen - m_uiUnknownBodyLenProcessed)
|
|
{
|
|
uiBytesToCopy = m_uiUnknownPacketBodyLen - m_uiUnknownBodyLenProcessed;
|
|
}
|
|
f_memcpy( pucBuffer,
|
|
m_pucUnknownPacketBody + m_uiUnknownBodyLenProcessed,
|
|
uiBytesToCopy);
|
|
pucBuffer += uiBytesToCopy;
|
|
uiLenToRead -= uiBytesToCopy;
|
|
uiBytesRead += uiBytesToCopy;
|
|
m_uiUnknownBodyLenProcessed += uiBytesToCopy;
|
|
|
|
// If we have exhausted the current packet, reset things so that
|
|
// we will get a new packet the next time around.
|
|
|
|
if (m_uiUnknownBodyLenProcessed == m_uiUnknownPacketBodyLen)
|
|
{
|
|
m_uiUnknownPacketBodyLen = 0;
|
|
m_uiUnknownBodyLenProcessed = 0;
|
|
m_pucUnknownPacketBody = NULL;
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
*puiBytesRead = uiBytesRead;
|
|
return( rc);
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Restore transactions from the roll-forward log to the
|
|
database.
|
|
*********************************************************************/
|
|
RCODE F_Rfl::recover(
|
|
FDB * pDb,
|
|
F_Restore * pRestore)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FLMUINT uiStartFileNum;
|
|
FLMUINT uiStartOffset;
|
|
FLMUINT uiOffset;
|
|
FLMUINT uiReadLen;
|
|
FLMUINT uiBytesRead;
|
|
FLMBYTE ucHdr [512];
|
|
FLMUINT uiOp;
|
|
FLMUINT uiContainer;
|
|
FLMUINT uiDrn;
|
|
FLMUINT uiIndex;
|
|
FLMUINT uiEndDrn;
|
|
FLMUINT uiStartTime;
|
|
FLMUINT uiCount;
|
|
FlmRecord * pRecord = NULL;
|
|
FlmRecord * pTmpRecord = NULL;
|
|
HFDB hDb = (HFDB)pDb;
|
|
FLMUINT uiTransId;
|
|
eRestoreActionType eRestoreAction;
|
|
FLMUINT uiLastLoggedCommitTransID;
|
|
FLMBOOL bTransActive = FALSE;
|
|
FLMBOOL bHadOperations = FALSE;
|
|
FLMBOOL bLastTransEndedAtFileEOF = FALSE;
|
|
FLMBOOL bForceNextFile;
|
|
FLMUINT uiOpFlags;
|
|
|
|
flmAssert( m_pFile);
|
|
|
|
m_pCurrentBuf = &m_Buf1;
|
|
m_uiLastLoggedCommitTransID = 0;
|
|
|
|
// We need to allow all updates logged in the RFL
|
|
// (including dictionary updates).
|
|
pDb->bFldStateUpdOk = TRUE;
|
|
|
|
// If we are less than version 4.3, we cannot do restore.
|
|
|
|
if (pRestore && m_pFile->FileHdr.uiVersionNum < FLM_VER_4_3)
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Turn off logging.
|
|
|
|
m_bLoggingOff = TRUE;
|
|
|
|
// Set the replay flag on the database.
|
|
|
|
pDb->uiFlags |= FDB_REPLAYING_RFL;
|
|
|
|
// Set the flag as to whether or not we are using multiple RFL files.
|
|
|
|
if (m_pFile->FileHdr.uiVersionNum < FLM_VER_4_3)
|
|
{
|
|
m_bKeepRflFiles = FALSE;
|
|
}
|
|
else
|
|
{
|
|
m_bKeepRflFiles = m_pFile->ucLastCommittedLogHdr [LOG_KEEP_RFL_FILES]
|
|
? TRUE
|
|
: FALSE;
|
|
}
|
|
|
|
// If pRestore is NULL, we are doing a database recovery after
|
|
// open. In that case, we start from the last checkpoint offset
|
|
// and only run until the last transaction offset.
|
|
//
|
|
|
|
if ((m_pRestore = pRestore) == NULL)
|
|
{
|
|
FLMBYTE * pucCheckSerialNum;
|
|
FLMUINT uiEndOffset;
|
|
|
|
uiStartFileNum = (FLMUINT)FB2UD(
|
|
&m_pFile->ucLastCommittedLogHdr [LOG_RFL_LAST_CP_FILE_NUM]);
|
|
m_uiLastRecoverFileNum = (FLMUINT)FB2UD(
|
|
&m_pFile->ucLastCommittedLogHdr [LOG_RFL_FILE_NUM]);
|
|
uiStartOffset = (FLMUINT)FB2UD(
|
|
&m_pFile->ucLastCommittedLogHdr [LOG_RFL_LAST_CP_OFFSET]);
|
|
uiEndOffset = (FLMUINT)FB2UD(
|
|
&m_pFile->ucLastCommittedLogHdr [LOG_RFL_LAST_TRANS_OFFSET]);
|
|
|
|
// Could be zero if the file was created, but no transactions were
|
|
// ever committed to it.
|
|
|
|
if (!uiEndOffset)
|
|
{
|
|
uiEndOffset = 512;
|
|
}
|
|
|
|
// Start offset better not be less than 512.
|
|
|
|
flmAssert( uiStartOffset >= 512);
|
|
flmAssert( uiEndOffset >= 512);
|
|
|
|
// If start and end are at the same place, there is nothing
|
|
// to recover.
|
|
|
|
if (uiStartFileNum == m_uiLastRecoverFileNum &&
|
|
uiStartOffset == uiEndOffset)
|
|
{
|
|
goto Finish_Recovery;
|
|
}
|
|
|
|
// We have not recorded the serial number of the last checkpoint file
|
|
// number, so we pass in NULL, unless it happens to be the same as the
|
|
// last transaction file number, in which case we can pass in the
|
|
// serial number we have stored in the log header.
|
|
|
|
pucCheckSerialNum =
|
|
(uiStartFileNum == m_uiLastRecoverFileNum)
|
|
? &m_pFile->ucLastCommittedLogHdr [LOG_LAST_TRANS_RFL_SERIAL_NUM]
|
|
: NULL;
|
|
if (RC_BAD( rc = openFile( uiStartFileNum, pucCheckSerialNum)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// If this is the last RFL file, the EOF is contained
|
|
// in the log header. Otherwise, it will be in the RFL
|
|
// file's header, and openFile will already have retrieved it.
|
|
|
|
if (uiStartFileNum == m_uiLastRecoverFileNum)
|
|
{
|
|
m_uiFileEOF = uiEndOffset;
|
|
}
|
|
|
|
// At this point, file EOF better be greater than or equal to 512.
|
|
|
|
flmAssert( m_uiFileEOF >= 512);
|
|
}
|
|
else if (!m_bKeepRflFiles)
|
|
{
|
|
// FlmDbRestore should be checking the "keep" flag and not
|
|
// attempting to do a restore of the RFL.
|
|
|
|
flmAssert( 0);
|
|
rc = RC_SET( FERR_CANNOT_RESTORE_RFL_FILES);
|
|
goto Exit;
|
|
}
|
|
else
|
|
{
|
|
uiStartFileNum = (FLMUINT)FB2UD(
|
|
&m_pFile->ucLastCommittedLogHdr [LOG_RFL_FILE_NUM]);
|
|
uiStartOffset = (FLMUINT)FB2UD(
|
|
&m_pFile->ucLastCommittedLogHdr [LOG_RFL_LAST_TRANS_OFFSET]);
|
|
|
|
// Could be zero if the RFL file had never been created.
|
|
|
|
if (!uiStartOffset)
|
|
{
|
|
uiStartOffset = 512;
|
|
}
|
|
|
|
// Ask the recovery object to open the file.
|
|
|
|
Retry_Open:
|
|
|
|
flmAssert( uiStartFileNum);
|
|
if (RC_BAD( rc = m_pRestore->openRflFile( uiStartFileNum)))
|
|
{
|
|
if( rc == FERR_IO_PATH_NOT_FOUND)
|
|
{
|
|
// Need to set m_pCurrentBuf->uiCurrFileNum in case the first
|
|
// call to openRflFile fails. This will cause the code at the
|
|
// Finish_Recovery label to correctly set up the log
|
|
// header.
|
|
|
|
if( !uiStartOffset)
|
|
{
|
|
m_pCurrentBuf->uiCurrFileNum = uiStartFileNum - 1;
|
|
}
|
|
else
|
|
{
|
|
m_pCurrentBuf->uiCurrFileNum = uiStartFileNum;
|
|
}
|
|
|
|
rc = FERR_OK;
|
|
goto Finish_Recovery;
|
|
}
|
|
else
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Get the first 512 bytes from the file and verify the header.
|
|
|
|
if (RC_BAD( rc = m_pRestore->read( 512, ucHdr, &uiBytesRead)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (uiBytesRead < 512)
|
|
{
|
|
rc = RC_SET( FERR_NOT_RFL);
|
|
goto Exit;
|
|
}
|
|
|
|
if (RC_BAD( rc = verifyHeader( ucHdr, uiStartFileNum,
|
|
&m_pFile->ucLastCommittedLogHdr [LOG_LAST_TRANS_RFL_SERIAL_NUM])))
|
|
{
|
|
RCODE tmpRc;
|
|
|
|
if (RC_BAD( tmpRc = m_pRestore->status(
|
|
RESTORE_ERROR,
|
|
0,
|
|
(void *)((FLMUINT)rc),
|
|
(void *)0,
|
|
(void *)0, &eRestoreAction)))
|
|
{
|
|
rc = tmpRc;
|
|
goto Exit;
|
|
}
|
|
|
|
if (eRestoreAction == RESTORE_ACTION_RETRY)
|
|
{
|
|
if( RC_BAD( rc = m_pRestore->close()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
goto Retry_Open;
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
// We may not know the actual EOF of files during restore operations.
|
|
|
|
if ((m_uiFileEOF = (FLMUINT)FB2UD( &ucHdr [RFL_EOF_POS])) == 0)
|
|
{
|
|
bLastTransEndedAtFileEOF = TRUE;
|
|
}
|
|
else
|
|
{
|
|
bLastTransEndedAtFileEOF = (m_uiFileEOF == uiStartOffset)
|
|
? TRUE
|
|
: FALSE;
|
|
}
|
|
|
|
// Position to the start offset. Unfortunately, this means reading
|
|
// through the data and discarding it.
|
|
|
|
uiOffset = 512;
|
|
while (uiOffset < uiStartOffset)
|
|
{
|
|
uiReadLen = (uiStartOffset - uiOffset);
|
|
if (uiReadLen > m_uiBufferSize)
|
|
{
|
|
uiReadLen = m_uiBufferSize;
|
|
}
|
|
if (RC_BAD( rc = m_pRestore->read( uiReadLen,
|
|
m_pCurrentBuf->pIOBuffer->m_pucBuffer, &uiBytesRead)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// RFL file is incomplete if we could not read up to the last
|
|
// committed transaction.
|
|
|
|
if (uiBytesRead < uiReadLen)
|
|
{
|
|
rc = RC_SET( FERR_RFL_INCOMPLETE);
|
|
goto Exit;
|
|
}
|
|
|
|
uiOffset += uiBytesRead;
|
|
}
|
|
|
|
// Need to set current file number
|
|
|
|
m_pCurrentBuf->uiCurrFileNum = uiStartFileNum;
|
|
|
|
// Better not be any transactions to recover - last database
|
|
// state needs to be a completed checkpoint.
|
|
|
|
flmAssert(
|
|
FB2UD( &m_pFile->ucLastCommittedLogHdr [LOG_LAST_CP_TRANS_ID]) ==
|
|
FB2UD( &m_pFile->ucLastCommittedLogHdr [LOG_CURR_TRANS_ID]));
|
|
|
|
// Use uiStartOffset here instead of LOG_RFL_LAST_TRANS_OFFSET,
|
|
// because LOG_RFL_LAST_TRANS_OFFSET may be zero, but we in that
|
|
// case we should be comparing to 512, and uiStartOffset will have
|
|
// been adjusted to 512 if that is the case.
|
|
|
|
flmAssert(
|
|
FB2UD( &m_pFile->ucLastCommittedLogHdr [LOG_RFL_LAST_CP_OFFSET]) ==
|
|
uiStartOffset);
|
|
|
|
flmAssert(
|
|
FB2UD( &m_pFile->ucLastCommittedLogHdr [LOG_RFL_LAST_CP_FILE_NUM]) ==
|
|
uiStartFileNum);
|
|
}
|
|
|
|
// Set last transaction ID to the last transaction
|
|
// that was checkpointed - transaction numbers should ascend
|
|
// from here.
|
|
|
|
m_uiLastTransID = (FLMUINT)FB2UD(
|
|
&m_pFile->ucLastCommittedLogHdr [LOG_LAST_CP_TRANS_ID]);
|
|
|
|
// Set the last committed trans ID if this is a 4.31+ database
|
|
|
|
if( m_pFile->FileHdr.uiVersionNum >= FLM_VER_4_31)
|
|
{
|
|
m_uiLastLoggedCommitTransID = (FLMUINT)FB2UD(
|
|
&m_pFile->ucLastCommittedLogHdr [LOG_LAST_RFL_COMMIT_ID]);
|
|
}
|
|
|
|
m_pCurrentBuf->uiRflFileOffset = uiStartOffset;
|
|
m_uiRflReadOffset = 0;
|
|
m_pCurrentBuf->uiRflBufBytes = 0;
|
|
|
|
|
|
// Now, read until we are done.
|
|
|
|
bForceNextFile = FALSE;
|
|
for (;;)
|
|
{
|
|
|
|
if (!pRecord)
|
|
{
|
|
if( (pRecord = f_new FlmRecord) == NULL)
|
|
{
|
|
rc = RC_SET( FERR_MEM);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Get the next operation from the file.
|
|
|
|
rc = readOp( pDb, bForceNextFile,
|
|
&uiOp, &uiContainer, &uiDrn, &uiIndex,
|
|
&uiEndDrn, pRecord, &uiTransId,
|
|
&uiStartTime, &uiLastLoggedCommitTransID,
|
|
&uiOpFlags);
|
|
bForceNextFile = FALSE;
|
|
|
|
if (RC_BAD( rc))
|
|
{
|
|
Handle_Packet_Error:
|
|
if (rc == FERR_END)
|
|
{
|
|
if (!m_pRestore)
|
|
{
|
|
|
|
// If we didn't end exactly where we should have, we have
|
|
// an incomplete log. The same is true if we are in the
|
|
// middle of a transaction.
|
|
|
|
if (m_pCurrentBuf->uiCurrFileNum != m_uiLastRecoverFileNum ||
|
|
bTransActive)
|
|
{
|
|
rc = RC_SET( FERR_RFL_INCOMPLETE);
|
|
}
|
|
else
|
|
{
|
|
rc = FERR_OK;
|
|
goto Finish_Recovery;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
// If we are doing a restore, and we get to the end of the
|
|
// log, it is OK - even if we are in the middle of a
|
|
// transaction - the transaction will simply be aborted.
|
|
|
|
rc = FERR_OK;
|
|
goto Finish_Recovery;
|
|
}
|
|
}
|
|
else if (rc == FERR_BAD_RFL_PACKET)
|
|
{
|
|
// If we don't know the current file size, and we
|
|
// are doing a restore, it is OK to end on a bad
|
|
// packet - we will simply abort the current
|
|
// transaction, if any. Then, try to go to the
|
|
// next file, because we really don't know where
|
|
// this file ends.
|
|
|
|
if (m_pRestore && !m_uiFileEOF)
|
|
{
|
|
if (bTransActive)
|
|
{
|
|
FlmDbTransAbort( hDb);
|
|
bTransActive = FALSE;
|
|
}
|
|
|
|
// Set current transaction ID to zero - as if we had encountered
|
|
// an abort packet.
|
|
|
|
m_uiCurrTransID = 0;
|
|
bLastTransEndedAtFileEOF = TRUE;
|
|
|
|
// Force to go to the next file
|
|
|
|
bForceNextFile = TRUE;
|
|
rc = FERR_OK;
|
|
continue;
|
|
}
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
// At this point, we know we have a good packet, see what it
|
|
// is and handle it.
|
|
|
|
bHadOperations = TRUE;
|
|
switch (uiOp)
|
|
{
|
|
case RFL_TRNS_BEGIN_EX_PACKET:
|
|
case RFL_TRNS_BEGIN_PACKET:
|
|
{
|
|
if (m_pRestore)
|
|
{
|
|
if (RC_BAD( rc = m_pRestore->status(
|
|
RESTORE_BEGIN_TRANS,
|
|
uiTransId,
|
|
(void *)uiStartTime,
|
|
(void *)0,
|
|
(void *)0,
|
|
&eRestoreAction)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (eRestoreAction == RESTORE_ACTION_STOP)
|
|
{
|
|
// Need to set m_uiCurrTransID to 0 since it was
|
|
// set by getPacket(). We are not going to
|
|
// start a transaction because of the user's request
|
|
// to exit.
|
|
|
|
m_uiCurrTransID = 0;
|
|
bLastTransEndedAtFileEOF = FALSE;
|
|
goto Finish_Recovery;
|
|
}
|
|
}
|
|
|
|
// If we already have a transaction active, we have
|
|
// a problem.
|
|
|
|
flmAssert( !bTransActive);
|
|
|
|
if( RC_BAD( rc = FlmDbTransBegin( hDb,
|
|
FLM_UPDATE_TRANS, 0)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
bTransActive = TRUE;
|
|
break;
|
|
}
|
|
|
|
case RFL_TRNS_COMMIT_PACKET:
|
|
{
|
|
// Commit the current transaction.
|
|
|
|
if (m_pRestore)
|
|
{
|
|
if (RC_BAD( rc = m_pRestore->status(
|
|
RESTORE_COMMIT_TRANS,
|
|
uiTransId,
|
|
(void *)0,
|
|
(void *)0,
|
|
(void *)0,
|
|
&eRestoreAction)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (eRestoreAction == RESTORE_ACTION_STOP)
|
|
{
|
|
bLastTransEndedAtFileEOF = FALSE;
|
|
goto Finish_Recovery;
|
|
}
|
|
}
|
|
|
|
flmAssert( bTransActive);
|
|
pDb->uiFlags |= FDB_REPLAYING_COMMIT;
|
|
rc = FlmDbTransCommit( hDb);
|
|
pDb->uiFlags &= ~FDB_REPLAYING_COMMIT;
|
|
bTransActive = FALSE;
|
|
|
|
if (RC_BAD( rc))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
m_uiLastLoggedCommitTransID = uiTransId;
|
|
Finish_Transaction:
|
|
if (!m_uiFileEOF)
|
|
{
|
|
bLastTransEndedAtFileEOF = TRUE;
|
|
}
|
|
else
|
|
{
|
|
bLastTransEndedAtFileEOF =
|
|
(m_uiRflReadOffset == m_pCurrentBuf->uiRflBufBytes &&
|
|
m_pCurrentBuf->uiRflFileOffset +
|
|
m_pCurrentBuf->uiRflBufBytes == m_uiFileEOF)
|
|
? TRUE
|
|
: FALSE;
|
|
}
|
|
m_uiLastTransID = uiTransId;
|
|
m_uiCurrTransID = 0;
|
|
break;
|
|
}
|
|
|
|
case RFL_TRNS_ABORT_PACKET:
|
|
{
|
|
// Abort the current transaction.
|
|
|
|
if (m_pRestore)
|
|
{
|
|
if (RC_BAD( rc = m_pRestore->status(
|
|
RESTORE_ABORT_TRANS,
|
|
uiTransId,
|
|
(void *)0,
|
|
(void *)0,
|
|
(void *)0,
|
|
&eRestoreAction)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
if (eRestoreAction == RESTORE_ACTION_STOP)
|
|
{
|
|
bLastTransEndedAtFileEOF = FALSE;
|
|
goto Finish_Recovery;
|
|
}
|
|
}
|
|
|
|
flmAssert( bTransActive);
|
|
rc = FlmDbTransAbort( hDb);
|
|
bTransActive = FALSE;
|
|
|
|
if (RC_BAD( rc))
|
|
{
|
|
goto Exit;
|
|
}
|
|
goto Finish_Transaction;
|
|
}
|
|
|
|
case RFL_ADD_RECORD_PACKET:
|
|
case RFL_ADD_RECORD_PACKET_VER_2:
|
|
{
|
|
if (m_pRestore)
|
|
{
|
|
if (RC_BAD( rc = m_pRestore->status(
|
|
RESTORE_ADD_REC,
|
|
uiTransId,
|
|
(void *)uiContainer,
|
|
(void *)uiDrn,
|
|
(void *)pRecord,
|
|
&eRestoreAction)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
if (eRestoreAction == RESTORE_ACTION_STOP)
|
|
{
|
|
bLastTransEndedAtFileEOF = FALSE;
|
|
goto Finish_Recovery;
|
|
}
|
|
}
|
|
|
|
rc = FlmRecordAdd( hDb, uiContainer, &uiDrn,
|
|
pRecord, uiOpFlags);
|
|
pRecord->Release();
|
|
pRecord = NULL;
|
|
if (RC_BAD( rc))
|
|
{
|
|
goto Exit;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case RFL_MODIFY_RECORD_PACKET:
|
|
case RFL_MODIFY_RECORD_PACKET_VER_2:
|
|
{
|
|
// Must retrieve the record and then get the
|
|
// modify packet(s) to alter it.
|
|
|
|
if (RC_BAD( rc = FlmRecordRetrieve( hDb, uiContainer,
|
|
uiDrn, FO_EXACT, &pRecord, NULL)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if ((pTmpRecord = pRecord->copy()) == NULL)
|
|
{
|
|
rc = RC_SET( FERR_MEM);
|
|
goto Exit;
|
|
}
|
|
|
|
pRecord->Release();
|
|
pRecord = NULL;
|
|
|
|
if (RC_BAD( rc = modifyRecord( hDb, pTmpRecord)))
|
|
{
|
|
goto Handle_Packet_Error;
|
|
}
|
|
|
|
// Finally, modify the record in the database.
|
|
|
|
if (m_pRestore)
|
|
{
|
|
if (RC_BAD( rc = m_pRestore->status(
|
|
RESTORE_MOD_REC,
|
|
uiTransId,
|
|
(void *)uiContainer,
|
|
(void *)uiDrn,
|
|
(void *)pTmpRecord,
|
|
&eRestoreAction)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (eRestoreAction == RESTORE_ACTION_STOP)
|
|
{
|
|
bLastTransEndedAtFileEOF = FALSE;
|
|
goto Finish_Recovery;
|
|
}
|
|
}
|
|
|
|
rc = FlmRecordModify( hDb, uiContainer, uiDrn,
|
|
pTmpRecord, uiOpFlags);
|
|
|
|
pTmpRecord->Release();
|
|
pTmpRecord = NULL;
|
|
|
|
if (RC_BAD( rc))
|
|
{
|
|
goto Exit;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case RFL_DELETE_RECORD_PACKET:
|
|
case RFL_DELETE_RECORD_PACKET_VER_2:
|
|
{
|
|
if (m_pRestore)
|
|
{
|
|
if (RC_BAD( rc = m_pRestore->status(
|
|
RESTORE_DEL_REC,
|
|
uiTransId,
|
|
(void *)uiContainer,
|
|
(void *)uiDrn,
|
|
(void *)0,
|
|
&eRestoreAction)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
if (eRestoreAction == RESTORE_ACTION_STOP)
|
|
{
|
|
bLastTransEndedAtFileEOF = FALSE;
|
|
goto Finish_Recovery;
|
|
}
|
|
}
|
|
|
|
if( RC_BAD( rc = FlmRecordDelete( hDb, uiContainer,
|
|
uiDrn, uiOpFlags)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case RFL_RESERVE_DRN_PACKET:
|
|
{
|
|
if (m_pRestore)
|
|
{
|
|
if (RC_BAD( rc = m_pRestore->status(
|
|
RESTORE_RESERVE_DRN,
|
|
uiTransId,
|
|
(void *)uiContainer,
|
|
(void *)uiDrn,
|
|
(void *)0,
|
|
&eRestoreAction)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
if (eRestoreAction == RESTORE_ACTION_STOP)
|
|
{
|
|
bLastTransEndedAtFileEOF = FALSE;
|
|
goto Finish_Recovery;
|
|
}
|
|
}
|
|
|
|
if( RC_BAD( rc = FlmReserveNextDrn( hDb, uiContainer, &uiDrn)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case RFL_INDEX_SUSPEND_PACKET:
|
|
{
|
|
// NOTE: The index number is returned in the uiDrn parameter of
|
|
// the readOp function.
|
|
|
|
if (m_pRestore)
|
|
{
|
|
if (RC_BAD( rc = m_pRestore->status(
|
|
RESTORE_INDEX_SUSPEND,
|
|
uiTransId,
|
|
(void *)uiIndex,
|
|
(void *)0,
|
|
(void *)0, &eRestoreAction)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (eRestoreAction == RESTORE_ACTION_STOP)
|
|
{
|
|
bLastTransEndedAtFileEOF = FALSE;
|
|
goto Finish_Recovery;
|
|
}
|
|
}
|
|
|
|
if( RC_BAD( rc = FlmIndexSuspend( hDb, uiIndex)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case RFL_INDEX_RESUME_PACKET:
|
|
{
|
|
// NOTE: The index number is returned in the uiDrn parameter of
|
|
// the readOp function.
|
|
|
|
if (m_pRestore)
|
|
{
|
|
if (RC_BAD( rc = m_pRestore->status(
|
|
RESTORE_INDEX_RESUME,
|
|
uiTransId,
|
|
(void *)uiIndex,
|
|
(void *)0,
|
|
(void *)0, &eRestoreAction)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (eRestoreAction == RESTORE_ACTION_STOP)
|
|
{
|
|
bLastTransEndedAtFileEOF = FALSE;
|
|
goto Finish_Recovery;
|
|
}
|
|
}
|
|
|
|
if( RC_BAD( rc = FlmIndexResume( hDb, uiIndex)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case RFL_INDEX_SET_PACKET:
|
|
case RFL_INDEX_SET_PACKET_VER_2:
|
|
{
|
|
if (m_pRestore)
|
|
{
|
|
if (RC_BAD( rc = m_pRestore->status(
|
|
RESTORE_INDEX_SET,
|
|
uiTransId,
|
|
(void *)uiIndex,
|
|
(void *)uiDrn,
|
|
(void *)uiEndDrn,
|
|
&eRestoreAction)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (eRestoreAction == RESTORE_ACTION_STOP)
|
|
{
|
|
bLastTransEndedAtFileEOF = FALSE;
|
|
goto Finish_Recovery;
|
|
}
|
|
}
|
|
#ifdef FLM_DEBUG
|
|
if( m_pFile->FileHdr.uiVersionNum < FLM_VER_4_50)
|
|
{
|
|
flmAssert( uiOp == RFL_INDEX_SET_PACKET);
|
|
}
|
|
#endif
|
|
|
|
if( RC_BAD( rc = flmDbIndexSetOfRecords( hDb, uiIndex,
|
|
uiContainer, uiDrn, uiEndDrn)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case RFL_BLK_CHAIN_FREE_PACKET:
|
|
{
|
|
if (m_pRestore)
|
|
{
|
|
if (RC_BAD( rc = m_pRestore->status(
|
|
RESTORE_BLK_CHAIN_DELETE,
|
|
uiTransId,
|
|
(void *)uiDrn,
|
|
(void *)uiOpFlags,
|
|
(void *)uiEndDrn,
|
|
&eRestoreAction)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (eRestoreAction == RESTORE_ACTION_STOP)
|
|
{
|
|
bLastTransEndedAtFileEOF = FALSE;
|
|
goto Finish_Recovery;
|
|
}
|
|
}
|
|
|
|
if( RC_BAD( rc = flmMaintFreeBlockChain(
|
|
pDb, uiDrn, uiOpFlags, uiEndDrn, NULL)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case RFL_START_UNKNOWN_PACKET:
|
|
{
|
|
if (m_pRestore)
|
|
{
|
|
F_RflUnknownStream unkStrm;
|
|
|
|
unkStrm.setup( this, TRUE);
|
|
m_bReadingUnknown = TRUE;
|
|
m_uiUnknownPacketBodyLen = 0;
|
|
m_pucUnknownPacketBody = NULL;
|
|
m_uiUnknownBodyLenProcessed = 0;
|
|
m_uiUnknownPacketRc = FERR_OK;
|
|
|
|
if (RC_BAD( rc = m_pRestore->processUnknown(
|
|
(F_UnknownStream *)&unkStrm)))
|
|
{
|
|
if (m_uiUnknownPacketRc != FERR_OK)
|
|
{
|
|
rc = m_uiUnknownPacketRc;
|
|
goto Handle_Packet_Error;
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
// If we did not read through all of the unknown
|
|
// packets, skip them at this time.
|
|
|
|
if (m_bReadingUnknown)
|
|
{
|
|
goto Skip_Unknown_Packets;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Skip_Unknown_Packets:
|
|
|
|
// Skip all unknown packets.
|
|
|
|
for (;;)
|
|
{
|
|
FLMUINT uiPacketType;
|
|
FLMBYTE * pucPacketBody;
|
|
FLMUINT uiPacketBodyLen;
|
|
|
|
if (RC_BAD( rc = getPacket( FALSE, &uiPacketType,
|
|
&pucPacketBody, &uiPacketBodyLen, NULL)))
|
|
{
|
|
goto Handle_Packet_Error;
|
|
}
|
|
|
|
// If we hit something other than an unknown packet,
|
|
// "push" it back into the pipe so it will be processed
|
|
// by readOp() up above.
|
|
|
|
if (uiPacketType != RFL_UNKNOWN_PACKET)
|
|
{
|
|
|
|
// At this point, we know that the entire packet is
|
|
// inside our memory buffer, so it is safe to reset
|
|
// m_uiRflReadOffset back to the beginning of the
|
|
// packet. The call to readOp() above will call
|
|
// getPacket again, which will get this exact same
|
|
// packet for processing.
|
|
|
|
m_uiRflReadOffset -= (RFL_PACKET_OVERHEAD +
|
|
uiPacketBodyLen);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case RFL_REDUCE_PACKET:
|
|
{
|
|
// NOTE: The count is returned in the uiDrn parameter of
|
|
// the readOp function.
|
|
|
|
if (m_pRestore)
|
|
{
|
|
if (RC_BAD( rc = m_pRestore->status(
|
|
RESTORE_REDUCE,
|
|
uiTransId,
|
|
(void *)uiDrn, // count
|
|
(void *)0,
|
|
(void *)0, &eRestoreAction)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (eRestoreAction == RESTORE_ACTION_STOP)
|
|
{
|
|
|
|
// Need to set m_uiCurrTransID to 0 since it was
|
|
// set by getPacket(). We are not going to
|
|
// start a transaction because of the user's request
|
|
// to exit.
|
|
|
|
m_uiCurrTransID = 0;
|
|
bLastTransEndedAtFileEOF = FALSE;
|
|
goto Finish_Recovery;
|
|
}
|
|
}
|
|
|
|
if( RC_BAD( rc = FlmDbReduceSize( hDb, uiDrn, &uiCount)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
goto Finish_Transaction;
|
|
}
|
|
|
|
case RFL_UPGRADE_PACKET:
|
|
{
|
|
if (m_pRestore)
|
|
{
|
|
if (RC_BAD( rc = m_pRestore->status(
|
|
RESTORE_UPGRADE,
|
|
uiTransId,
|
|
(void *)uiDrn, // Old DB Version
|
|
(void *)uiEndDrn, // New DB Version
|
|
(void *)0,
|
|
&eRestoreAction)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (eRestoreAction == RESTORE_ACTION_STOP)
|
|
{
|
|
// Need to set m_uiCurrTransID to 0 since it was
|
|
// set by getPacket(). We are not going to
|
|
// start a transaction because of the user's request
|
|
// to exit.
|
|
|
|
m_uiCurrTransID = 0;
|
|
bLastTransEndedAtFileEOF = FALSE;
|
|
goto Finish_Recovery;
|
|
}
|
|
}
|
|
|
|
// Attempt the conversion if the current version is less
|
|
// than the target version and the target version is
|
|
// less than or equal to the highest version supported
|
|
// by this code.
|
|
|
|
if( uiEndDrn > FLM_CURRENT_VERSION_NUM)
|
|
{
|
|
rc = RC_SET( FERR_UNALLOWED_UPGRADE);
|
|
goto Exit;
|
|
}
|
|
else
|
|
{
|
|
flmAssert( m_pFile->FileHdr.uiVersionNum < uiEndDrn);
|
|
|
|
// The logged "new" version may be a lesser version
|
|
// than FLM_CURRENT_VERSION_NUM, which is what FlmDbUpgrade
|
|
// upgrades to. This is O.K. because the current version
|
|
// should support all packets in the RFL for versions
|
|
// that are less than it. Otherwise, the RFL chain
|
|
// would have been broken by the original upgrade and it
|
|
// would not have logged an upgrade packet.
|
|
|
|
if( RC_BAD( rc = FlmDbUpgrade( hDb, uiEndDrn, NULL, NULL)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
goto Finish_Transaction;
|
|
}
|
|
|
|
case RFL_WRAP_KEY_PACKET:
|
|
case RFL_ENABLE_ENCRYPTION_PACKET:
|
|
{
|
|
goto Finish_Transaction;
|
|
}
|
|
|
|
default:
|
|
{
|
|
// Should not be getting other packet types at this
|
|
// point.
|
|
|
|
// If we don't know the current file size, and we
|
|
// are doing a restore, it is OK to end on a bad
|
|
// packet - we will simply abort the current
|
|
// transaction, if any.
|
|
|
|
if (m_pRestore && !m_uiFileEOF)
|
|
{
|
|
rc = FERR_OK;
|
|
goto Finish_Recovery;
|
|
}
|
|
else
|
|
{
|
|
rc = RC_SET( FERR_BAD_RFL_PACKET);
|
|
}
|
|
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
Finish_Recovery:
|
|
|
|
if (bTransActive)
|
|
{
|
|
FlmDbTransAbort( hDb);
|
|
bTransActive = FALSE;
|
|
}
|
|
|
|
if (m_pRestore)
|
|
{
|
|
FLMUINT uiNextRflFileNum = m_pCurrentBuf->uiCurrFileNum + 1;
|
|
|
|
// At the end of the restore operation, we need to set things
|
|
// up so that the next transaction will begin a new RFL file.
|
|
// If we ended the restore in the middle of an RFL file, we
|
|
// need to set it up so that the new RFL file will have a new
|
|
// serial number. If we ended at the end of an RFL file, we
|
|
// can set it up so that the new RFL file will have the next
|
|
// serial number.
|
|
|
|
// Set up the next RFL file number and offset.
|
|
|
|
UD2FBA( uiNextRflFileNum,
|
|
&m_pFile->ucLastCommittedLogHdr [LOG_RFL_FILE_NUM]);
|
|
|
|
// Set a zero into the offset, this is a special case which tells
|
|
// us that we should create the file no matter what - even if
|
|
// it already exists - it should be overwritten.
|
|
|
|
UD2FBA( 0,
|
|
&m_pFile->ucLastCommittedLogHdr [LOG_RFL_LAST_TRANS_OFFSET]);
|
|
|
|
if (bLastTransEndedAtFileEOF)
|
|
{
|
|
// Move the next serial number of the last RFL file processed into
|
|
// into the current RFL serial number so that the log header
|
|
// will be correct
|
|
|
|
f_memcpy(
|
|
&m_pFile->ucLastCommittedLogHdr [LOG_LAST_TRANS_RFL_SERIAL_NUM],
|
|
m_ucNextSerialNum, F_SERIAL_NUM_SIZE);
|
|
}
|
|
else
|
|
{
|
|
|
|
// Must create a new serial number so that when the new RFL
|
|
// file is created, it will have that next serial number.
|
|
|
|
if (RC_BAD( rc = f_createSerialNumber(
|
|
&m_pFile->ucLastCommittedLogHdr [LOG_LAST_TRANS_RFL_SERIAL_NUM])))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Save the last logged commit transaction ID.
|
|
|
|
if (m_pFile->FileHdr.uiVersionNum >= FLM_VER_4_31 &&
|
|
m_uiLastLoggedCommitTransID)
|
|
{
|
|
UD2FBA(m_uiLastLoggedCommitTransID,
|
|
&m_pFile->ucLastCommittedLogHdr [LOG_LAST_RFL_COMMIT_ID]);
|
|
}
|
|
|
|
// No matter what, we must generate a new next serial number. This
|
|
// is what will be written to the new RFL file's header when it is
|
|
// created.
|
|
|
|
if (RC_BAD( rc = f_createSerialNumber(
|
|
&m_pFile->ucLastCommittedLogHdr [LOG_RFL_NEXT_SERIAL_NUM])))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
if (!bHadOperations)
|
|
{
|
|
|
|
// No transactions were recovered, but still need to
|
|
// setup a few things.
|
|
|
|
m_pFile->uiFirstLogCPBlkAddress = 0;
|
|
m_pFile->uiLastCheckpointTime = (FLMUINT)FLM_GET_TIMER();
|
|
|
|
// Save the state of the log header into the ucCheckpointLogHdr buffer.
|
|
|
|
f_memcpy( m_pFile->ucCheckpointLogHdr,
|
|
m_pFile->ucLastCommittedLogHdr,
|
|
LOG_HEADER_SIZE);
|
|
}
|
|
|
|
// Force a checkpoint to force the log files to be truncated and
|
|
// everything to be reset. This is done because during recovery
|
|
// the checkpoints that are executed do NOT truncate the RFL file -
|
|
// because it is using the log file to recover!
|
|
|
|
closeFile();
|
|
m_pRestore = NULL;
|
|
m_bLoggingOff = FALSE;
|
|
pDb->uiFlags &= ~FDB_REPLAYING_RFL;
|
|
if (RC_BAD( rc = FlmDbCheckpoint( hDb, 0)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
|
|
if (pRecord)
|
|
{
|
|
pRecord->Release();
|
|
}
|
|
|
|
if (pTmpRecord)
|
|
{
|
|
pTmpRecord->Release();
|
|
}
|
|
|
|
if (bTransActive)
|
|
{
|
|
FlmDbTransAbort( hDb);
|
|
}
|
|
|
|
pDb->bFldStateUpdOk = FALSE;
|
|
pDb->uiFlags &= ~FDB_REPLAYING_RFL;
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/*API~***********************************************************************
|
|
Desc: Returns the name of an RFL file given its number
|
|
*END************************************************************************/
|
|
RCODE FlmDbGetRflFileName(
|
|
HFDB hDb,
|
|
FLMUINT uiFileNum,
|
|
char * pszFileName)
|
|
{
|
|
((FDB *)hDb)->pFile->pRfl->getBaseRflFileName(
|
|
uiFileNum, pszFileName);
|
|
|
|
return( FERR_OK);
|
|
}
|
|
|
|
/********************************************************************
|
|
Desc: Log a block chain free operation
|
|
*********************************************************************/
|
|
RCODE F_Rfl::logBlockChainFree(
|
|
FLMUINT uiTrackerDrn,
|
|
FLMUINT uiCount,
|
|
FLMUINT uiEndAddr)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FLMBYTE * pucPacketBody;
|
|
FLMUINT uiPacketBodyLen;
|
|
|
|
// This call is new with 4.52 databases - not supported in older
|
|
// versions, so don't log it.
|
|
|
|
if (m_pFile->FileHdr.uiVersionNum < FLM_VER_4_60)
|
|
{
|
|
flmAssert( 0);
|
|
goto Exit;
|
|
}
|
|
|
|
// Do nothing if logging is disabled.
|
|
|
|
if (m_bLoggingOff)
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Better not be in the middle of logging unknown stuff
|
|
// for the application
|
|
|
|
flmAssert( !m_bLoggingUnknown);
|
|
|
|
// Better be in the middle of a transaction.
|
|
|
|
flmAssert( m_uiCurrTransID);
|
|
m_uiOperCount++;
|
|
|
|
uiPacketBodyLen = 16;
|
|
|
|
// Make sure we have space in the RFL buffer for a complete packet.
|
|
|
|
if (!haveBuffSpace( uiPacketBodyLen + RFL_PACKET_OVERHEAD))
|
|
{
|
|
if (RC_BAD( rc = flush( m_pCurrentBuf)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Get a pointer to where we will be laying down the packet body.
|
|
|
|
pucPacketBody = getPacketBodyPtr();
|
|
|
|
// Output the transaction ID.
|
|
|
|
UD2FBA( (FLMUINT32)m_uiCurrTransID, pucPacketBody);
|
|
pucPacketBody += 4;
|
|
|
|
// Output the tracker record number
|
|
|
|
UD2FBA( (FLMUINT32)uiTrackerDrn, pucPacketBody);
|
|
pucPacketBody += 4;
|
|
|
|
// Output the count
|
|
|
|
UD2FBA( (FLMUINT32)uiCount, pucPacketBody);
|
|
pucPacketBody += 4;
|
|
|
|
// Output the ending block address
|
|
|
|
UD2FBA( (FLMUINT32)uiEndAddr, pucPacketBody);
|
|
pucPacketBody += 4;
|
|
|
|
// Finish the packet
|
|
|
|
if (RC_BAD( rc = finishPacket( RFL_BLK_CHAIN_FREE_PACKET,
|
|
uiPacketBodyLen, TRUE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|