Files
mars-flaim/sql/src/rfl.cpp
2006-06-02 22:19:25 +00:00

6032 lines
142 KiB
C++

// Desc: This module contains routine 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$
//------------------------------------------------------------------------------
#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)))
/********************************************************************
Desc:
*********************************************************************/
FINLINE FLMBOOL F_Rfl::useDataOnlyBlocks(
F_Db * pDb,
FLMUINT uiDataLen)
{
if( uiDataLen > (pDb->m_pDatabase->m_uiBlockSize * 8) / 5)
{
return( TRUE);
}
else
{
return( FALSE);
}
}
/********************************************************************
Desc:
*********************************************************************/
F_Rfl::F_Rfl()
{
m_pDatabase = 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 = SFLM_DEFAULT_MIN_RFL_FILE_SIZE;
m_uiRflMaxFileSize = SFLM_DEFAULT_MAX_RFL_FILE_SIZE;
m_pFileHdl = NULL;
m_uiLastRecoverFileNum = 0;
f_memset( m_ucCurrSerialNum, 0, sizeof( m_ucCurrSerialNum));
m_uiTransStartFile = 0;
m_uiTransStartAddr = 0;
m_ui64CurrTransID = 0;
m_ui64LastTransID = 0;
m_ui64LastLoggedCommitTransID = 0;
m_uiOperCount = 0;
m_uiRflReadOffset = 0;
m_uiFileEOF = 0;
m_pRestore = NULL;
m_pRestoreStatus = NULL;
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;
m_uiLastLfNum = 0;
m_eLastLfType = SFLM_LF_INVALID;
m_pIxCompareObject = NULL;
m_pCompareObject = NULL;
m_uiDisableCount = 0;
m_tmpPool.poolInit( 2048);
}
/********************************************************************
Desc: Destructor
*********************************************************************/
F_Rfl::~F_Rfl()
{
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_pDatabase = NULL;
}
if (m_pIxCompareObject)
{
m_pIxCompareObject->Release();
}
m_tmpPool.poolFree();
}
/********************************************************************
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 uiFileNum,
char * pszBaseNameOut,
FLMUINT * puiFileNameBufSize,
FLMBOOL * pbNameTruncated)
{
FLMUINT uiCnt;
FLMUINT uiDigit;
char szTmpBuf [14];
char * pszTmp = &szTmpBuf [0];
// Output as eight digit hex number
uiCnt = 0;
pszTmp += 7;
while (uiCnt < 8)
{
uiDigit = (FLMUINT)(uiFileNum & 0xF);
uiFileNum >>= 4;
if (uiDigit <= 9)
{
uiDigit += NATIVE_ZERO;
}
else
{
uiDigit += (NATIVE_LOWER_A - 10);
}
*pszTmp = (FLMBYTE)uiDigit;
pszTmp--;
uiCnt++;
}
// Skip to end of digits and append ".log" to name
f_strcpy( pszTmp + 9, ".log");
if (*puiFileNameBufSize >= 13)
{
*puiFileNameBufSize = 12;
f_strcpy( pszBaseNameOut, szTmpBuf);
if (pbNameTruncated)
{
*pbNameTruncated = FALSE;
}
}
else
{
flmAssert( *puiFileNameBufSize);
(*puiFileNameBufSize)--;
if (*puiFileNameBufSize)
{
f_memcpy( pszBaseNameOut, szTmpBuf, *puiFileNameBufSize);
}
pszBaseNameOut [*puiFileNameBufSize] = 0;
if (pbNameTruncated)
{
*pbNameTruncated = TRUE;
}
}
}
/********************************************************************
Desc: Generates the full roll forward log file name.
*********************************************************************/
void F_Rfl::getFullRflFileName(
FLMUINT uiFileNum,
char * pszRflFileName,
FLMUINT * puiFileNameBufSize,
FLMBOOL * pbNameTruncated)
{
FLMUINT uiBaseNameSize;
FLMUINT uiLen = f_strlen( m_szRflDir);
FLMBOOL bNameTruncated = FALSE;
// Must at least be room for a null byte to terminate the string.
flmAssert( *puiFileNameBufSize);
if (uiLen > *puiFileNameBufSize - 1)
{
uiLen = *puiFileNameBufSize - 1;
if (uiLen)
{
f_memcpy( pszRflFileName, m_szRflDir, uiLen);
}
bNameTruncated = TRUE;
goto Exit;
}
// Get the directory name.
if (uiLen)
{
f_memcpy( pszRflFileName, m_szRflDir, uiLen);
// See if we need to append a slash.
#ifdef FLM_UNIX
if (m_szRflDir [uiLen - 1] != '/')
#else
if (m_szRflDir [uiLen - 1] != '/' &&
m_szRflDir [uiLen - 1] != '\\')
#endif
{
// See if we have room for one more character, plus null
if (uiLen == *puiFileNameBufSize - 1)
{
bNameTruncated = TRUE;
goto Exit;
}
#ifdef FLM_UNIX
pszRflFileName [uiLen] = '/';
#else
pszRflFileName [uiLen] = '\\';
#endif
uiLen++;
}
}
// See if there is room for at least one more byte plus a
// null byte.
if (uiLen == *puiFileNameBufSize - 1)
{
bNameTruncated = TRUE;
goto Exit;
}
// Get the base RFL file name.
uiBaseNameSize = *puiFileNameBufSize - uiLen;
rflGetBaseFileName( uiFileNum, pszRflFileName + uiLen,
&uiBaseNameSize, &bNameTruncated);
uiLen += uiBaseNameSize;
Exit:
pszRflFileName [uiLen] = 0;
*puiFileNameBufSize = uiLen;
if (pbNameTruncated)
{
*pbNameTruncated = bNameTruncated;
}
}
/********************************************************************
Desc: Positions to the offset specified in the RFL file.
*********************************************************************/
RCODE F_Rfl::positionTo(
FLMUINT uiFileOffset)
{
RCODE rc = NE_SFLM_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->getBuffer(), &uiBytesRead)))
{
if (rc == NE_FLM_IO_END_OF_FILE)
{
rc = RC_SET( NE_SFLM_NOT_RFL);
}
else
{
m_bRflVolumeOk = FALSE;
}
goto Exit;
}
else if (uiBytesRead < m_pCurrentBuf->uiRflBufBytes)
{
rc = RC_SET( NE_SFLM_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(
const char * pszDbFileName,
const char * pszRflDirIn,
char * pszRflDirOut)
{
RCODE rc = NE_SFLM_OK;
char szDbPath [F_PATH_MAX_SIZE];
char szBaseName [F_FILENAME_SIZE];
char szPrefix [F_FILENAME_SIZE];
F_DbSystem dbSystem;
// Parse the database name into directory and base name
if (RC_BAD( rc = gv_SFlmSysData.pFileSystem->pathReduce( pszDbFileName,
szDbPath, szBaseName)))
{
goto Exit;
}
// Get the base path
F_DbSystem::getDbBasePath( szPrefix, szBaseName, NULL);
// 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);
}
f_strcpy( szBaseName, szPrefix);
f_strcat( szBaseName, ".rfl");
gv_SFlmSysData.pFileSystem->pathAppend( pszRflDirOut, szBaseName);
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 F_Database pointer.
flmAssert( m_pDatabase != NULL);
m_bRflDirSameAsDb = (!pszRflDir || !(*pszRflDir))
? TRUE
: FALSE;
flmAssert( m_pDatabase->m_lastCommittedDbHdr.ui32DbVersion);
m_bCreateRflDir = TRUE;
return( rflGetDirAndPrefix( m_pDatabase->m_pszDbPath,
pszRflDir, m_szRflDir));
}
/********************************************************************
Desc: Gets an RFL file name - based on DB name and RFL directory.
*********************************************************************/
RCODE rflGetFileName(
const char * pszDbName,
const char * pszRflDir,
FLMUINT uiFileNum,
char * pszRflFileName)
{
RCODE rc = NE_SFLM_OK;
char szBaseName [F_FILENAME_SIZE];
FLMUINT uiBaseNameSize;
// Get the full RFL file name.
if (RC_BAD( rc = rflGetDirAndPrefix( pszDbName, pszRflDir,
pszRflFileName)))
{
goto Exit;
}
uiBaseNameSize = sizeof( szBaseName);
rflGetBaseFileName( uiFileNum, szBaseName, &uiBaseNameSize, NULL);
if (RC_BAD( rc = gv_SFlmSysData.pFileSystem->pathAppend(
pszRflFileName, szBaseName)))
{
goto Exit;
}
Exit:
return( rc);
}
/********************************************************************
Desc: Gets an RFL file number from the RFL file name.
*********************************************************************/
FLMBOOL rflGetFileNum(
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( gv_SFlmSysData.pFileSystem->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;
// 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);
Exit:
return( bGotNum);
}
/********************************************************************
Desc: Sets up the RFL object - associating with a file, etc.
*********************************************************************/
RCODE F_Rfl::setup(
F_Database * pDatabase,
const char * pszRflDir)
{
RCODE rc = NE_SFLM_OK;
// Better not already be associated with an F_Database object
flmAssert( m_pDatabase == NULL);
m_pDatabase = pDatabase;
// Allocate memory for the RFL buffers
#ifndef FLM_UNIX
if (!gv_SFlmSysData.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( RC_BAD( rc = FlmAllocIOBufferMgr( &m_Buf1.pBufferMgr)))
{
goto Exit;
}
if( RC_BAD( rc = FlmAllocIOBufferMgr( &m_Buf2.pBufferMgr)))
{
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_pCurrentBuf = &m_Buf1;
m_pCurrentBuf->uiRflBufBytes = 0;
// Set the RFL directory and prefix if necessary.
if (RC_BAD( rc = setRflDir( pszRflDir)))
{
goto Exit;
}
// Set up the compare object for comparing index keys.
if ((m_pIxCompareObject = f_new IXKeyCompare) == NULL)
{
rc = RC_SET( NE_SFLM_MEM);
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(
F_SEM hWaitSem,
RFL_BUFFER * pBuffer,
FLMBOOL bIsWriter)
{
RCODE rc = NE_SFLM_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 = hWaitSem;
// Note: rc better be changed to success or write error
// by the process that signals us.
rc = RC_SET( NE_SFLM_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)))
{
RC_UNEXPECTED_ASSERT( TempRc);
rc = TempRc;
}
else
{
// Process that signaled us better set the rc to something
// besides NE_SFLM_FAILURE.
if (rc == NE_SFLM_FAILURE)
{
RC_UNEXPECTED_ASSERT( rc);
}
}
if (bMutexLocked)
{
f_mutexUnlock( m_hBufMutex);
}
return( rc);
}
/********************************************************************
Desc: If a commit is in progress, wait for it to finish.
*********************************************************************/
RCODE F_Rfl::waitForCommit(
F_SEM hWaitSem)
{
RCODE rc = NE_SFLM_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)
{
bMutexLocked = FALSE;
rc = waitForWrites( hWaitSem, 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 = NE_SFLM_OK;
FLMBYTE ucBuf[ 512];
FLMUINT uiBytesWritten;
flmAssert( m_pDatabase);
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]);
f_memcpy( &ucBuf [RFL_DB_SERIAL_NUM_POS],
m_pDatabase->m_lastCommittedDbHdr.ucDbSerialNum,
SFLM_SERIAL_NUM_SIZE);
f_memcpy( &ucBuf [RFL_SERIAL_NUM_POS], pucSerialNum,
SFLM_SERIAL_NUM_SIZE);
f_memcpy( &ucBuf [RFL_NEXT_FILE_SERIAL_NUM_POS], pucNextSerialNum,
SFLM_SERIAL_NUM_SIZE);
f_strcpy( (char *)&ucBuf [RFL_KEEP_SIGNATURE_POS],
(char *)((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 == NE_FLM_IO_DISK_FULL)
{
rc = RC_SET( NE_SFLM_RFL_DISK_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 == NE_FLM_IO_DISK_FULL)
{
rc = RC_SET( NE_SFLM_RFL_DISK_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 = NE_SFLM_OK;
flmAssert( m_pDatabase);
// Check the RFL name and version number
if( f_memcmp( &pucHeader [RFL_NAME_POS], RFL_NAME,
RFL_NAME_LEN) != 0)
{
rc = RC_SET( NE_SFLM_NOT_RFL);
goto Exit;
}
if( f_memcmp( &pucHeader [RFL_VERSION_POS], RFL_VERSION,
RFL_VERSION_LEN) != 0)
{
rc = RC_SET( NE_SFLM_NOT_RFL);
goto Exit;
}
// Verify the database serial number
if( f_memcmp( &pucHeader [RFL_DB_SERIAL_NUM_POS],
m_pDatabase->m_lastCommittedDbHdr.ucDbSerialNum,
SFLM_SERIAL_NUM_SIZE) != 0)
{
rc = RC_SET( NE_SFLM_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, SFLM_SERIAL_NUM_SIZE) != 0)
{
rc = RC_SET( NE_SFLM_BAD_RFL_SERIAL_NUM);
goto Exit;
}
// Verify the file number.
if( uiFileNum != (FLMUINT)FB2UD( &pucHeader [RFL_FILE_NUMBER_POS]))
{
rc = RC_SET( NE_SFLM_BAD_RFL_FILE_NUMBER);
goto Exit;
}
// Save serial numbers from the header.
f_memcpy( m_ucCurrSerialNum, &pucHeader [RFL_SERIAL_NUM_POS],
SFLM_SERIAL_NUM_SIZE);
f_memcpy( m_ucNextSerialNum, &pucHeader [RFL_NEXT_FILE_SERIAL_NUM_POS],
SFLM_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(
F_SEM hWaitSem,
FLMUINT uiFileNum,
FLMBYTE * pucSerialNum)
{
RCODE rc = NE_SFLM_OK;
char szRflFileName [F_PATH_MAX_SIZE];
FLMUINT uiFileNameSize;
FLMBYTE ucBuf [512];
FLMUINT uiBytesRead;
flmAssert( m_pDatabase);
// 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( hWaitSem)))
{
goto Exit;
}
closeFile();
}
else
{
goto Exit; // Will return NE_SFLM_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.
uiFileNameSize = sizeof( szRflFileName);
getFullRflFileName( uiFileNum, szRflFileName, &uiFileNameSize, NULL);
// Open the file.
if (RC_BAD( rc = gv_SFlmSysData.pFileSystem->openBlockFile( szRflFileName,
FLM_IO_RDWR | FLM_IO_SH_DENYNONE | FLM_IO_DIRECT,
512, &m_pFileHdl)))
{
goto Exit;
}
// Read the header.
if (RC_BAD( rc = m_pFileHdl->sectorRead( 0, 512, ucBuf,
&uiBytesRead)))
{
if (rc == NE_FLM_IO_END_OF_FILE)
{
rc = RC_SET( NE_SFLM_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( NE_SFLM_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( hWaitSem);
closeFile();
}
return( rc);
}
/********************************************************************
Desc: Creates a new roll forward log file.
*********************************************************************/
RCODE F_Rfl::createFile(
F_Db * pDb,
FLMUINT uiFileNum,
FLMBYTE * pucSerialNum,
FLMBYTE * pucNextSerialNum,
FLMBOOL bKeepSignature)
{
RCODE rc = NE_SFLM_OK;
char szRflFileName [F_PATH_MAX_SIZE];
FLMUINT uiFileNameSize;
flmAssert( m_pDatabase);
// 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( pDb->m_hWaitSem)))
{
goto Exit;
}
closeFile();
// Generate the log file name.
uiFileNameSize = sizeof( szRflFileName);
getFullRflFileName( uiFileNum, szRflFileName, &uiFileNameSize, NULL);
// Delete the file if it already exists - don't care
// about return code here
(void)gv_SFlmSysData.pFileSystem->deleteFile( 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_SFlmSysData.pFileSystem->doesFileExist( m_szRflDir)))
{
if (rc != NE_FLM_IO_PATH_NOT_FOUND &&
rc != NE_FLM_IO_INVALID_FILENAME)
{
goto Exit;
}
else
{
if (RC_BAD( rc =
gv_SFlmSysData.pFileSystem->createDir( m_szRflDir)))
{
goto Exit;
}
}
}
m_bCreateRflDir = FALSE;
}
// Create the file
if (RC_BAD( rc = gv_SFlmSysData.pFileSystem->createBlockFile( szRflFileName,
FLM_IO_RDWR | FLM_IO_EXCL | FLM_IO_SH_DENYNONE | FLM_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_SFlmSysData.pFileSystem->deleteFile( 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(
F_Db * pDb,
RFL_BUFFER * pBuffer,
FLMBOOL bFinalWrite,
FLMUINT uiCurrPacketLen,
FLMBOOL bStartingNewFile)
{
RCODE rc = NE_SFLM_OK;
FLMUINT uiBytesWritten;
IF_IOBuffer * pNewBuffer = NULL;
IF_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( pDb->m_hWaitSem)))
{
goto Exit;
}
}
if (m_uiRflWriteBufs > 1 && m_pFileHdl->canDoAsync())
{
pAsyncBuf = pBuffer->pIOBuffer;
}
if ((FLMUINT)(-1) - pBuffer->uiRflFileOffset <=
pBuffer->uiRflBufBytes)
{
rc = RC_SET( NE_SFLM_DB_FULL);
goto Exit;
}
pucOldBuffer = pBuffer->pIOBuffer->getBuffer();
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->getBuffer(),
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 == NE_FLM_IO_DISK_FULL)
{
rc = RC_SET( NE_SFLM_RFL_DISK_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->getBuffer(),
m_pCurrentBuf->pIOBuffer->getBuffer(), 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(
F_SEM hWaitSem,
FLMBOOL bForceWait)
{
FLMBOOL bWritesDone;
f_mutexLock( m_hBufMutex);
if (!bForceWait)
{
bWritesDone = (FLMBOOL)((m_pCurrentBuf->pFirstWaiter || m_pCommitBuf)
? FALSE
: TRUE);
if( bWritesDone)
{
m_pCurrentBuf->uiRflBufBytes = 0;
}
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( NE_SFLM_OK, TRUE);
(void)waitForWrites( hWaitSem, 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( hWaitSem, 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);
}
m_pCurrentBuf->uiRflBufBytes = 0;
}
else if (m_pCommitBuf)
{
(void)waitForWrites( hWaitSem, 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)
{
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(
F_Db * pDb,
FLMBOOL bCommitting,
FLMBOOL bOkToUnlock)
{
RCODE rc = NE_SFLM_OK;
RCODE tmpRc;
FLMBOOL bMutexLocked = FALSE;
FLMBOOL bNotifyWaiters = FALSE;
FLMBOOL bDbUnlocked = FALSE;
SFLM_DB_STATS * pDbStats = NULL;
F_TMSTAMP StartTime;
f_mutexLock( m_hBufMutex);
bMutexLocked = TRUE;
m_pCurrentBuf->bTransInProgress = FALSE;
flmAssert( pDb->m_uiFlags & FDB_HAS_WRITE_LOCK);
// When recovering or restoring all we need to do is write out
// the database header.
if (pDb->m_uiFlags & FDB_REPLAYING_RFL)
{
if (pDb->m_bHadUpdOper &&
m_pCurrentBuf->bOkToWriteHdrs)
{
f_mutexUnlock( m_hBufMutex);
bMutexLocked = FALSE;
if (RC_BAD( rc = m_pDatabase->writeDbHdr( pDb->m_pDbStats,
pDb->m_pSFileHdl,
&m_pCurrentBuf->dbHdr,
&m_pCurrentBuf->cpHdr, FALSE)))
{
m_pDatabase->setMustCloseFlags( 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->m_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)
{
pDb->unlinkFromTransList( bCommitting);
bDbUnlocked = TRUE;
}
m_pCommitBuf = m_pCurrentBuf;
switchBuffers();
wakeUpWaiter( NE_SFLM_OK, TRUE);
if (!bOkToUnlock)
{
bMutexLocked = FALSE;
(void)waitForWrites( pDb->m_hWaitSem, m_pCommitBuf, FALSE);
}
}
else if (!bOkToUnlock)
{
bMutexLocked = FALSE;
(void)waitForWrites( pDb->m_hWaitSem, m_pCurrentBuf, FALSE);
}
}
else if (m_pCommitBuf)
{
if (!bOkToUnlock)
{
bMutexLocked = FALSE;
rc = waitForWrites( pDb->m_hWaitSem, 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)
{
pDb->unlinkFromTransList( bCommitting);
bDbUnlocked = TRUE;
}
bMutexLocked = FALSE;
rc = waitForWrites( pDb->m_hWaitSem, 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)
{
pDb->unlinkFromTransList( 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( pDb->m_hWaitSem, m_pCommitBuf, FALSE);
goto Exit;
}
else
{
m_pCommitBuf = m_pCurrentBuf;
switchBuffers();
if (bOkToUnlock)
{
pDb->unlinkFromTransList( 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->m_pDbStats) != NULL)
{
f_timeGetTimeStamp( &StartTime);
}
// Must write out whatever we have in the commit buffer before
// unlocking the database.
if (RC_BAD( tmpRc = flush( pDb, 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 == NE_FLM_IO_DISK_FULL)
{
rc = RC_SET( NE_SFLM_RFL_DISK_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 = m_pDatabase->writeDbHdr( pDb->m_pDbStats,
pDb->m_pSFileHdl,
&m_pCommitBuf->dbHdr,
&m_pCommitBuf->cpHdr, FALSE)))
{
if (RC_OK( rc))
{
rc = tmpRc;
}
m_pDatabase->setMustCloseFlags( tmpRc, FALSE);
goto Exit;
}
}
Exit:
if (!bDbUnlocked && bOkToUnlock)
{
pDb->unlinkFromTransList( 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: 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(
F_Db * pDb,
FLMUINT uiCurrPacketLen,
FLMBOOL bStartingNewFile)
{
RCODE rc = NE_SFLM_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( pDb, 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(
F_Db * pDb,
FLMUINT uiPacketLen,
FLMBOOL bDoNewIfOverLowLimit)
{
RCODE rc = NE_SFLM_OK;
FLMBYTE ucNextSerialNum [SFLM_SERIAL_NUM_SIZE];
flmAssert( m_pDatabase);
// 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;
}
// 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( pDb, 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( pDb, 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, SFLM_SERIAL_NUM_SIZE);
f_memcpy( m_ucNextSerialNum, ucNextSerialNum, SFLM_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(
F_Db * pDb,
FLMBOOL bNewKeepState)
{
RCODE rc = NE_SFLM_OK;
FLMBOOL bDbLocked = FALSE;
FLMUINT uiTransFileNum;
FLMUINT uiTransOffset;
FLMUINT uiTruncateSize;
SFLM_DB_HDR * pUncommittedDbHdr;
SFLM_DB_HDR checkpointDbHdr;
// Make sure we don't have a transaction going
if (pDb->m_eTransType != SFLM_NO_TRANS)
{
rc = RC_SET( NE_SFLM_TRANS_ACTIVE);
goto Exit;
}
// Make sure there is no active backup running
m_pDatabase->lockMutex();
if (m_pDatabase->m_bBackupActive)
{
m_pDatabase->unlockMutex();
rc = RC_SET( NE_SFLM_BACKUP_ACTIVE);
goto Exit;
}
m_pDatabase->unlockMutex();
// Lock the database - need to prevent update
// transactions and checkpoint thread from running.
if (RC_BAD( rc = pDb->lockExclusive( FLM_NO_TIMEOUT)))
{
goto Exit;
}
bDbLocked = TRUE;
// Must wait for all RFL writes before switching files.
(void)seeIfRflWritesDone( pDb->m_hWaitSem, TRUE);
// Better not be in the middle of a transaction.
flmAssert( !m_ui64CurrTransID);
pUncommittedDbHdr = &m_pDatabase->m_uncommittedDbHdr;
// 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( pUncommittedDbHdr, &m_pDatabase->m_lastCommittedDbHdr,
sizeof( SFLM_DB_HDR));
// 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 (!pUncommittedDbHdr->ui8RflKeepFiles)
{
goto Exit;
}
}
// Get the last committed serial numbers from the file's log header
// buffer.
f_memcpy( m_ucCurrSerialNum,
pUncommittedDbHdr->ucLastTransRflSerialNum,
SFLM_SERIAL_NUM_SIZE);
f_memcpy( m_ucNextSerialNum,
pUncommittedDbHdr->ucNextRflSerialNum,
SFLM_SERIAL_NUM_SIZE);
uiTransFileNum = (FLMUINT)pUncommittedDbHdr->ui32RflCurrFileNum;
uiTransOffset = (FLMUINT)pUncommittedDbHdr->ui32RflLastTransOffset;
// If ui32RflLastTransOffset 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;
}
}
else if (RC_BAD( rc = openFile( pDb->m_hWaitSem, uiTransFileNum,
m_ucCurrSerialNum)))
{
if (rc == NE_FLM_IO_PATH_NOT_FOUND || rc == NE_FLM_IO_INVALID_FILENAME)
{
rc = NE_SFLM_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_pDatabase->m_lastCommittedDbHdr.ui8RflKeepFiles
? 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++;
pUncommittedDbHdr->ui32RflCurrFileNum = (FLMUINT32)uiTransFileNum;
}
// 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, SFLM_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.
pUncommittedDbHdr->ui32RflLastTransOffset = 0;
f_memcpy( pUncommittedDbHdr->ucLastTransRflSerialNum,
m_ucCurrSerialNum, SFLM_SERIAL_NUM_SIZE);
f_memcpy( pUncommittedDbHdr->ucNextRflSerialNum,
m_ucNextSerialNum, SFLM_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 NE_SFLM_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)
{
// Do a quick check to see if it looks like we are in a
// checkpointed state
if( !m_pDatabase->m_lastCommittedDbHdr.ui8RflKeepFiles &&
m_pDatabase->m_lastCommittedDbHdr.ui32RflLastTransOffset > 512)
{
rc = RC_SET_AND_ASSERT( NE_SFLM_DATA_ERROR);
goto Exit;
}
f_memcpy( &checkpointDbHdr,
&m_pDatabase->m_checkpointDbHdr, sizeof( SFLM_DB_HDR));
checkpointDbHdr.ui32RflLastCPFileNum = (FLMUINT32)uiTransFileNum;
pUncommittedDbHdr->ui32RflLastCPFileNum = (FLMUINT32)uiTransFileNum;
checkpointDbHdr.ui32RflLastCPOffset = 512;
pUncommittedDbHdr->ui32RflLastCPOffset = 512;
}
// Write out the db header to disk.
if (RC_BAD( rc = m_pDatabase->writeDbHdr( pDb->m_pDbStats, pDb->m_pSFileHdl,
pUncommittedDbHdr,
bNewKeepState
? &checkpointDbHdr
: &m_pDatabase->m_checkpointDbHdr, FALSE)))
{
goto Exit;
}
// Copy the uncommitted log header back to the committed log header and
// copy the CP log header (if changed above).
m_pDatabase->lockMutex();
f_memcpy( &m_pDatabase->m_lastCommittedDbHdr, pUncommittedDbHdr,
sizeof( SFLM_DB_HDR));
if( bNewKeepState)
{
f_memcpy( &m_pDatabase->m_checkpointDbHdr, &checkpointDbHdr,
sizeof( SFLM_DB_HDR));
}
m_pDatabase->unlockMutex();
Exit:
if (bDbLocked)
{
(void)pDb->unlockExclusive();
}
return( rc);
}
/********************************************************************
Desc: Finish packet by outputting header information for it.
*********************************************************************/
RCODE F_Rfl::finishPacket(
F_Db * pDb,
FLMUINT uiPacketType,
FLMUINT uiPacketBodyLen,
FLMBOOL bDoNewIfOverLowLimit)
{
RCODE rc = NE_SFLM_OK;
FLMUINT uiPacketLen;
FLMBYTE * pucPacket;
uiPacketLen = uiPacketBodyLen + 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( pDb, uiPacketLen, bDoNewIfOverLowLimit)))
{
goto Exit;
}
// Get a pointer to packet header.
pucPacket = &(m_pCurrentBuf->pIOBuffer->getBuffer()[
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,
uiPacketBodyLen);
// 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(
F_SEM hWaitSem,
FLMUINT uiTruncateSize)
{
RCODE rc = NE_SFLM_OK;
FLMUINT uiFileNum;
flmAssert( uiTruncateSize >= 512);
// Keeping of log files better not be enabled.
flmAssert( !m_pDatabase->m_lastCommittedDbHdr.ui8RflKeepFiles);
// Better not be in the middle of a transaction.
flmAssert( !m_ui64CurrTransID);
// Open the current RFL file. If it does not exist, it is OK - there
// is nothing to truncate.
uiFileNum = (FLMUINT)m_pDatabase->m_lastCommittedDbHdr.ui32RflCurrFileNum;
if (RC_BAD( rc = openFile( hWaitSem, uiFileNum,
m_pDatabase->m_lastCommittedDbHdr.ucLastTransRflSerialNum)))
{
if (rc == NE_FLM_IO_PATH_NOT_FOUND || rc == NE_FLM_IO_INVALID_FILENAME)
{
rc = NE_SFLM_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(
F_Db * pDb)
{
RCODE rc = NE_SFLM_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_pDatabase->m_lastCommittedDbHdr.ucLastTransRflSerialNum,
SFLM_SERIAL_NUM_SIZE);
f_memcpy( m_ucNextSerialNum,
m_pDatabase->m_lastCommittedDbHdr.ucNextRflSerialNum,
SFLM_SERIAL_NUM_SIZE);
uiFileNum = (FLMUINT)m_pDatabase->m_lastCommittedDbHdr.ui32RflCurrFileNum;
uiLastTransOffset =
(FLMUINT)m_pDatabase->m_lastCommittedDbHdr.ui32RflLastTransOffset;
// 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( pDb->m_hWaitSem)))
{
goto Exit;
}
closeFile();
}
else if (RC_BAD( rc = openFile( pDb->m_hWaitSem,
uiFileNum, m_ucCurrSerialNum)))
{
if (rc != NE_FLM_IO_PATH_NOT_FOUND && rc != NE_FLM_IO_INVALID_FILENAME)
{
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( NE_SFLM_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( pDb, uiFileNum,
m_ucCurrSerialNum, m_ucNextSerialNum,
m_pDatabase->m_lastCommittedDbHdr.ui8RflKeepFiles
? 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.
m_bKeepRflFiles = m_pDatabase->m_lastCommittedDbHdr.ui8RflKeepFiles
? TRUE
: FALSE;
m_uiRflMaxFileSize =
(FLMUINT)m_pDatabase->m_lastCommittedDbHdr.ui32RflMaxFileSize;
// 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_SFlmSysData.uiMaxFileSize)
{
m_uiRflMaxFileSize = gv_SFlmSysData.uiMaxFileSize;
}
m_uiRflMinFileSize =
(FLMUINT)m_pDatabase->m_lastCommittedDbHdr.ui32RflMinFileSize;
// 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_pDatabase->m_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(
F_Db * pDb)
{
RCODE rc = NE_SFLM_OK;
FLMBYTE * pucPacketStart;
FLMBYTE * pucPacketBody;
FLMUINT uiPacketBodyLen;
FLMUINT uiMaxPacketBodyLen;
flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK);
#ifndef FLM_DEBUG
F_UNREFERENCED_PARM( pDb);
#endif
// Do nothing if logging is disabled.
if( !isLoggingEnabled())
{
goto Exit;
}
flmAssert( !(pDb->m_uiFlags & FDB_REPLAYING_RFL));
// Better not be in the middle of a transaction.
flmAssert( !m_ui64CurrTransID);
if( RC_BAD( rc = setupTransaction( pDb)))
{
goto Exit;
}
uiMaxPacketBodyLen = FLM_MAX_SEN_LEN;
// Make sure we have space in the RFL buffer for a complete packet.
if (!haveBuffSpace( uiMaxPacketBodyLen + RFL_PACKET_OVERHEAD))
{
if (RC_BAD( rc = flush( pDb, m_pCurrentBuf)))
{
goto Exit;
}
}
// Get a pointer to where we will be laying down the packet body.
pucPacketBody = pucPacketStart = getPacketBodyPtr();
// Output the transaction ID.
f_encodeSEN( pDb->m_ui64CurrTransID, &pucPacketBody);
// Finish the packet
uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart);
flmAssert( uiPacketBodyLen <= uiMaxPacketBodyLen);
if (RC_BAD( rc = finishPacket(
pDb, 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_ui64CurrTransID = pDb->m_ui64CurrTransID;
Exit:
return( rc);
}
/********************************************************************
Desc: Do a transaction begin operation during restore or recovery.
*********************************************************************/
RCODE F_Rfl::recovTransBegin(
F_Db * pDb,
eRestoreAction * peAction)
{
RCODE rc = NE_SFLM_OK;
if( m_pRestoreStatus)
{
if( RC_BAD( rc = m_pRestoreStatus->reportBeginTrans(
peAction, m_ui64CurrTransID)))
{
goto Exit;
}
if (*peAction == SFLM_RESTORE_ACTION_STOP)
{
// Need to set m_ui64CurrTransID 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_ui64CurrTransID = 0;
goto Exit;
}
}
if (RC_BAD( rc = pDb->transBegin( SFLM_UPDATE_TRANS)))
{
goto Exit;
}
Exit:
return( rc);
}
/********************************************************************
Desc: Flushes the RFL and sets some things in the log header.
*********************************************************************/
void F_Rfl::finalizeTransaction( void)
{
FLMUINT uiRflTransEndOffset;
SFLM_DB_HDR * pDbHdr = &m_pDatabase->m_uncommittedDbHdr;
// Save the serial numbers and file numbers into the file's
// uncommitted log header.
pDbHdr->ui32RflCurrFileNum = (FLMUINT32)m_pCurrentBuf->uiCurrFileNum;
uiRflTransEndOffset = getCurrWriteOffset();
pDbHdr->ui32RflLastTransOffset = (FLMUINT32)uiRflTransEndOffset;
f_memcpy( pDbHdr->ucLastTransRflSerialNum,
m_ucCurrSerialNum, SFLM_SERIAL_NUM_SIZE);
f_memcpy( pDbHdr->ucNextRflSerialNum,
m_ucNextSerialNum, SFLM_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(
F_Db * pDb,
FLMUINT uiPacketType,
FLMBOOL bThrowLogAway,
FLMBOOL * pbLoggedTransEnd)
{
RCODE rc = NE_SFLM_OK;
RCODE rc2 = NE_SFLM_OK;
FLMUINT uiLowFileNum;
FLMUINT uiHighFileNum;
char szRflFileName [F_PATH_MAX_SIZE];
FLMBYTE * pucPacketStart;
FLMBYTE * pucPacketBody;
FLMUINT uiPacketBodyLen;
FLMUINT uiMaxPacketBodyLen;
flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK);
#ifndef FLM_DEBUG
F_UNREFERENCED_PARM( pDb);
#endif
// Initialize the "logged trans end" flag
if( pbLoggedTransEnd)
{
*pbLoggedTransEnd = FALSE;
}
// Do nothing if logging is disabled.
if (!isLoggingEnabled())
{
goto Exit;
}
flmAssert( m_pFileHdl);
flmAssert( m_pDatabase);
// 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)m_pDatabase->m_uncommittedDbHdr.ui32RflCurrFileNum);
uiLowFileNum = m_uiTransStartFile + 1;
uiHighFileNum = m_pCurrentBuf->uiCurrFileNum;
// Close the current file so it can be deleted.
if (RC_BAD( rc = waitForCommit( pDb->m_hWaitSem)))
{
goto Exit;
}
closeFile();
// Delete as many of the files as possible. Don't worry
// about errors here.
while (uiLowFileNum <= uiHighFileNum)
{
FLMUINT uiFileNameSize = sizeof( szRflFileName);
FLMBOOL bNameTruncated;
getFullRflFileName( uiLowFileNum, szRflFileName,
&uiFileNameSize, &bNameTruncated);
if (!bNameTruncated)
{
(void)gv_SFlmSysData.pFileSystem->deleteFile( 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 = NE_SFLM_OK;
goto Exit;
}
}
}
else
{
// Log a commit or abort packet.
uiMaxPacketBodyLen = FLM_MAX_SEN_LEN;
// Make sure we have space in the RFL buffer for a complete packet.
if( !haveBuffSpace( uiMaxPacketBodyLen + RFL_PACKET_OVERHEAD))
{
if (RC_BAD( rc = flush( pDb, m_pCurrentBuf)))
{
goto Throw_Away_Transaction;
}
}
// Get a pointer to where we will be laying down the packet body.
pucPacketBody = pucPacketStart = getPacketBodyPtr();
// Output the transaction ID.
f_encodeSEN( m_ui64CurrTransID, &pucPacketBody);
// Finish the packet
uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart);
flmAssert( uiPacketBodyLen <= uiMaxPacketBodyLen);
if (RC_BAD( rc = finishPacket( pDb, uiPacketType,
uiPacketBodyLen, FALSE)))
{
goto Throw_Away_Transaction;
}
finalizeTransaction();
if( pbLoggedTransEnd)
{
*pbLoggedTransEnd = TRUE;
}
}
Exit:
m_ui64CurrTransID = 0;
return( RC_BAD( rc) ? rc : rc2);
}
/********************************************************************
Desc: Log a block chain free packet
*********************************************************************/
RCODE F_Rfl::logBlockChainFree(
F_Db * pDb,
FLMUINT64 ui64MaintDocID,
FLMUINT uiStartBlkAddr,
FLMUINT uiEndBlkAddr,
FLMUINT uiCount)
{
RCODE rc = NE_SFLM_OK;
FLMBYTE * pucPacketStart;
FLMBYTE * pucPacketBody;
FLMUINT uiPacketBodyLen;
FLMUINT uiMaxPacketBodyLen;
flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK);
#ifndef FLM_DEBUG
F_UNREFERENCED_PARM( pDb);
#endif
// Do nothing if logging is disabled.
if (!isLoggingEnabled())
{
goto Exit;
}
// Better be in the middle of a transaction.
flmAssert( m_ui64CurrTransID);
// Increment the operation count
m_uiOperCount++;
// Determine the maximum packet size
uiMaxPacketBodyLen = FLM_MAX_SEN_LEN * 4;
// Make sure we have space in the RFL buffer for a complete packet.
if( !haveBuffSpace( uiMaxPacketBodyLen + RFL_PACKET_OVERHEAD))
{
if( RC_BAD( rc = flush( pDb, m_pCurrentBuf)))
{
goto Exit;
}
}
// Get a pointer to where we will be laying down the packet body.
pucPacketBody = pucPacketStart = getPacketBodyPtr();
// Output the maintenance document ID
f_encodeSEN( ui64MaintDocID, &pucPacketBody);
// Output the starting block address
f_encodeSEN( uiStartBlkAddr, &pucPacketBody);
// Output the ending block address
f_encodeSEN( uiEndBlkAddr, &pucPacketBody);
// Output the block count
f_encodeSEN( uiCount, &pucPacketBody);
// Finish the packet
uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart);
flmAssert( uiPacketBodyLen <= uiMaxPacketBodyLen);
if( RC_BAD( rc = finishPacket( pDb, RFL_BLK_CHAIN_FREE_PACKET,
uiPacketBodyLen, FALSE)))
{
goto Exit;
}
Exit:
return( rc);
}
/********************************************************************
Desc: Free a chain of blocks
*********************************************************************/
RCODE F_Rfl::recovBlockChainFree(
F_Db * pDb,
const FLMBYTE * pucPacketBody,
FLMUINT uiPacketBodyLen,
eRestoreAction * peAction)
{
RCODE rc = NE_SFLM_OK;
FLMUINT64 ui64MaintRowId;
FLMUINT uiStartBlkAddr;
FLMUINT uiEndBlkAddr;
FLMUINT uiCount;
FLMUINT uiBlocksFreed;
const FLMBYTE * pucEnd = pucPacketBody + uiPacketBodyLen;
if( RC_BAD( rc = f_decodeSEN64( &pucPacketBody, pucEnd, &ui64MaintRowId)))
{
goto Exit;
}
if( RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiStartBlkAddr)))
{
goto Exit;
}
if( RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiEndBlkAddr)))
{
goto Exit;
}
if( RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiCount)))
{
goto Exit;
}
if( pucPacketBody != pucEnd)
{
rc = RC_SET_AND_ASSERT( NE_SFLM_BAD_RFL_PACKET);
goto Exit;
}
if( m_pRestoreStatus)
{
if( RC_BAD( rc = m_pRestoreStatus->reportBlockChainFree(
peAction, m_ui64CurrTransID, ui64MaintRowId, uiStartBlkAddr,
uiEndBlkAddr, uiCount)))
{
goto Exit;
}
if( *peAction == SFLM_RESTORE_ACTION_STOP)
{
goto Exit;
}
}
if( RC_BAD( rc = pDb->maintBlockChainFree(
ui64MaintRowId, uiStartBlkAddr, uiCount, uiEndBlkAddr, &uiBlocksFreed)))
{
goto Exit;
}
if( uiCount != uiBlocksFreed)
{
rc = RC_SET_AND_ASSERT( NE_SFLM_DATA_ERROR);
goto Exit;
}
Exit:
return( rc);
}
/********************************************************************
Desc: Log index suspend and resume packets
*********************************************************************/
RCODE F_Rfl::logIndexSuspendOrResume(
F_Db * pDb,
FLMUINT uiIndexNum,
FLMUINT uiPacketType)
{
RCODE rc = NE_SFLM_OK;
FLMBYTE * pucPacketStart;
FLMBYTE * pucPacketBody;
FLMUINT uiPacketBodyLen;
FLMUINT uiMaxPacketBodyLen;
flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK);
#ifndef FLM_DEBUG
F_UNREFERENCED_PARM( pDb);
#endif
// Do nothing if logging is disabled.
if (!isLoggingEnabled())
{
goto Exit;
}
// Better be in the middle of a transaction.
flmAssert( m_ui64CurrTransID);
// Increment the operation count
m_uiOperCount++;
// Determine the maximum packet size
uiMaxPacketBodyLen = FLM_MAX_SEN_LEN;
// Make sure we have space in the RFL buffer for a complete packet.
if( !haveBuffSpace( uiMaxPacketBodyLen + RFL_PACKET_OVERHEAD))
{
if( RC_BAD( rc = flush( pDb, m_pCurrentBuf)))
{
goto Exit;
}
}
// Get a pointer to where we will be laying down the packet body.
pucPacketBody = pucPacketStart = getPacketBodyPtr();
// Output the index number.
flmAssert( uiIndexNum <= FLM_MAX_UINT16);
f_encodeSEN( uiIndexNum, &pucPacketBody);
// Finish the packet
uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart);
flmAssert( uiPacketBodyLen <= uiMaxPacketBodyLen);
if (RC_BAD( rc = finishPacket( pDb, uiPacketType, uiPacketBodyLen, FALSE)))
{
goto Exit;
}
Exit:
return( rc);
}
/********************************************************************
Desc: Suspend or resume an index during restore or recovery.
*********************************************************************/
RCODE F_Rfl::recovIndexSuspendResume(
F_Db * pDb,
FLMUINT uiPacketType,
const FLMBYTE * pucPacketBody,
FLMUINT uiPacketBodyLen,
eRestoreAction * peAction)
{
RCODE rc = NE_SFLM_OK;
FLMUINT uiIndexNum;
const FLMBYTE * pucEnd = pucPacketBody + uiPacketBodyLen;
if( RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiIndexNum)))
{
goto Exit;
}
if( pucPacketBody != pucEnd)
{
rc = RC_SET_AND_ASSERT( NE_SFLM_BAD_RFL_PACKET);
goto Exit;
}
if( m_pRestoreStatus)
{
if( uiPacketType == RFL_INDEX_SUSPEND_PACKET)
{
if( RC_BAD( rc = m_pRestoreStatus->reportIndexSuspend(
peAction, m_ui64CurrTransID, uiIndexNum)))
{
goto Exit;
}
}
else
{
if( RC_BAD( rc = m_pRestoreStatus->reportIndexResume(
peAction, m_ui64CurrTransID, uiIndexNum)))
{
goto Exit;
}
}
if( *peAction == SFLM_RESTORE_ACTION_STOP)
{
goto Exit;
}
}
if( uiPacketType == RFL_INDEX_SUSPEND_PACKET)
{
if( RC_BAD( rc = pDb->indexSuspend( uiIndexNum)))
{
goto Exit;
}
}
else
{
if( RC_BAD( rc = pDb->indexResume( uiIndexNum)))
{
goto Exit;
}
}
Exit:
return( rc);
}
/********************************************************************
Desc: Log a reduce packet
*********************************************************************/
RCODE F_Rfl::logReduce(
F_Db * pDb,
FLMUINT uiCount)
{
RCODE rc = NE_SFLM_OK;
FLMBYTE * pucPacketStart;
FLMBYTE * pucPacketBody;
FLMUINT uiPacketBodyLen;
FLMUINT uiMaxPacketBodyLen;
flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK);
#ifndef FLM_DEBUG
F_UNREFERENCED_PARM( pDb);
#endif
// Do nothing if logging is disabled.
if (!isLoggingEnabled())
{
goto Exit;
}
// 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( pDb)))
{
goto Exit;
}
uiMaxPacketBodyLen = 2 * FLM_MAX_SEN_LEN;
// Make sure we have space in the RFL buffer for a complete packet.
if( !haveBuffSpace( uiMaxPacketBodyLen + RFL_PACKET_OVERHEAD))
{
if( RC_BAD( rc = flush( pDb, m_pCurrentBuf)))
{
goto Exit;
}
}
// Get a pointer to where we will be laying down the packet body.
pucPacketBody = pucPacketStart = getPacketBodyPtr();
// Output the transaction ID.
f_encodeSEN( pDb->m_ui64CurrTransID, &pucPacketBody);
// Output the count
f_encodeSEN( uiCount, &pucPacketBody);
// Finish the packet
uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart);
flmAssert( uiPacketBodyLen <= uiMaxPacketBodyLen);
if( RC_BAD( rc = finishPacket( pDb, RFL_REDUCE_PACKET,
uiPacketBodyLen, TRUE)))
{
goto Exit;
}
// Finalize the transaction (as if we were committing a transaction)
finalizeTransaction();
Exit:
return( rc);
}
/********************************************************************
Desc: Reduce the database during restore or recovery.
*********************************************************************/
RCODE F_Rfl::recovReduce(
F_Db * pDb,
const FLMBYTE * pucPacketBody,
FLMUINT uiPacketBodyLen,
eRestoreAction * peAction)
{
RCODE rc = NE_SFLM_OK;
FLMUINT uiCount;
const FLMBYTE * pucEnd = pucPacketBody + uiPacketBodyLen;
if( RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiCount)))
{
goto Exit;
}
if( pucPacketBody != pucEnd)
{
rc = RC_SET_AND_ASSERT( NE_SFLM_BAD_RFL_PACKET);
goto Exit;
}
if( m_pRestoreStatus)
{
if( RC_BAD( rc = m_pRestoreStatus->reportReduce(
peAction, m_ui64CurrTransID, uiCount)))
{
goto Exit;
}
if( *peAction == SFLM_RESTORE_ACTION_STOP)
{
// Need to set m_ui64CurrTransID 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_ui64CurrTransID = 0;
goto Exit;
}
}
if( RC_BAD( rc = pDb->reduceSize( uiCount, &uiCount)))
{
goto Exit;
}
Exit:
m_ui64CurrTransID = 0;
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(
F_Db * pDb,
FLMUINT uiOldVersion)
{
RCODE rc = NE_SFLM_OK;
FLMBYTE * pucPacketStart;
FLMBYTE * pucPacketBody;
FLMUINT uiPacketBodyLen;
FLMUINT uiMaxPacketBodyLen;
flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK);
#ifndef FLM_DEBUG
F_UNREFERENCED_PARM( pDb);
#endif
// Do nothing if logging is disabled.
if (!isLoggingEnabled())
{
goto Exit;
}
// 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( pDb)))
{
goto Exit;
}
uiMaxPacketBodyLen = 3 * FLM_MAX_SEN_LEN;
// Make sure we have space in the RFL buffer for a complete packet.
if( !haveBuffSpace( uiMaxPacketBodyLen + RFL_PACKET_OVERHEAD))
{
if (RC_BAD( rc = flush( pDb, m_pCurrentBuf)))
{
goto Exit;
}
}
// Get a pointer to where we will be laying down the packet body.
pucPacketBody = pucPacketStart = getPacketBodyPtr();
// Output the transaction ID
f_encodeSEN( pDb->m_ui64CurrTransID, &pucPacketBody);
// Output the old database version
f_encodeSEN( uiOldVersion, &pucPacketBody);
// Output the new database version
f_encodeSEN( SFLM_CURRENT_VERSION_NUM, &pucPacketBody);
// Finish the packet
uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart);
flmAssert( uiPacketBodyLen <= uiMaxPacketBodyLen);
if( RC_BAD( rc = finishPacket( pDb, RFL_UPGRADE_PACKET,
uiPacketBodyLen, TRUE)))
{
goto Exit;
}
// Finalize the transaction (as if we were committing a transaction)
finalizeTransaction();
Exit:
m_ui64CurrTransID = 0;
return( rc);
}
/********************************************************************
Desc: Upgrade the database during restore or recovery.
*********************************************************************/
RCODE F_Rfl::recovUpgrade(
F_Db * pDb,
const FLMBYTE * pucPacketBody,
FLMUINT uiPacketBodyLen,
eRestoreAction * peAction)
{
RCODE rc = NE_SFLM_OK;
FLMUINT uiOldDbVersion;
FLMUINT uiNewDbVersion;
if( uiPacketBodyLen != 8)
{
rc = RC_SET_AND_ASSERT( NE_SFLM_BAD_RFL_PACKET);
goto Exit;
}
uiOldDbVersion = (FLMUINT)FB2UD( pucPacketBody);
pucPacketBody += 4;
uiNewDbVersion = (FLMUINT)FB2UD( pucPacketBody);
pucPacketBody += 4;
if( m_pRestoreStatus)
{
if( RC_BAD( rc = m_pRestoreStatus->reportUpgrade(
peAction, m_ui64CurrTransID, uiOldDbVersion, uiNewDbVersion)))
{
goto Exit;
}
if( *peAction == SFLM_RESTORE_ACTION_STOP)
{
// Need to set m_ui64CurrTransID 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_ui64CurrTransID = 0;
goto Exit;
}
}
// 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( uiNewDbVersion > SFLM_CURRENT_VERSION_NUM)
{
rc = RC_SET( NE_SFLM_UNALLOWED_UPGRADE);
goto Exit;
}
else if( (FLMUINT)m_pDatabase->m_lastCommittedDbHdr.ui32DbVersion <
uiNewDbVersion)
{
// The logged "new" version may be a lesser version
// than SFLM_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 = pDb->upgrade( NULL)))
{
goto Exit;
}
}
Exit:
m_ui64CurrTransID = 0;
return( rc);
}
/********************************************************************
Desc:
*********************************************************************/
RCODE F_Rfl::logEncryptionKey(
F_Db * pDb,
FLMUINT uiPacketType,
FLMBYTE * pucKey,
FLMUINT32 ui32KeyLen)
{
RCODE rc = NE_SFLM_OK;
FLMBYTE * pucPacketStart;
FLMBYTE * pucPacketBody;
FLMUINT uiPacketBodyLen;
FLMUINT uiMaxPacketBodyLen;
flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK);
#ifndef FLM_DEBUG
F_UNREFERENCED_PARM( pDb);
#endif
flmAssert( uiPacketType == RFL_WRAP_KEY_PACKET ||
uiPacketType == RFL_ENABLE_ENCRYPTION_PACKET);
// Do nothing if logging is disabled.
if (!isLoggingEnabled())
{
goto Exit;
}
if( RC_BAD( rc = setupTransaction( pDb)))
{
goto Exit;
}
uiMaxPacketBodyLen = (2 * FLM_MAX_SEN_LEN) + ui32KeyLen;
// Make sure we have space in the RFL buffer for a complete packet.
if( !haveBuffSpace( uiMaxPacketBodyLen + RFL_PACKET_OVERHEAD))
{
if( RC_BAD( rc = flush( pDb, m_pCurrentBuf)))
{
goto Exit;
}
}
// Get a pointer to where we will be laying down the packet body.
pucPacketBody = pucPacketStart = getPacketBodyPtr();
// Output the transaction ID
f_encodeSEN( pDb->m_ui64CurrTransID, &pucPacketBody);
// Store the length of the key
f_encodeSEN( ui32KeyLen, &pucPacketBody);
// If we were built without encryption, the key length will be zero,
// so no need to store the key.
if( ui32KeyLen)
{
f_memcpy( pucPacketBody, pucKey, ui32KeyLen);
pucPacketBody += ui32KeyLen;
}
// Finish the packet
uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart);
flmAssert( uiPacketBodyLen <= uiMaxPacketBodyLen);
if( RC_BAD( rc = finishPacket( pDb, uiPacketType,
uiPacketBodyLen, TRUE)))
{
goto Exit;
}
finalizeTransaction();
Exit:
return( rc);
}
/********************************************************************
Desc:
*********************************************************************/
RCODE F_Rfl::recovEncryptionKey(
F_Db * pDb,
FLMUINT uiPacketType,
const FLMBYTE * pucPacketBody,
FLMUINT uiPacketBodyLen,
eRestoreAction * peAction)
{
RCODE rc = NE_SFLM_OK;
FLMUINT uiDBKeyLen;
FLMBOOL bTransStarted = FALSE;
SFLM_DB_HDR * pUncommittedLogHdr = &m_pDatabase->m_uncommittedDbHdr;
const FLMBYTE * pucEnd = pucPacketBody + uiPacketBodyLen;
if( RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiDBKeyLen)))
{
goto Exit;
}
if( pucPacketBody + uiDBKeyLen != pucEnd)
{
rc = RC_SET_AND_ASSERT( NE_SFLM_BAD_RFL_PACKET);
goto Exit;
}
if( m_pRestoreStatus)
{
if( uiPacketType == RFL_WRAP_KEY_PACKET)
{
if( RC_BAD( rc = m_pRestoreStatus->reportWrapKey(
peAction, m_ui64CurrTransID)))
{
goto Exit;
}
}
else if( uiPacketType == RFL_ENABLE_ENCRYPTION_PACKET)
{
if( RC_BAD( rc = m_pRestoreStatus->reportEnableEncryption(
peAction, m_ui64CurrTransID)))
{
goto Exit;
}
}
else
{
rc = RC_SET_AND_ASSERT( NE_SFLM_BAD_RFL_PACKET);
goto Exit;
}
if( *peAction == SFLM_RESTORE_ACTION_STOP)
{
m_ui64CurrTransID = 0;
goto Exit;
}
}
if( uiDBKeyLen)
{
// 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 = pDb->transBegin( SFLM_UPDATE_TRANS)))
{
goto Exit;
}
bTransStarted = TRUE;
if( uiDBKeyLen > SFLM_MAX_ENC_KEY_SIZE)
{
rc = RC_SET_AND_ASSERT( NE_SFLM_BAD_ENCKEY_SIZE);
goto Exit;
}
f_memcpy( pUncommittedLogHdr->ucDbKey, pucPacketBody, uiDBKeyLen);
pUncommittedLogHdr->ui32DbKeyLen = (FLMUINT32)uiDBKeyLen;
pDb->m_bHadUpdOper = TRUE;
if( RC_BAD( rc = pDb->commitTrans( 0, TRUE)))
{
goto Exit;
}
bTransStarted = FALSE;
}
Exit:
if( bTransStarted)
{
RCODE tmpRc;
if( RC_BAD( tmpRc = pDb->commitTrans( 0, TRUE)))
{
pDb->abortTrans();
if (RC_OK( rc))
{
rc = tmpRc;
}
}
}
m_ui64CurrTransID = 0;
return( rc);
}
/********************************************************************
Desc:
*********************************************************************/
RCODE F_Rfl::logEncDefKey(
F_Db * pDb,
FLMUINT uiEncDefId,
FLMBYTE * pucKeyValue,
FLMUINT uiKeyValueLen,
FLMUINT uiKeySize)
{
RCODE rc = NE_SFLM_OK;
FLMBYTE * pucPacketStart;
FLMBYTE * pucPacketBody;
FLMUINT uiPacketBodyLen;
FLMUINT uiMaxPacketBodyLen;
flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK);
#ifndef FLM_DEBUG
F_UNREFERENCED_PARM( pDb);
#endif
// Do nothing if logging is disabled.
if (!isLoggingEnabled())
{
goto Exit;
}
// Better be in the middle of a transaction.
flmAssert( m_ui64CurrTransID);
// Increment the operation count
m_uiOperCount++;
// Determine the maximum packet size
uiMaxPacketBodyLen = (3 * FLM_MAX_SEN_LEN) + uiKeyValueLen;
// Make sure we have space in the RFL buffer for a complete packet.
if( !haveBuffSpace( uiMaxPacketBodyLen + RFL_PACKET_OVERHEAD))
{
if( RC_BAD( rc = flush( pDb, m_pCurrentBuf)))
{
goto Exit;
}
}
// Get a pointer to where we will be laying down the packet body.
pucPacketBody = pucPacketStart = getPacketBodyPtr();
// Output the encryption definition ID
f_encodeSEN( uiEncDefId, &pucPacketBody);
// Output the key size (number of bits)
f_encodeSEN( uiKeySize, &pucPacketBody);
// Output the key value length
f_encodeSEN( uiKeyValueLen, &pucPacketBody);
// Output the key
f_memcpy( pucPacketBody, pucKeyValue, uiKeyValueLen);
pucPacketBody += uiKeyValueLen;
// Finish the packet
uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart);
flmAssert( uiPacketBodyLen <= uiMaxPacketBodyLen);
if (RC_BAD( rc = finishPacket( pDb, RFL_ENC_DEF_KEY_PACKET,
uiPacketBodyLen, FALSE)))
{
goto Exit;
}
Exit:
return( rc);
}
/********************************************************************
Desc:
*********************************************************************/
RCODE F_Rfl::recovEncDefKey(
F_Db * pDb,
const FLMBYTE * pucPacketBody,
FLMUINT uiPacketBodyLen,
eRestoreAction *) // peAction
{
RCODE rc = NE_SFLM_OK;
FLMUINT64 ui64RowId;
FLMUINT uiKeySize;
FLMUINT uiKeyValueLen;
const FLMBYTE * pucEnd = pucPacketBody + uiPacketBodyLen;
if( RC_BAD( rc = f_decodeSEN64( &pucPacketBody, pucEnd, &ui64RowId)))
{
goto Exit;
}
if( RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiKeySize)))
{
goto Exit;
}
if( RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiKeyValueLen)))
{
goto Exit;
}
if( pucPacketBody + uiKeyValueLen != pucEnd)
{
rc = RC_SET_AND_ASSERT( NE_SFLM_BAD_RFL_PACKET);
goto Exit;
}
// Retrieve the encryption definition
//VISIT: NEED TO FLESH THIS OUT
#if 0
if( RC_BAD( rc = pDb->getNode( XFLM_DICT_COLLECTION,
ui64RootId, FLM_EXACT, &pNode)))
{
if( rc == NE_SFLM_ROW_NOT_FOUND)
{
rc = RC_SET_AND_ASSERT( NE_SFLM_BAD_RFL_PACKET);
}
goto Exit;
}
// Set the key in the DOM node as a binary string.
if( RC_BAD( rc = pNode->createAttribute( pDb, ATTR_ENCRYPTION_KEY_TAG,
(IF_DOMNode **)&pAttr)))
{
goto Exit;
}
if( RC_BAD( rc = pAttr->removeModeFlags( pDb,
FDOM_READ_ONLY | FDOM_CANNOT_DELETE)))
{
goto Exit;
}
if( RC_BAD( rc = pAttr->setBinary( pDb, pucPacketBody, uiKeyValueLen)))
{
goto Exit;
}
if( RC_BAD( rc = pAttr->addModeFlags( pDb,
FDOM_READ_ONLY | FDOM_CANNOT_DELETE)))
{
goto Exit;
}
// Set the key size
if( RC_BAD( rc = pNode->createAttribute( pDb, ATTR_ENCRYPTION_KEY_SIZE_TAG,
(IF_DOMNode **)&pAttr)))
{
goto Exit;
}
if( RC_BAD( rc = pAttr->removeModeFlags( pDb,
FDOM_READ_ONLY | FDOM_CANNOT_DELETE)))
{
goto Exit;
}
if( RC_BAD( rc = pAttr->setUINT( pDb, uiKeySize)))
{
goto Exit;
}
if( RC_BAD( rc = pAttr->addModeFlags( pDb,
FDOM_READ_ONLY | FDOM_CANNOT_DELETE)))
{
goto Exit;
}
#endif
Exit:
return( rc);
}
/********************************************************************
Desc:
*********************************************************************/
RCODE F_Rfl::logRollOverDbKey(
F_Db * pDb)
{
RCODE rc = NE_SFLM_OK;
FLMBYTE * pucPacketStart;
FLMBYTE * pucPacketBody;
FLMUINT uiPacketBodyLen;
FLMUINT uiMaxPacketBodyLen;
flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK);
#ifndef FLM_DEBUG
F_UNREFERENCED_PARM( pDb);
#endif
// Do nothing if logging is disabled.
if (!isLoggingEnabled())
{
goto Exit;
}
if( RC_BAD( rc = setupTransaction( pDb)))
{
goto Exit;
}
uiMaxPacketBodyLen = FLM_MAX_SEN_LEN;
// Make sure we have space in the RFL buffer for a complete packet.
if( !haveBuffSpace( uiMaxPacketBodyLen + RFL_PACKET_OVERHEAD))
{
if( RC_BAD( rc = flush( pDb, m_pCurrentBuf)))
{
goto Exit;
}
}
// Get a pointer to where we will be laying down the packet body.
pucPacketBody = pucPacketStart = getPacketBodyPtr();
// Output the transaction ID
f_encodeSEN( pDb->m_ui64CurrTransID, &pucPacketBody);
// Finish the packet
uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart);
flmAssert( uiPacketBodyLen <= uiMaxPacketBodyLen);
if( RC_BAD( rc = finishPacket( pDb, RFL_ROLL_OVER_DB_KEY_PACKET,
uiPacketBodyLen, TRUE)))
{
goto Exit;
}
finalizeTransaction();
Exit:
return( rc);
}
/********************************************************************
Desc:
*********************************************************************/
RCODE F_Rfl::recovRollOverDbKey(
F_Db * pDb,
const FLMBYTE *, // pucPacketBody,
FLMUINT uiPacketBodyLen,
eRestoreAction * peAction)
{
RCODE rc = NE_SFLM_OK;
if( uiPacketBodyLen)
{
rc = RC_SET_AND_ASSERT( NE_SFLM_BAD_RFL_PACKET);
goto Exit;
}
if( m_pRestoreStatus)
{
if( RC_BAD( rc = m_pRestoreStatus->reportRollOverDbKey(
peAction, m_ui64CurrTransID)))
{
goto Exit;
}
if( *peAction == SFLM_RESTORE_ACTION_STOP)
{
m_ui64CurrTransID = 0;
goto Exit;
}
}
if( RC_BAD( rc = pDb->rollOverDbKey()))
{
goto Exit;
}
Exit:
m_ui64CurrTransID = 0;
return( rc);
}
/********************************************************************
Desc:
*********************************************************************/
RCODE F_Rfl::logColumnValues(
F_Db * pDb,
FLMUINT uiTableNum,
F_COLUMN_VALUE * pColumnValues,
FLMUINT uiNumColumnValues)
{
RCODE rc = NE_SFLM_OK;
FLMUINT uiPacketLen = RFL_PACKET_OVERHEAD;
FLMUINT uiBytesLeftToWrite;
FLMUINT uiBytesToWrite;
FLMBYTE * pucValue;
FLMBYTE * pucOut;
FLMBYTE * pucIV;
FLMBYTE * pucStart;
F_TABLE * pTable = pDb->m_pDict->getTable( uiTableNum);
F_COLUMN * pColumn;
F_ENCDEF * pTableEncDef = (pTable->lfInfo.uiEncDefNum)
? pDb->m_pDict->getEncDef( pTable->lfInfo.uiEncDefNum)
: (F_ENCDEF *)NULL;
F_ENCDEF * pEncDef;
FLMUINT uiIVLen;
FLMUINT uiEncLen;
FLMUINT uiSenLen1;
FLMUINT uiSenLen2;
FLMUINT uiSpaceNeeded;
FLMUINT uiEncOutputLen;
FLMUINT uiBytesAvail;
flmAssert( isLoggingEnabled());
if( !haveBuffSpace( RFL_PACKET_OVERHEAD))
{
if( RC_BAD( rc = flush( pDb, m_pCurrentBuf)))
{
goto Exit;
}
}
// Go through each column's data
while (uiNumColumnValues)
{
pColumn = pDb->m_pDict->getColumn( pTable, pColumnValues->uiColumnNum);
if( RC_BAD( rc = makeRoom( pDb, FLM_MAX_SEN_LEN * 2 + 1,
&uiPacketLen, RFL_COLUMN_DATA_PACKET, NULL, NULL)))
{
goto Exit;
}
pucOut = pucStart = getPacketPtr() + uiPacketLen;
// Output the column number.
f_encodeSEN( pColumnValues->uiColumnNum, &pucOut);
// Output the data type.
*pucOut++ = (FLMBYTE)pColumnValues->eColumnDataType;
// Output the value length
f_encodeSEN( pColumnValues->uiValueLen, &pucOut);
uiPacketLen += (FLMUINT)(pucOut - pucStart);
uiBytesLeftToWrite = pColumnValues->uiValueLen;
pucValue = pColumnValues->pucColumnValue;
while (uiBytesLeftToWrite)
{
// If the column or table are encrypted, we need to make enough room
// to encrypt the data.
pEncDef = (pColumn->uiEncDefNum)
? pDb->m_pDict->getEncDef( pColumn->uiEncDefNum)
: pTableEncDef;
if (!pEncDef)
{
if (RC_BAD( rc = makeRoom( pDb, uiBytesLeftToWrite,
&uiPacketLen, RFL_COLUMN_DATA_PACKET, &uiBytesToWrite, NULL)))
{
goto Exit;
}
f_memcpy( getPacketPtr() + uiPacketLen, pucValue, uiBytesToWrite);
pucValue += uiBytesToWrite;
uiBytesLeftToWrite -= uiBytesToWrite;
uiPacketLen += uiBytesToWrite;
}
else
{
uiIVLen = pEncDef->pCcs->getIVLen();
flmAssert( uiIVLen == 8 || uiIVLen == 16);
uiBytesToWrite = uiBytesLeftToWrite;
uiEncLen = getEncLen( uiBytesToWrite);
uiSenLen1 = f_getSENByteCount( uiEncLen);
uiSenLen2 = f_getSENByteCount( uiBytesToWrite);
uiSpaceNeeded = uiSenLen1 + uiSenLen2 + uiIVLen + uiEncLen;
if (RC_BAD( rc = makeRoom( pDb, uiSpaceNeeded,
&uiPacketLen, RFL_COLUMN_DATA_PACKET, &uiBytesAvail, NULL)))
{
goto Exit;
}
// If we can't hold the entire encrypted chunk, we must
// recalculate down.
if (uiBytesAvail < uiSpaceNeeded)
{
// If we cannot hold the encrypted length (as a SEN), the
// IV, and a minimal encrypted chunk, we need to flush
// out the current packet.
if (uiBytesAvail < uiSenLen1 + uiSenLen2 + uiIVLen + ENCRYPT_MIN_CHUNK_SIZE)
{
goto Finish_Packet;
}
// Round down to fit whatever data will end right on an
// encryption boundary.
uiBytesToWrite = uiBytesAvail - uiSenLen1 - uiSenLen2 - uiIVLen;
uiBytesToWrite -= extraEncBytes( uiBytesToWrite);
uiEncLen = getEncLen( uiBytesToWrite);
flmAssert( uiEncLen == uiBytesToWrite);
uiSenLen1 = f_getSENByteCount( uiEncLen);
uiSenLen2 = f_getSENByteCount( uiBytesToWrite);
uiSpaceNeeded = uiSenLen1 + uiSenLen2 + uiIVLen + uiEncLen;
if (RC_BAD( rc = makeRoom( pDb, uiSpaceNeeded,
&uiPacketLen, RFL_COLUMN_DATA_PACKET, &uiBytesAvail, NULL)))
{
goto Exit;
}
// We should be guaranteed enough room to write everything we
// asked for at this point.
flmAssert( uiBytesAvail >= uiSpaceNeeded);
}
// First output the encrypted length, then the IV, and finally
// the data. Then encrypt the data in place.
pucOut = getPacketPtr() + uiPacketLen;
// Output the encrypted length
f_encodeSEN( uiEncLen, &pucOut);
// Output the actual data length
f_encodeSEN( uiBytesToWrite, &pucOut);
// Output the IV
pucIV = pucOut;
if( RC_BAD( rc = pEncDef->pCcs->generateIV( uiIVLen, pucIV)))
{
goto Exit;
}
pucOut += uiIVLen;
// Output the unencrypted data and encrypt it in place.
f_memcpy( pucOut, pucValue, uiBytesToWrite);
if (RC_BAD( rc = pDb->encryptData( pEncDef->uiEncDefNum, pucIV,
pucOut, uiEncLen, uiBytesToWrite, &uiEncOutputLen)))
{
goto Exit;
}
flmAssert( uiEncOutputLen == uiEncLen);
pucValue += uiBytesToWrite;
uiBytesLeftToWrite -= uiBytesToWrite;
uiPacketLen += (uiSenLen1 + uiSenLen2 + uiIVLen + uiEncLen);
}
Finish_Packet:
if( RC_BAD( rc = finishPacket( pDb, RFL_COLUMN_DATA_PACKET,
uiPacketLen - RFL_PACKET_OVERHEAD, FALSE)))
{
goto Exit;
}
uiPacketLen = RFL_PACKET_OVERHEAD;
}
pColumnValues++;
uiNumColumnValues--;
}
// Add a column number of zero to terminate the columns.
if (RC_BAD( rc = makeRoom( pDb, 1, &uiPacketLen,
RFL_COLUMN_DATA_PACKET, NULL, NULL)))
{
goto Exit;
}
pucOut = getPacketPtr() + uiPacketLen;
*pucOut++ = 0;
uiPacketLen++;
// Finish the packet.
if (RC_BAD( rc = finishPacket( pDb, RFL_COLUMN_DATA_PACKET,
uiPacketLen - RFL_PACKET_OVERHEAD, FALSE)))
{
goto Exit;
}
Exit:
return( rc);
}
/********************************************************************
Desc:
*********************************************************************/
RCODE F_Rfl::logInsertRow(
F_Db * pDb,
FLMUINT uiTableNum,
F_COLUMN_VALUE * pColumnValues,
FLMUINT uiNumColumnValues)
{
RCODE rc = NE_SFLM_OK;
FLMUINT uiPacketBodyLen;
FLMBYTE * pucPacketBody;
FLMBYTE * pucPacketStart;
flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK);
// Do nothing if logging is disabled.
if( !isLoggingEnabled())
{
goto Exit;
}
// Better be in the middle of a transaction.
flmAssert( m_ui64CurrTransID);
// Increment the operation count
m_uiOperCount++;
// Make sure we have space in the RFL buffer for a complete packet. NOTE:
// this is calculating the maximum packet body length that would be needed.
if( !haveBuffSpace( FLM_MAX_SEN_LEN * 2 + RFL_PACKET_OVERHEAD))
{
if( RC_BAD( rc = flush( pDb, m_pCurrentBuf)))
{
goto Exit;
}
}
// Get a pointer to where we will be laying down the packet body.
pucPacketBody = pucPacketStart = getPacketBodyPtr();
// Output the table number
f_encodeSEN( uiTableNum, &pucPacketBody);
// Output the number of column values
f_encodeSEN( uiNumColumnValues, &pucPacketBody);
// Finish the packet - calculate the actual packet body length.
uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart);
flmAssert( uiPacketBodyLen <= FLM_MAX_SEN_LEN * 2);
if (RC_BAD( rc = finishPacket( pDb, RFL_INSERT_ROW_PACKET,
uiPacketBodyLen, FALSE)))
{
goto Exit;
}
// Now output the column data
if (RC_BAD( rc = logColumnValues( pDb, uiTableNum,
pColumnValues, uiNumColumnValues)))
{
goto Exit;
}
Exit:
return( rc);
}
/********************************************************************
Desc:
*********************************************************************/
RCODE F_Rfl::recovInsertRow(
F_Db * pDb,
const FLMBYTE * pucPacketBody,
FLMUINT uiPacketBodyLen,
eRestoreAction * peAction)
{
RCODE rc = NE_SFLM_OK;
FLMUINT uiTableNum;
FLMUINT uiNumColumnValues;
FLMUINT uiPacketType;
F_COLUMN_VALUE * pColValues;
F_COLUMN_VALUE * pColumnValue;
const FLMBYTE * pucEnd = pucPacketBody + uiPacketBodyLen;
FLMUINT uiIVLen;
FLMBOOL bHitEnd;
FLMUINT uiColumnNum;
F_TABLE * pTable;
F_COLUMN * pColumn;
F_ENCDEF * pTableEncDef;
F_ENCDEF * pEncDef;
FLMBYTE * pucValue;
FLMUINT uiBytesLeftToRead;
FLMUINT uiBytesToRead;
FLMUINT uiEncLen;
FLMBYTE ucIV [16];
// Get the table number from the packet
if (RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiTableNum)))
{
goto Exit;
}
if ((pTable = pDb->m_pDict->getTable( uiTableNum)) == NULL ||
pTable->bSystemTable)
{
rc = RC_SET( NE_SFLM_BAD_RFL_PACKET);
goto Exit;
}
pTableEncDef = (pTable->lfInfo.uiEncDefNum)
? pDb->m_pDict->getEncDef( pTable->lfInfo.uiEncDefNum)
: (F_ENCDEF *)NULL;
// Get the number of column values from the packet.
if (RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiNumColumnValues)))
{
goto Exit;
}
// Get the column values.
if (uiNumColumnValues)
{
// Allocate space for the values.
m_tmpPool.poolReset( NULL);
if (RC_BAD( rc = m_tmpPool.poolAlloc(
sizeof( F_COLUMN_VALUE) * uiNumColumnValues,
(void **)&pColValues)))
{
goto Exit;
}
// Go into a loop processing packets until we have retrieved all of
// the columns. At that point, we had better be at the end
// of the column data.
pColumnValue = pColValues;
pucPacketBody = pucEnd = NULL;
for (;;)
{
// If we have used everything in our current packet, get another.
if (pucPacketBody == pucEnd)
{
if( RC_BAD( rc = getPacket(
pDb, FALSE, &uiPacketType, &pucPacketBody, &uiPacketBodyLen,
&bHitEnd)))
{
goto Exit;
}
// Should not hit the end of the RFL here!
if (bHitEnd)
{
rc = RC_SET( NE_SFLM_RFL_INCOMPLETE);
goto Exit;
}
if (uiPacketType != RFL_COLUMN_DATA_PACKET)
{
rc = RC_SET( NE_SFLM_BAD_RFL_PACKET);
goto Exit;
}
pucEnd = pucPacketBody + uiPacketBodyLen;
}
// Get the column number.
if (RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd, &uiColumnNum)))
{
goto Exit;
}
// A zero column number means we are at the end
if (!uiColumnNum)
{
// Shouldn't have hit zero if we still have column values to
// process.
if (uiNumColumnValues)
{
rc = RC_SET( NE_SFLM_BAD_RFL_PACKET);
goto Exit;
}
break;
}
if ((pColumn = pDb->m_pDict->getColumn( pTable, uiColumnNum)) == NULL)
{
rc = RC_SET( NE_SFLM_BAD_RFL_PACKET);
goto Exit;
}
pColumnValue->uiColumnNum = uiColumnNum;
// Get the data type
if (pucPacketBody >= pucEnd)
{
rc = RC_SET( NE_SFLM_BAD_RFL_PACKET);
goto Exit;
}
pColumnValue->eColumnDataType = (eDataType)(*pucPacketBody);
switch (pColumnValue->eColumnDataType)
{
case SFLM_STRING_TYPE:
case SFLM_NUMBER_TYPE:
case SFLM_BINARY_TYPE:
break;
default:
rc = RC_SET( NE_SFLM_BAD_RFL_PACKET);
goto Exit;
}
pucPacketBody++;
// Get the value length.
if (RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd,
&uiBytesLeftToRead)))
{
goto Exit;
}
// Get the value, if there is one.
if ((pColumnValue->uiValueLen = uiBytesLeftToRead) != 0)
{
pEncDef = (pColumn->uiEncDefNum)
? pDb->m_pDict->getEncDef( pColumn->uiEncDefNum)
: pTableEncDef;
// If data is encrypted, allocate enough so that we can
// decrypt in place - rounding up to nearest encryption boundary
// should ensure that.
if (!pEncDef)
{
if (RC_BAD( rc = m_tmpPool.poolAlloc( uiBytesLeftToRead,
(void **)&pucValue)))
{
goto Exit;
}
}
else
{
if (RC_BAD( rc = m_tmpPool.poolAlloc( getEncLen( uiBytesLeftToRead),
(void **)&pucValue)))
{
goto Exit;
}
}
pColumnValue->pucColumnValue = pucValue;
// Now get the value.
while (uiBytesLeftToRead)
{
// If we have used up our current packet, get another.
if (pucPacketBody == pucEnd)
{
if( RC_BAD( rc = getPacket(
pDb, FALSE, &uiPacketType, &pucPacketBody, &uiPacketBodyLen,
&bHitEnd)))
{
goto Exit;
}
// Should not hit the end of the RFL here!
if (bHitEnd)
{
rc = RC_SET( NE_SFLM_RFL_INCOMPLETE);
goto Exit;
}
if (uiPacketType != RFL_COLUMN_DATA_PACKET)
{
rc = RC_SET( NE_SFLM_BAD_RFL_PACKET);
goto Exit;
}
pucEnd = pucPacketBody + uiPacketBodyLen;
}
// Getting data is simple if it is not encrypted.
if (!pEncDef)
{
uiBytesToRead = (FLMUINT)(pucEnd - pucPacketBody);
if (uiBytesToRead > uiBytesLeftToRead)
{
uiBytesToRead = uiBytesLeftToRead;
}
f_memcpy( pucValue, pucPacketBody, uiBytesToRead);
pucPacketBody += uiBytesToRead;
pucValue += uiBytesToRead;
uiBytesLeftToRead -= uiBytesToRead;
}
else
{
// Encrypted data should have the encrypted length, the
// length of data encrypted, an IV, and the encrypted
// data - all in the same packet.
if (RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd,
&uiEncLen)))
{
goto Exit;
}
if (RC_BAD( rc = f_decodeSEN( &pucPacketBody, pucEnd,
&uiBytesToRead)))
{
goto Exit;
}
if (uiBytesToRead > uiEncLen || uiBytesToRead > uiBytesLeftToRead)
{
rc = RC_SET( NE_SFLM_BAD_RFL_PACKET);
goto Exit;
}
uiIVLen = pEncDef->pCcs->getIVLen();
flmAssert( uiIVLen == 8 || uiIVLen == 16);
// Make sure packet has the IV and the encrypted data.
if (pucPacketBody + uiIVLen + uiEncLen > pucEnd)
{
rc = RC_SET( NE_SFLM_BAD_RFL_PACKET);
goto Exit;
}
// Get the IV from the packet.
f_memcpy( ucIV, pucPacketBody, uiIVLen);
pucPacketBody += uiIVLen;
// Get the encrypted data into our buffer.
f_memcpy( pucValue, pucPacketBody, uiEncLen);
// Decrypt the data in place.
if (RC_BAD( rc = pDb->decryptData( pEncDef->uiEncDefNum, ucIV,
pucValue, uiEncLen, pucValue, uiEncLen)))
{
goto Exit;
}
// Increment our value buffer only past the decrypted data.
// Increment the packet body pointer past the encrypted data.
pucValue += uiBytesToRead;
uiBytesLeftToRead -= uiBytesToRead;
pucPacketBody += uiEncLen;
}
}
}
uiNumColumnValues--;
pColumnValue++;
}
}
if (m_pRestoreStatus)
{
if( RC_BAD( rc = m_pRestoreStatus->reportInsertRow(
peAction, uiTableNum, pColValues, uiNumColumnValues)))
{
goto Exit;
}
if( *peAction == SFLM_RESTORE_ACTION_STOP)
{
m_ui64CurrTransID = 0;
goto Exit;
}
}
if( RC_BAD( rc = pDb->insertRow( uiTableNum, pColValues, uiNumColumnValues)))
{
goto Exit;
}
Exit:
m_tmpPool.poolReset( NULL);
m_ui64CurrTransID = 0;
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(
F_Db * pDb,
FLMUINT uiAdditionalBytesNeeded,
FLMUINT * puiCurrPacketLenRV,
FLMUINT uiPacketType,
FLMUINT * puiBytesAvailableRV,
FLMUINT * puiPacketCountRV)
{
RCODE rc = NE_SFLM_OK;
FLMUINT uiBytesNeeded;
uiBytesNeeded = *puiCurrPacketLenRV + uiAdditionalBytesNeeded;
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( pDb, *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( pDb, uiPacketType,
*puiCurrPacketLenRV - RFL_PACKET_OVERHEAD,
FALSE)))
{
goto Exit;
}
if( RC_BAD( rc = flush( pDb, 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( pDb, *puiCurrPacketLenRV, FALSE)))
{
goto Exit;
}
}
}
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 = NE_SFLM_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_pCurrentBuf->uiRflBufBytes >= m_uiRflReadOffset &&
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 &&
m_pCurrentBuf->uiRflBufBytes >= m_uiRflReadOffset)
{
// Move the bytes left in the buffer down to the beginning
// of the buffer.
f_memmove( m_pCurrentBuf->pIOBuffer->getBuffer(),
&(m_pCurrentBuf->pIOBuffer->getBuffer()[ 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( NE_SFLM_BAD_RFL_PACKET);
goto Exit;
}
// Read enough to get the entire packet.
if( RC_BAD( rc = m_pRestore->read( uiReadLen,
&(m_pCurrentBuf->pIOBuffer->getBuffer()[
m_pCurrentBuf->uiRflBufBytes]), &uiBytesRead)))
{
if( rc == NE_FLM_IO_END_OF_FILE)
{
rc = NE_SFLM_OK;
}
else
{
goto Exit;
}
}
if( m_pRestoreStatus)
{
eRestoreAction eAction;
if( RC_BAD( rc = m_pRestoreStatus->reportRflRead(
&eAction, m_pCurrentBuf->uiCurrFileNum, uiBytesRead)))
{
goto Exit;
}
if( eAction == SFLM_RESTORE_ACTION_STOP)
{
rc = RC_SET( NE_SFLM_USER_ABORT);
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( NE_SFLM_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( NE_SFLM_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->getBuffer(),
&uiBytesRead)))
{
if( rc == NE_FLM_IO_END_OF_FILE)
{
rc = NE_SFLM_OK;
}
else
{
m_bRflVolumeOk = FALSE;
goto Exit;
}
}
if( uiBytesRead < uiReadLen)
{
rc = RC_SET( NE_SFLM_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(
F_Db * pDb,
FLMBOOL bForceNextFile,
FLMUINT * puiPacketTypeRV,
const FLMBYTE ** ppucPacketBodyRV,
FLMUINT * puiPacketBodyLenRV,
FLMBOOL * pbHitEnd)
{
RCODE rc = NE_SFLM_OK;
FLMBYTE * pucPacket;
const FLMBYTE * pucPacketBody;
const FLMBYTE * pucPacketBodyEnd;
FLMUINT uiOrigPacketBodyLen;
FLMUINT uiPacketBodyLen;
FLMUINT uiPacketType;
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)
{
*pbHitEnd = TRUE;
goto Exit;
}
else if( (m_pCurrentBuf->uiCurrFileNum + 1 ) ==
m_uiLastRecoverFileNum &&
!m_pDatabase->m_lastCommittedDbHdr.ui32RflLastTransOffset)
{
// 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;
*pbHitEnd = TRUE;
goto Exit;
}
// Open the next file in the sequence.
if( RC_BAD( rc = openFile( pDb->m_hWaitSem,
m_pCurrentBuf->uiCurrFileNum + 1, m_ucNextSerialNum)))
{
if( rc == NE_FLM_IO_PATH_NOT_FOUND)
{
rc = NE_SFLM_OK;
*pbHitEnd = TRUE;
}
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 =
m_pDatabase->m_lastCommittedDbHdr.ui32RflLastTransOffset;
// 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 == NE_FLM_IO_PATH_NOT_FOUND)
{
rc = NE_SFLM_OK;
*pbHitEnd = TRUE;
}
goto Exit;
}
if( m_pRestoreStatus)
{
eRestoreAction eAction;
if( RC_BAD( rc = m_pRestoreStatus->reportOpenRflFile(
&eAction, m_pCurrentBuf->uiCurrFileNum + 1)))
{
goto Exit;
}
if( eAction == SFLM_RESTORE_ACTION_STOP)
{
rc = RC_SET( NE_SFLM_USER_ABORT);
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( m_pRestoreStatus)
{
eRestoreAction eAction;
if( RC_BAD( rc = m_pRestoreStatus->reportRflRead(
&eAction, m_pCurrentBuf->uiCurrFileNum + 1, uiBytesRead)))
{
goto Exit;
}
if( eAction == SFLM_RESTORE_ACTION_STOP)
{
rc = RC_SET( NE_SFLM_USER_ABORT);
goto Exit;
}
}
if( uiBytesRead < 512)
{
rc = RC_SET( NE_SFLM_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);
*pbHitEnd = TRUE;
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->getBuffer()[m_uiRflReadOffset]);
if ((FLMUINT)FB2UD( &pucPacket [RFL_PACKET_ADDRESS_OFFSET]) !=
m_uiPacketAddress)
{
rc = RC_SET( NE_SFLM_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]);
uiPacketBodyLen = uiOrigPacketBodyLen =
(FLMUINT)FB2UW( &pucPacket [RFL_PACKET_BODY_LENGTH_OFFSET]);
// Make sure we have the entire packet in the buffer.
if (RC_BAD( rc = readPacket( uiPacketBodyLen + RFL_PACKET_OVERHEAD)))
{
goto Exit;
}
pucPacket = &(m_pCurrentBuf->pIOBuffer->getBuffer()[m_uiRflReadOffset]);
// At this point, we are guaranteed to have the entire packet
// in the buffer.
pucPacketBody = &pucPacket [RFL_PACKET_OVERHEAD];
pucPacketBodyEnd = pucPacketBody + uiPacketBodyLen;
// Validate the packet checksum
if (RflCalcChecksum( pucPacket, uiPacketBodyLen) !=
pucPacket [RFL_PACKET_CHECKSUM_OFFSET])
{
rc = RC_SET( NE_SFLM_BAD_RFL_PACKET);
goto Exit;
}
if (uiPacketType == RFL_TRNS_BEGIN_PACKET ||
uiPacketType == RFL_UPGRADE_PACKET ||
uiPacketType == RFL_REDUCE_PACKET ||
uiPacketType == RFL_WRAP_KEY_PACKET ||
uiPacketType == RFL_ENABLE_ENCRYPTION_PACKET ||
uiPacketType == RFL_ROLL_OVER_DB_KEY_PACKET)
{
// Current transaction ID better be zero, otherwise, we
// have two or more begin packets in a row.
if( m_ui64CurrTransID)
{
rc = RC_SET( NE_SFLM_BAD_RFL_PACKET);
goto Exit;
}
if( RC_BAD( rc = f_decodeSEN64( &pucPacketBody,
pucPacketBodyEnd, &m_ui64CurrTransID)))
{
goto Exit;
}
uiPacketBodyLen = (FLMUINT)(pucPacketBodyEnd - pucPacketBody);
// Make sure the transaction numbers are ascending
if( m_ui64CurrTransID <= m_ui64LastTransID)
{
rc = RC_SET( NE_SFLM_BAD_RFL_PACKET);
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_ui64CurrTransID)
{
rc = RC_SET( NE_SFLM_BAD_RFL_PACKET);
goto Exit;
}
if( uiPacketType == RFL_TRNS_COMMIT_PACKET ||
uiPacketType == RFL_TRNS_ABORT_PACKET)
{
FLMUINT64 ui64Tmp;
if( RC_BAD( rc = f_decodeSEN64( &pucPacketBody,
pucPacketBodyEnd, &ui64Tmp)))
{
goto Exit;
}
if( ui64Tmp != m_ui64CurrTransID)
{
rc = RC_SET( NE_SFLM_BAD_RFL_PACKET);
goto Exit;
}
uiPacketBodyLen = (FLMUINT)(pucPacketBodyEnd - pucPacketBody);
}
}
// Set read offset to beginning of next packet.
m_uiRflReadOffset += (RFL_PACKET_OVERHEAD + uiOrigPacketBodyLen);
*puiPacketBodyLenRV = uiPacketBodyLen;
*ppucPacketBodyRV = pucPacketBody;
Exit:
return( rc);
}
/********************************************************************
Desc: Restore transactions from the roll-forward log to the
database.
*********************************************************************/
RCODE F_Rfl::recover(
F_Db * pDb,
IF_RestoreClient * pRestore,
IF_RestoreStatus * pRestoreStatus)
{
RCODE rc = NE_SFLM_OK;
FLMUINT uiStartFileNum;
FLMUINT uiStartOffset;
FLMUINT uiOffset;
FLMUINT uiReadLen;
FLMUINT uiBytesRead;
FLMBYTE ucHdr[ 512];
FLMUINT uiPacketType;
const FLMBYTE * pucPacketBody;
FLMUINT uiPacketBodyLen = 0;
eRestoreAction eAction;
FLMBOOL bTransActive = FALSE;
FLMBOOL bHadOperations = FALSE;
FLMBOOL bLastTransEndedAtFileEOF = FALSE;
FLMBOOL bForceNextFile;
FLMBOOL bHitEnd;
FLMUINT uiRflToken = 0;
flmAssert( m_pDatabase);
m_pCurrentBuf = &m_Buf1;
m_ui64LastLoggedCommitTransID = 0;
// We need to allow all updates logged in the RFL
// (including dictionary updates).
pDb->m_bItemStateUpdOk = TRUE;
// Turn off logging.
disableLogging( &uiRflToken);
// Set the replay flag on the database.
pDb->m_uiFlags |= FDB_REPLAYING_RFL;
// Set the flag as to whether or not we are using multiple RFL files.
m_bKeepRflFiles = m_pDatabase->m_lastCommittedDbHdr.ui8RflKeepFiles
? 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.
m_pRestoreStatus = pRestoreStatus;
if( (m_pRestore = pRestore) == NULL)
{
FLMBYTE * pucCheckSerialNum;
FLMUINT uiEndOffset;
// Clear the restore status pointer. Assert that the
// pointer is NULL so we can catch the improper use
// of this object.
if( m_pRestoreStatus)
{
flmAssert( 0);
m_pRestoreStatus = NULL;
}
uiStartFileNum =
(FLMUINT)m_pDatabase->m_lastCommittedDbHdr.ui32RflLastCPFileNum;
m_uiLastRecoverFileNum =
(FLMUINT)m_pDatabase->m_lastCommittedDbHdr.ui32RflCurrFileNum;
uiStartOffset =
(FLMUINT)m_pDatabase->m_lastCommittedDbHdr.ui32RflLastCPOffset;
uiEndOffset =
(FLMUINT)m_pDatabase->m_lastCommittedDbHdr.ui32RflLastTransOffset;
// 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_pDatabase->m_lastCommittedDbHdr.ucLastTransRflSerialNum [0]
: NULL;
if( RC_BAD( rc = openFile( pDb->m_hWaitSem,
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.
rc = RC_SET_AND_ASSERT( NE_SFLM_CANNOT_RESTORE_RFL_FILES);
goto Exit;
}
else
{
uiStartFileNum =
(FLMUINT)m_pDatabase->m_lastCommittedDbHdr.ui32RflCurrFileNum;
uiStartOffset =
(FLMUINT)m_pDatabase->m_lastCommittedDbHdr.ui32RflLastTransOffset;
// 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 == NE_FLM_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 = NE_SFLM_OK;
goto Finish_Recovery;
}
else
{
goto Exit;
}
}
if( m_pRestoreStatus)
{
if( RC_BAD( rc = m_pRestoreStatus->reportOpenRflFile(
&eAction, uiStartFileNum)))
{
goto Exit;
}
if( eAction == SFLM_RESTORE_ACTION_STOP)
{
rc = RC_SET( NE_SFLM_USER_ABORT);
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( m_pRestoreStatus)
{
if( RC_BAD( rc = m_pRestoreStatus->reportRflRead(
&eAction, uiStartFileNum, uiBytesRead)))
{
goto Exit;
}
if( eAction == SFLM_RESTORE_ACTION_STOP)
{
rc = RC_SET( NE_SFLM_USER_ABORT);
goto Exit;
}
}
if( uiBytesRead < 512)
{
rc = RC_SET( NE_SFLM_NOT_RFL);
goto Exit;
}
if( RC_BAD( rc = verifyHeader( ucHdr, uiStartFileNum,
m_pDatabase->m_lastCommittedDbHdr.ucLastTransRflSerialNum)))
{
RCODE tmpRc;
if( m_pRestoreStatus)
{
if( RC_BAD( tmpRc = m_pRestoreStatus->reportError( &eAction, rc)))
{
rc = tmpRc;
goto Exit;
}
if( eAction == SFLM_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->getBuffer(), &uiBytesRead)))
{
goto Exit;
}
if( m_pRestoreStatus)
{
if( RC_BAD( rc = m_pRestoreStatus->reportRflRead(
&eAction, uiStartFileNum, uiBytesRead)))
{
goto Exit;
}
if( eAction == SFLM_RESTORE_ACTION_STOP)
{
rc = RC_SET( NE_SFLM_USER_ABORT);
goto Exit;
}
}
// RFL file is incomplete if we could not read up to the last
// committed transaction.
if( uiBytesRead < uiReadLen)
{
rc = RC_SET( NE_SFLM_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(
m_pDatabase->m_lastCommittedDbHdr.ui64RflLastCPTransID ==
m_pDatabase->m_lastCommittedDbHdr.ui64CurrTransID);
// Use uiStartOffset here instead of ui32RflLastTransOffset,
// because ui32RflLastTransOffset 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(
(FLMUINT)m_pDatabase->m_lastCommittedDbHdr.ui32RflLastCPOffset ==
uiStartOffset);
flmAssert(
(FLMUINT)m_pDatabase->m_lastCommittedDbHdr.ui32RflLastCPFileNum ==
uiStartFileNum);
}
// Set last transaction ID to the last transaction
// that was checkpointed - transaction numbers should ascend
// from here.
m_ui64LastTransID =
m_pDatabase->m_lastCommittedDbHdr.ui64RflLastCPTransID;
// Set the last committed trans ID
m_ui64LastLoggedCommitTransID =
m_pDatabase->m_lastCommittedDbHdr.ui64LastRflCommitID;
m_pCurrentBuf->uiRflFileOffset = uiStartOffset;
m_uiRflReadOffset = 0;
m_pCurrentBuf->uiRflBufBytes = 0;
// Now, read until we are done.
bForceNextFile = FALSE;
bHitEnd = FALSE;
for (;;)
{
flmAssert( pDb->m_uiFlags & FDB_REPLAYING_RFL);
// Get the next operation from the file.
rc = getPacket( pDb, bForceNextFile, &uiPacketType, &pucPacketBody,
&uiPacketBodyLen, &bHitEnd);
bForceNextFile = FALSE;
if( RC_BAD( rc))
{
if( rc == NE_SFLM_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)
{
pDb->transAbort();
bTransActive = FALSE;
}
// Set current transaction ID to zero - as if we had encountered
// an abort packet.
m_ui64CurrTransID = 0;
bLastTransEndedAtFileEOF = TRUE;
// Force to go to the next file
bForceNextFile = TRUE;
rc = NE_SFLM_OK;
continue;
}
}
goto Exit;
}
if (bHitEnd)
{
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( NE_SFLM_RFL_INCOMPLETE);
}
else
{
rc = NE_SFLM_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 = NE_SFLM_OK;
goto Finish_Recovery;
}
goto Exit;
}
// At this point, we know we have a good packet, see what it
// is and handle it.
bHadOperations = TRUE;
eAction = SFLM_RESTORE_ACTION_CONTINUE;
switch (uiPacketType)
{
case RFL_TRNS_BEGIN_PACKET:
{
// If we already have a transaction active, we have
// a problem.
flmAssert( !bTransActive);
if( uiPacketBodyLen)
{
rc = RC_SET_AND_ASSERT( NE_SFLM_BAD_RFL_PACKET);
goto Exit;
}
if( RC_BAD( rc = recovTransBegin( pDb, &eAction)))
{
goto Exit;
}
if (eAction == SFLM_RESTORE_ACTION_STOP)
{
bLastTransEndedAtFileEOF = FALSE;
goto Finish_Recovery;
}
bTransActive = TRUE;
break;
}
case RFL_TRNS_COMMIT_PACKET:
{
// Commit the current transaction.
if (m_pRestoreStatus)
{
if (RC_BAD( rc = m_pRestoreStatus->reportCommitTrans(
&eAction, m_ui64CurrTransID)))
{
goto Exit;
}
if (eAction == SFLM_RESTORE_ACTION_STOP)
{
bLastTransEndedAtFileEOF = FALSE;
goto Finish_Recovery;
}
}
flmAssert( bTransActive);
pDb->m_uiFlags |= FDB_REPLAYING_COMMIT;
rc = pDb->transCommit();
pDb->m_uiFlags &= ~FDB_REPLAYING_COMMIT;
bTransActive = FALSE;
if (RC_BAD( rc))
{
goto Exit;
}
m_ui64LastLoggedCommitTransID = m_ui64CurrTransID;
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_ui64CurrTransID = 0;
break;
}
case RFL_TRNS_ABORT_PACKET:
{
// Abort the current transaction.
if (m_pRestoreStatus)
{
if (RC_BAD( rc = m_pRestoreStatus->reportAbortTrans(
&eAction, m_ui64CurrTransID)))
{
goto Exit;
}
if (eAction == SFLM_RESTORE_ACTION_STOP)
{
bLastTransEndedAtFileEOF = FALSE;
goto Finish_Recovery;
}
}
flmAssert( bTransActive);
m_uiLastLfNum = 0;
m_eLastLfType = SFLM_LF_INVALID;
rc = pDb->transAbort();
bTransActive = FALSE;
if (RC_BAD( rc))
{
goto Exit;
}
goto Finish_Transaction;
}
case RFL_REDUCE_PACKET:
{
if( RC_BAD( rc = recovReduce( pDb, pucPacketBody,
uiPacketBodyLen, &eAction)))
{
goto Exit;
}
if( eAction == SFLM_RESTORE_ACTION_STOP)
{
bLastTransEndedAtFileEOF = FALSE;
goto Finish_Recovery;
}
goto Finish_Transaction;
}
case RFL_UPGRADE_PACKET:
{
if( RC_BAD( rc = recovUpgrade( pDb,
pucPacketBody, uiPacketBodyLen, &eAction)))
{
goto Exit;
}
if( eAction == SFLM_RESTORE_ACTION_STOP)
{
bLastTransEndedAtFileEOF = FALSE;
goto Finish_Recovery;
}
goto Finish_Transaction;
}
case RFL_INDEX_SUSPEND_PACKET:
case RFL_INDEX_RESUME_PACKET:
{
if( RC_BAD( rc = recovIndexSuspendResume( pDb,
uiPacketType, pucPacketBody, uiPacketBodyLen, &eAction)))
{
goto Exit;
}
if( eAction == SFLM_RESTORE_ACTION_STOP)
{
bLastTransEndedAtFileEOF = FALSE;
goto Finish_Recovery;
}
break;
}
case RFL_BLK_CHAIN_FREE_PACKET:
{
if( RC_BAD( rc = recovBlockChainFree( pDb,
pucPacketBody, uiPacketBodyLen, &eAction)))
{
goto Exit;
}
if( eAction == SFLM_RESTORE_ACTION_STOP)
{
bLastTransEndedAtFileEOF = FALSE;
goto Finish_Recovery;
}
break;
}
case RFL_ENABLE_ENCRYPTION_PACKET:
case RFL_WRAP_KEY_PACKET:
{
if( RC_BAD( rc = recovEncryptionKey( pDb,
uiPacketType, pucPacketBody, uiPacketBodyLen, &eAction)))
{
goto Exit;
}
if( eAction == SFLM_RESTORE_ACTION_STOP)
{
bLastTransEndedAtFileEOF = FALSE;
goto Finish_Recovery;
}
goto Finish_Transaction;
}
case RFL_ENC_DEF_KEY_PACKET:
{
if( RC_BAD( rc = recovEncDefKey( pDb,
pucPacketBody, uiPacketBodyLen, &eAction)))
{
goto Exit;
}
if( eAction == SFLM_RESTORE_ACTION_STOP)
{
bLastTransEndedAtFileEOF = FALSE;
goto Finish_Recovery;
}
break;
}
case RFL_ROLL_OVER_DB_KEY_PACKET:
{
if( RC_BAD( rc = recovRollOverDbKey( pDb,
pucPacketBody, uiPacketBodyLen, &eAction)))
{
goto Exit;
}
if( eAction == SFLM_RESTORE_ACTION_STOP)
{
bLastTransEndedAtFileEOF = FALSE;
goto Finish_Recovery;
}
goto Finish_Transaction;
}
case RFL_INSERT_ROW_PACKET:
{
if( RC_BAD( rc = recovInsertRow( pDb,
pucPacketBody, uiPacketBodyLen, &eAction)))
{
goto Exit;
}
if( eAction == SFLM_RESTORE_ACTION_STOP)
{
bLastTransEndedAtFileEOF = FALSE;
goto Finish_Recovery;
}
break;
}
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 = NE_SFLM_OK;
goto Finish_Recovery;
}
else
{
rc = RC_SET_AND_ASSERT( NE_SFLM_BAD_RFL_PACKET);
}
goto Exit;
}
}
}
Finish_Recovery:
if( bTransActive)
{
pDb->transAbort();
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.
m_pDatabase->m_lastCommittedDbHdr.ui32RflCurrFileNum =
(FLMINT32)uiNextRflFileNum;
// 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.
m_pDatabase->m_lastCommittedDbHdr.ui32RflLastTransOffset = 0;
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_pDatabase->m_lastCommittedDbHdr.ucLastTransRflSerialNum,
m_ucNextSerialNum, SFLM_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_pDatabase->m_lastCommittedDbHdr.ucLastTransRflSerialNum)))
{
goto Exit;
}
}
// Save the last logged commit transaction ID.
if( m_ui64LastLoggedCommitTransID)
{
m_pDatabase->m_lastCommittedDbHdr.ui64LastRflCommitID =
m_ui64LastLoggedCommitTransID;
}
// 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_pDatabase->m_lastCommittedDbHdr.ucNextRflSerialNum)))
{
goto Exit;
}
}
if( !bHadOperations)
{
// No transactions were recovered, but still need to
// setup a few things.
m_pDatabase->m_uiFirstLogCPBlkAddress = 0;
m_pDatabase->m_uiLastCheckpointTime = (FLMUINT)FLM_GET_TIMER();
// Save the state of the log header into the checkpointDbHdr buffer.
f_memcpy( &m_pDatabase->m_checkpointDbHdr,
&m_pDatabase->m_lastCommittedDbHdr,
sizeof( SFLM_DB_HDR));
}
// 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_pRestoreStatus = NULL;
enableLogging( &uiRflToken);
pDb->m_uiFlags &= ~FDB_REPLAYING_RFL;
if( RC_BAD( rc = pDb->doCheckpoint( 0)))
{
goto Exit;
}
Exit:
if( bTransActive)
{
pDb->transAbort();
}
pDb->m_bItemStateUpdOk = FALSE;
pDb->m_uiFlags &= ~FDB_REPLAYING_RFL;
m_uiLastLfNum = 0;
return( rc);
}
/****************************************************************************
Desc: Returns the name of an RFL file given its number
****************************************************************************/
void F_Db::getRflFileName(
FLMUINT uiFileNum,
FLMBOOL bBaseOnly,
char * pszFileName,
FLMUINT * puiFileNameBufSize,
FLMBOOL * pbNameTruncated)
{
if (bBaseOnly)
{
rflGetBaseFileName( uiFileNum, pszFileName, puiFileNameBufSize,
pbNameTruncated);
}
else
{
m_pDatabase->m_pRfl->getFullRflFileName( uiFileNum,
pszFileName, puiFileNameBufSize,
pbNameTruncated);
}
}