From f8a9a13a8b5f0f0436773662bf451850c7c85f8c Mon Sep 17 00:00:00 2001 From: dsandersoremutah Date: Wed, 5 Apr 2006 19:05:30 +0000 Subject: [PATCH] Set eof-style property to Unix (LF) git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@295 0109f412-320b-0410-ab79-c3e0c5ffbbe6 --- flaim/src/imonbase.cpp | 5504 ++++++------- flaim/src/rcache.cpp | 6148 +++++++------- flaim/src/rfl.cpp | 16864 +++++++++++++++++++-------------------- 3 files changed, 14258 insertions(+), 14258 deletions(-) diff --git a/flaim/src/imonbase.cpp b/flaim/src/imonbase.cpp index e445134..1a3f24e 100644 --- a/flaim/src/imonbase.cpp +++ b/flaim/src/imonbase.cpp @@ -1,2752 +1,2752 @@ -//------------------------------------------------------------------------- -// Desc: Base class for monitoring code. -// Tabs: 3 -// -// Copyright (c) 2001-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: imonbase.cpp 12334 2006-01-23 12:45:35 -0700 (Mon, 23 Jan 2006) dsanders $ -//------------------------------------------------------------------------- - -#include "flaimsys.h" - -#define RESP_WRITE_BUF_SIZE 1024 -#define FLM_SESSION_ID_NAME "flmsessionid" - -#define MAX_FIELD_SIZE(uiSize) \ - (uiSize > 100 ? 100 : (uiSize < 20 ? 20 : uiSize)) - -/**************************************************************************** - Desc: Outputs a javascript function that, when called, causes a new - window to open and a page to be displayed in it. -****************************************************************************/ -void F_WebPage::popupFrame( void) -{ - fnPrintf( m_pHRequest, "\n"); -} - -/****************************************************************************** -Desc: This method will extract the value of the parameter in the form of - PARAMETER=VALUE -*******************************************************************************/ -RCODE F_WebPage::ExtractParameter( - FLMUINT uiNumParams, - const char ** ppszParams, - const char * pszParamName, - FLMUINT uiParamLen, - char* pszParamValue) -{ - RCODE rc = FERR_OK; - FLMUINT uiLoop; - FLMUINT uiParamNameLen; - const char* pszTemp; - FLMBOOL bFound = FALSE; - - uiParamNameLen = f_strlen( pszParamName); - for (uiLoop = 0; uiLoop < uiNumParams; uiLoop++) - { - if( f_strncmp( (char*) ppszParams[uiLoop], pszParamName, uiParamNameLen) == 0 && - (ppszParams[uiLoop][uiParamNameLen] == '\0' || - ppszParams[uiLoop][uiParamNameLen] == '=')) - { - pszTemp = &ppszParams[uiLoop][uiParamNameLen]; - if (*pszTemp == '=') - { - // Skip past the equal sign - - pszTemp++; - - f_strncpy( pszParamValue, pszTemp, uiParamLen - 1); - - // See if the param was too long to store - - if (f_strlen( pszTemp) >= uiParamLen) - { - pszParamValue[uiParamLen] = '\0'; - rc = RC_SET( FERR_MEM); - } - } - else - { - *pszParamValue = 0; - } - - bFound = TRUE; - break; - } - } - - return (bFound ? rc : RC_SET( FERR_NOT_FOUND)); -} - -/**************************************************************************** -Desc: This method will detect the presence of a parameter in the form - PARAMETER - no value will be checked. A TRUE or FALSE value will - be returned. -*****************************************************************************/ -FLMBOOL F_WebPage::DetectParameter( - FLMUINT uiNumParams, - const char ** ppszParams, - const char * pszParamName) -{ - for (FLMUINT uiLoop = 0; uiLoop < uiNumParams; uiLoop++) - { - if (f_strncmp( (char*) ppszParams[uiLoop], pszParamName, - f_strlen( (char*) pszParamName)) == 0) - { - return (TRUE); - } - } - - return (FALSE); -} - -/**************************************************************************** -Desc: -*****************************************************************************/ -RCODE F_WebPage::getDatabaseHandleParam( - FLMUINT uiNumParams, - const char ** ppszParams, - F_Session * pFlmSession, - HFDB* phDb, - char* pszKey) -{ - RCODE rc = FERR_OK; - HFDB hDb; - char szTmp[ 64]; - char * pTmp; - - if (phDb) - { - *phDb = hDb = HFDB_NULL; - } - - if (pszKey) - { - *pszKey = 0; - } - - // Need to memset the first F_SESSION_DB_KEY_LEN bytes of szTmp - // because the hash lookup algorithm expects the buffer to be padded - // with zeros at the end of the key. - - f_memset( szTmp, 0, F_SESSION_DB_KEY_LEN); - - if (RC_BAD( ExtractParameter( uiNumParams, ppszParams, "dbhandle", - sizeof(szTmp), szTmp))) - { - pTmp = &szTmp[0]; - if (RC_BAD( getFormValueByName( "dbhandle", &pTmp, sizeof(szTmp), NULL))) - { - rc = RC_SET( FERR_NOT_FOUND); - goto Exit; - } - } - - if (szTmp[0]) - { - if (RC_BAD( rc = pFlmSession->getDbHandle( szTmp, &hDb))) - { - goto Exit; - } - - if (pszKey) - { - f_memcpy( pszKey, szTmp, F_SESSION_DB_KEY_LEN); - } - } - - if (phDb) - { - *phDb = hDb; - } - -Exit: - - return (rc); -} - -/**************************************************************************** -Desc: Function to display the formatted time value in the form dd hh:mm:ss.ccc -*****************************************************************************/ -void F_WebPage::FormatTime( - FLMUINT uiTimerUnits, - char * pszFormattedTime) -{ - FLMUINT uiMilli; - FLMUINT uiSec; - FLMUINT uiMin; - FLMUINT uiHr; - FLMUINT uiDays; - FLMUINT uiTemp; - - // Initialize to NULL; - - pszFormattedTime[0] = '\0'; - - // Convert the timer units to milliseconds - - FLM_TIMER_UNITS_TO_MILLI( uiTimerUnits, uiMilli); - - // Determine the number of days - - uiDays = uiMilli / 86400000; - uiTemp = uiMilli % 86400000; - - // Now the hours - - uiHr = uiTemp / 3600000; - uiTemp = uiTemp % 3600000; - - // Determine the minutes - - uiMin = uiTemp / 60000; - uiTemp = uiTemp % 60000; - - // Determine seconds - - uiSec = uiTemp / 1000; - - // Determine the milliseconds - - uiMilli = uiTemp % 1000; - - // Put it all together - hh:mm:ss - - f_sprintf( (char *) pszFormattedTime, "%ld %2.2ld:%2.2ld:%2.2ld.%3.3ld", - uiDays, uiHr, uiMin, uiSec, uiMilli); -} - -/**************************************************************************** - Desc: Procedure to generate the HTML page that displays the usage statistics - structure. -****************************************************************************/ -RCODE F_WebPage::writeUsage( - FLM_CACHE_USAGE* pUsage, - FLMBOOL bRefresh, - const char* pszURL, - const char* pszTitle) -{ - RCODE rc = FERR_OK; - FLMBOOL bHighlight = FALSE; - char szTemp[100]; - - stdHdr(); - fnPrintf( m_pHRequest, HTML_DOCTYPE); - fnPrintf( m_pHRequest, "\n"); - - // Setup the page header & refresh control... Assuming the the first - // parameter ?Usage is already contained in the pszURL string. - - if (bRefresh) - { - fnPrintf( m_pHRequest, "" - "" - "%s\n", m_pszURLString, pszURL, pszTitle); - printStyle(); - fnPrintf( m_pHRequest, "\n\n"); - - f_sprintf( (char*) szTemp, "Stop Auto-refresh", - m_pszURLString, pszURL); - } - else - { - fnPrintf( m_pHRequest, "%s\n", pszTitle); - printStyle(); - fnPrintf( m_pHRequest, "\n\n"); - - f_sprintf( (char*) szTemp, - "Start Auto-refresh (5 sec.)", - m_pszURLString, pszURL); - } - - // Begin the table - - printTableStart( (char*) pszTitle, 4); - printTableRowStart(); - printColumnHeading( "", JUSTIFY_LEFT, FLM_IMON_COLOR_PUTTY_1, 4, 1, FALSE); - fnPrintf( m_pHRequest, "Refresh, ", m_pszURLString, pszURL); - fnPrintf( m_pHRequest, "%s\n", szTemp); - printColumnHeadingClose(); - printTableRowEnd(); - - // Write out the table headings. - - printTableRowStart(); - printColumnHeading( "Byte Offset (hex)"); - printColumnHeading( "Field Name"); - printColumnHeading( "Byte Offset"); - printColumnHeading( "Value"); - printTableRowEnd(); - - // uiMaxBytes - - printHTMLUint( (char*) "uiMaxBytes", (char*) "FLMUINT", (void*) pUsage, - (void*) &pUsage->uiMaxBytes, pUsage->uiMaxBytes, - (bHighlight = ~bHighlight)); - - // uiTotalBytesAllocated - - printHTMLUint( (char*) "uiTotalBytesAllocated", (char*) "FLMUINT", - (void*) pUsage, (void*) &pUsage->uiTotalBytesAllocated, - pUsage->uiTotalBytesAllocated, (bHighlight = ~bHighlight)); - - // uiCount - - printHTMLUint( (char*) "uiCount", (char*) "FLMUINT", (void*) pUsage, - (void*) &pUsage->uiCount, pUsage->uiCount, - (bHighlight = ~bHighlight)); - - // uiOldVerCount - - printHTMLUint( (char*) "uiOldVerCount", (char*) "FLMUINT", (void*) pUsage, - (void*) &pUsage->uiOldVerCount, pUsage->uiOldVerCount, - (bHighlight = ~bHighlight)); - - // uiOldVerBytes - - printHTMLUint( (char*) "uiOldVerBytes", (char*) "FLMUINT", (void*) pUsage, - (void*) &pUsage->uiOldVerBytes, pUsage->uiOldVerBytes, - (bHighlight = ~bHighlight)); - - // uiCacheHits - - printHTMLUint( (char*) "uiCacheHits", (char*) "FLMUINT", (void*) pUsage, - (void*) &pUsage->uiCacheHits, pUsage->uiCacheHits, - (bHighlight = ~bHighlight)); - - // uiCacheHitLooks - - printHTMLUint( (char*) "uiCacheHitLooks", (char*) "FLMUINT", (void*) pUsage, - (void*) &pUsage->uiCacheHitLooks, pUsage->uiCacheHitLooks, - (bHighlight = ~bHighlight)); - - // uiCacheFaults - - printHTMLUint( (char*) "uiCacheFaults", (char*) "FLMUINT", (void*) pUsage, - (void*) &pUsage->uiCacheFaults, pUsage->uiCacheFaults, - (bHighlight = ~bHighlight)); - - // uiCacheFaultLooks - - printHTMLUint( (char*) "uiCacheFaultLooks", (char*) "FLMUINT", - (void*) pUsage, (void*) &pUsage->uiCacheFaultLooks, - pUsage->uiCacheFaultLooks, (bHighlight = ~bHighlight)); - - printTableEnd(); - - fnPrintf( m_pHRequest, "
\n"); - fnPrintf( m_pHRequest, - "
\n"); - fnPrintf( m_pHRequest, "
\n"); - - fnPrintf( m_pHRequest, "\n"); - - fnEmit(); - - return (rc); -} - -/********************************************************************* -Desc: This function prints a linkable field in HTML -*********************************************************************/ -void F_WebPage::printHTMLLink( - const char * pszName, - const char * pszType, - void * pvBase, - void * pvAddress, - void * pvValue, - const char * pszLink, - FLMBOOL bHighlight) -{ - char szAddress[20]; - char szOffset[8]; - - printOffset( pvBase, pvAddress, szOffset); - printTableRowStart( bHighlight); - fnPrintf( m_pHRequest, TD_s, szOffset); // Field offset - - if (pvValue) - { - printAddress( pvValue, szAddress); - fnPrintf( m_pHRequest, TD_a_s_s, pszLink, pszName); // Link & Name - fnPrintf( m_pHRequest, TD_s, pszType); // Type - fnPrintf( m_pHRequest, TD_a_s_s, pszLink, szAddress); // Link & Value - } - else - { - fnPrintf( m_pHRequest, TD_s, pszName); - fnPrintf( m_pHRequest, TD_s, pszType); - fnPrintf( m_pHRequest, TD_s, "Null"); - } - - printTableRowEnd(); -} - -/********************************************************************* -Desc: This function prints a text field in HTML -*********************************************************************/ -void F_WebPage::printHTMLString( - const char * pszName, - const char * pszType, - void * pvBase, - void * pvAddress, - const char * pszValue, - FLMBOOL bHighlight) -{ - char szOffset[8]; - - printOffset( pvBase, pvAddress, szOffset); - printTableRowStart( bHighlight); - fnPrintf( m_pHRequest, TD_s, szOffset); // Field offset - fnPrintf( m_pHRequest, TD_s, pszName); // Name - fnPrintf( m_pHRequest, TD_s, pszType); // Type - fnPrintf( m_pHRequest, TD_s, pszValue); // Value - printTableRowEnd(); -} - -/********************************************************************* -Desc: This function prints a unsigned long (FLMUINT) field in HTML -*********************************************************************/ -void F_WebPage::printHTMLUint( - const char * pszName, - const char * pszType, - void * pvBase, - void * pvAddress, - FLMUINT uiValue, - FLMBOOL bHighlight) -{ - char szOffset[8]; - - printOffset( pvBase, pvAddress, szOffset); - printTableRowStart( bHighlight); - fnPrintf( m_pHRequest, TD_s, szOffset); // Field offset - fnPrintf( m_pHRequest, TD_s, pszName); // Name - fnPrintf( m_pHRequest, TD_s, pszType); // Type - fnPrintf( m_pHRequest, TD_ui, uiValue); // Value - printTableRowEnd(); -} - -/********************************************************************* -Desc: This function prints a signed long (FLMINT) field in HTML -*********************************************************************/ -void F_WebPage::printHTMLInt( - const char * pszName, - const char * pszType, - void * pvBase, - void * pvAddress, - FLMINT iValue, - FLMBOOL bHighlight) -{ - char szOffset[8]; - - printOffset( pvBase, pvAddress, szOffset); - printTableRowStart( bHighlight); - fnPrintf( m_pHRequest, TD_s, szOffset); // Field offset - fnPrintf( m_pHRequest, TD_s, pszName); // Name - fnPrintf( m_pHRequest, TD_s, pszType); // Type - fnPrintf( m_pHRequest, TD_i, iValue); // Value - printTableRowEnd(); -} - -/********************************************************************* -Desc: This function prints a unsigned long field in HTML -*********************************************************************/ -void F_WebPage::printHTMLUlong( - const char * pszName, - const char * pszType, - void * pvBase, - void * pvAddress, - unsigned long luValue, - FLMBOOL bHighlight) -{ - char szOffset[8]; - - printOffset( pvBase, pvAddress, szOffset); - printTableRowStart( bHighlight); - fnPrintf( m_pHRequest, TD_s, szOffset); // Field offset - fnPrintf( m_pHRequest, TD_s, pszName); // Name - fnPrintf( m_pHRequest, TD_s, pszType); // Type - fnPrintf( m_pHRequest, TD_lu, luValue); // Value - printTableRowEnd(); -} - -/********************************************************************* -Desc: This function takes the name of a form field, and returns a - pointer to it. This will allocate the buffer returned. The - calling function is responsible for freeing that buffer. -*********************************************************************/ -RCODE F_WebPage::getFormValueByName( - const char * pszValueTag, - char ** ppszBuf, - FLMUINT uiBufLen, - FLMUINT * puiDataLen) -{ - RCODE rc = FERR_OK; - char szTag[128]; - char * pszValue; - FLMUINT uiLen; - FLMBOOL bFreeFormData = FALSE; - FLMBOOL bFreeUserData = FALSE; - - if (puiDataLen) - { - *puiDataLen = 0; - } - -#ifdef FLM_DEBUG - if (!uiBufLen) - { - flmAssert( ppszBuf && *ppszBuf == NULL); - } -#endif - - if (f_strlen( pszValueTag) + 1 >= sizeof(szTag)) - { - flmAssert( 0); - rc = RC_SET( FERR_MEM); - goto Exit; - } - - f_sprintf( (char*) szTag, "%s=", pszValueTag); - - if (!m_pszFormData) - { - char * pszContentLength; - FLMUINT uiContentLength; - - // First we need to determine how much form data there is. - - if ((pszContentLength = (char*) fnReqHdrValue( "Content-Length")) == NULL) - { - rc = RC_SET( FERR_NOT_FOUND); - goto Exit; - } - - if ((uiContentLength = f_atoi( pszContentLength)) == 0) - { - rc = RC_SET( FERR_NOT_FOUND); - goto Exit; - } - - // Now allocate a buffer to hold the form data - - if (RC_BAD( rc = f_alloc( uiContentLength + 1, &m_pszFormData))) - { - goto Exit; - } - - bFreeFormData = TRUE; - - if (fnRecvBuffer( m_pszFormData, (size_t*) &uiContentLength) != 0) - { - rc = RC_SET( FERR_FAILURE); - goto Exit; - } - - m_pszFormData[uiContentLength] = 0; - bFreeFormData = FALSE; - } - - // Now, parse through the buffer until we find the field we are - // looking for. The data is in the form name=value:name=value... - - if ((pszValue = f_strstr( m_pszFormData, szTag)) != NULL) - { - pszValue += f_strlen( szTag); - for (uiLen = 0; - pszValue[uiLen] && pszValue[uiLen] != ':' && pszValue[uiLen] != '&'; - uiLen++); - - if (ppszBuf) - { - if (!uiBufLen) - { - uiBufLen = uiLen + 1; - bFreeUserData = TRUE; - *ppszBuf = NULL; - if (RC_BAD( rc = f_alloc( uiBufLen, ppszBuf))) - { - goto Exit; - } - } - - if (uiLen >= uiBufLen) - { - rc = RC_SET( FERR_CONV_DEST_OVERFLOW); - goto Exit; - } - - f_memcpy( *ppszBuf, pszValue, uiLen); - (*ppszBuf)[uiLen] = 0; - bFreeUserData = FALSE; - } - - if (puiDataLen) - { - *puiDataLen = uiLen + 1; - } - - goto Exit; - } - else - { - rc = RC_SET( FERR_NOT_FOUND); - goto Exit; - } - -Exit: - - if (bFreeFormData) - { - f_free( &m_pszFormData); - } - - if (bFreeUserData && *ppszBuf) - { - f_free( ppszBuf); - } - - return (rc); -} - -/**************************************************************************** -Desc: Prints the standard style sheet -****************************************************************************/ -void F_WebPage::printStyle(void) -{ - fnPrintf( m_pHRequest, - "\n", - m_pszURLString); -} - -/**************************************************************************** -Desc: Outputs a column heading using elements from the standard style sheet -****************************************************************************/ -void F_WebPage::printColumnHeading( - const char * pszHeading, - JustificationType eJustification, - const char * pszBackground, - FLMUINT uiColSpan, - FLMUINT uiRowSpan, - FLMBOOL bClose, - FLMUINT uiWidth) -{ - fnPrintf( m_pHRequest, - "\n"); - - if (pszHeading) - { - printEncodedString( pszHeading); - } - - if (bClose) - { - fnPrintf( m_pHRequest, "\n"); - } -} - -/**************************************************************************** -Desc: Closes a column heading -****************************************************************************/ -void F_WebPage::printColumnHeadingClose(void) -{ - fnPrintf( m_pHRequest, "\n"); -} - -/**************************************************************************** -Desc: Encodes a string for rendering in an HTML page or for inclusion in - an URL -****************************************************************************/ -void F_WebPage::printEncodedString( - const char * pszString, - FStringEncodeType eEncodeType, - FLMBOOL bMapSlashes) -{ - char ucChar; - - while ((ucChar = *pszString) != 0) - { - if ((ucChar >= '0' && ucChar <= '9') || - (ucChar >= 'A' && ucChar <= 'Z') || - (ucChar >= 'a' && ucChar <= 'z') || - ucChar == '_' || - ( - eEncodeType == URL_PATH_ENCODING && - (ucChar == '.' || (bMapSlashes && (ucChar == '/' || ucChar == '\\'))) - )) - { - if (ucChar == '\\') - { - ucChar = '/'; - } - - fnPrintf( m_pHRequest, "%c", ucChar); - } - else if (eEncodeType == URL_PATH_ENCODING) - { - fnPrintf( m_pHRequest, "%%%02X", (unsigned) ucChar); - } - else if (eEncodeType == URL_QUERY_ENCODING) - { - if (ucChar == ' ') - { - ucChar = '+'; - } - - fnPrintf( m_pHRequest, "%%%02X", (unsigned) ucChar); - } - else // HTML encoding - { - fnPrintf( m_pHRequest, "&#%u;", (unsigned) ucChar); - } - - pszString++; - } -} - -/**************************************************************************** -Desc: -****************************************************************************/ -void F_WebPage::printDocStart( - const char * pszTitle, - FLMBOOL bPrintTitle, - FLMBOOL bStdHeader, - const char * pszBackground) -{ - if (bStdHeader) - { - stdHdr(); - } - - fnPrintf( m_pHRequest, HTML_DOCTYPE); - fnPrintf( m_pHRequest, "\n"); - fnPrintf( m_pHRequest, "\n"); - printRecordStyle(); - printStyle(); - fnPrintf( m_pHRequest, "Database iMonitor - "); - printEncodedString( pszTitle); - fnPrintf( m_pHRequest, "\n"); - fnPrintf( m_pHRequest, "\n"); - fnPrintf( m_pHRequest, "\n", - pszBackground ? pszBackground : "white"); - - if (bPrintTitle) - { - printTableStart( pszTitle, 1); - printTableEnd(); - fnPrintf( m_pHRequest, "
\n"); - } -} - -/**************************************************************************** -Desc: -****************************************************************************/ -void F_WebPage::printDocEnd(void) -{ - fnPrintf( m_pHRequest, "\n"); - fnPrintf( m_pHRequest, "\n"); -} - -/**************************************************************************** -Desc: -****************************************************************************/ -void F_WebPage::printMenuReload(void) -{ - fnPrintf( m_pHRequest, "\n"); -} - -/**************************************************************************** -Desc: -****************************************************************************/ -void F_WebPage::printTableStart( - const char* pszTitle, - FLMUINT uiColumns, - FLMUINT uiWidthFactor) -{ - fnPrintf( m_pHRequest, "\n"); - - if (pszTitle) - { - printTableRowStart(); - fnPrintf( m_pHRequest, ""); - printTableRowEnd(); - } -} - -/**************************************************************************** -Desc: -****************************************************************************/ -void F_WebPage::printTableEnd(void) -{ - fnPrintf( m_pHRequest, "
\n"); - printEncodedString( pszTitle); - fnPrintf( m_pHRequest, "
\n"); -} - -/**************************************************************************** -Desc: -****************************************************************************/ -void F_WebPage::printTableRowStart( - FLMBOOL bHighlight) -{ - fnPrintf( m_pHRequest, "\n"); -} - -/**************************************************************************** -Desc: -****************************************************************************/ -void F_WebPage::printTableRowEnd(void) -{ - fnPrintf( m_pHRequest, "\n"); -} - -/**************************************************************************** -Desc: -****************************************************************************/ -void F_WebPage::printTableDataStart( - FLMBOOL bNoWrap, - JustificationType eJustification, - FLMUINT uiWidth) -{ - fnPrintf( m_pHRequest, "\n"); -} - -/**************************************************************************** -Desc: -****************************************************************************/ -void F_WebPage::printTableDataEnd(void) -{ - fnPrintf( m_pHRequest, "\n"); -} - -/**************************************************************************** -Desc: -****************************************************************************/ -void F_WebPage::printTableDataEmpty(void) -{ - fnPrintf( m_pHRequest, " "); -} - -/**************************************************************************** -Desc: -****************************************************************************/ -void F_WebPage::printErrorPage( - RCODE rc, - FLMBOOL bStdHeader, - const char * pszWhat) -{ - printDocStart( "Error", TRUE, bStdHeader); - - fnPrintf( m_pHRequest, "

\n"); - fnPrintf( m_pHRequest, "%s
%s (0x%04X).\n", pszWhat, FlmErrorString( rc), - (unsigned) rc); - fnPrintf( m_pHRequest, "

\n"); - - printDocEnd(); -} - -/**************************************************************************** -Desc: -****************************************************************************/ -void F_WebPage::printErrorPage( - const char * pszErrStr, - const char * pszErrStr2, - FLMBOOL bStdHeader) -{ - printDocStart( "Error", TRUE, bStdHeader); - - fnPrintf( m_pHRequest, "

\n"); - fnPrintf( m_pHRequest, "%s\n", pszErrStr); - if (pszErrStr2 && *pszErrStr2) - { - fnPrintf( m_pHRequest, "
%s\n", pszErrStr2); - } - - fnPrintf( m_pHRequest, "

\n"); - - printDocEnd(); -} - -/**************************************************************************** -Desc: Start an input form -****************************************************************************/ -void F_WebPage::printStartInputForm( - const char * pszFormName, - const char * pszPage, - FLMUINT uiFormValue) -{ - fnPrintf( m_pHRequest, - "
\n" - "\n", - pszFormName, m_pszURLString, pszPage, (unsigned) uiFormValue); -} - -/**************************************************************************** -Desc: End an input form -****************************************************************************/ -void F_WebPage::printEndInputForm(void) -{ - fnPrintf( m_pHRequest, "
"); -} - -/**************************************************************************** -Desc: Generic function to output HTML that gererates a button -****************************************************************************/ -void F_WebPage::printButton( - const char * pszContents, - ButtonTypes eBType, - const char * pszName, - const char * pszValue, - const char * pszExtra, - FLMBOOL bDisabled, - FLMBYTE ucAccessKey, - FLMUINT uiTabIndex) -{ - fnPrintf( m_pHRequest, "\n", - (char*) (pszContents ? pszContents : "")); -} - - -/**************************************************************************** -Desc: Format and output date. -****************************************************************************/ -void F_WebPage::printDate( - FLMUINT uiGMTTime, - char * pszBuffer) -{ - F_TMSTAMP timeStamp; - FLMUINT uiLocalTime; - char * pszAmPm; - const char * pszMonth; - - uiLocalTime = (FLMUINT) (uiGMTTime - f_timeGetLocalOffset()); - f_timeSecondsToDate( uiLocalTime, &timeStamp); - - pszAmPm = (char*) ((timeStamp.hour >= 12) ? (char*) "pm" : (char*) "am"); - if (timeStamp.hour > 12) - { - timeStamp.hour -= 12; - } - - if (timeStamp.hour == 0) - { - timeStamp.hour = 12; - } - - switch (timeStamp.month) - { - case 0: - pszMonth = "Jan"; - break; - case 1: - pszMonth = "Feb"; - break; - case 2: - pszMonth = "Mar"; - break; - case 3: - pszMonth = "Apr"; - break; - case 4: - pszMonth = "May"; - break; - case 5: - pszMonth = "Jun"; - break; - case 6: - pszMonth = "Jul"; - break; - case 7: - pszMonth = "Aug"; - break; - case 8: - pszMonth = "Sep"; - break; - case 9: - pszMonth = "Oct"; - break; - case 10: - pszMonth = "Nov"; - break; - default: - case 11: - pszMonth = "Dec"; - break; - } - - if (pszBuffer != NULL) - { - f_sprintf( (char*) pszBuffer, "%s %u, %u %u:%02u:%02u %s", pszMonth, - (unsigned) timeStamp.day, (unsigned) timeStamp.year, - (unsigned) timeStamp.hour, (unsigned) timeStamp.minute, - (unsigned) timeStamp.second, pszAmPm); - } - else - { - fnPrintf( m_pHRequest, "%s %u, %u %u:%02u:%02u %s", pszMonth, - (unsigned) timeStamp.day, (unsigned) timeStamp.year, - (unsigned) timeStamp.hour, (unsigned) timeStamp.minute, - (unsigned) timeStamp.second, pszAmPm); - } -} - -/**************************************************************************** -Desc: Outputs a Yes or No value based on the passed-in boolean -****************************************************************************/ -void F_WebPage::printYesNo( - FLMBOOL bYes) -{ - fnPrintf( m_pHRequest, "%s", bYes ? "Yes" : "No"); -} - -/**************************************************************************** -Desc: Outputs a number with commas, for easier reading. -****************************************************************************/ -void F_WebPage::printCommaNumText( - FLMUINT64 ui64Num) -{ - FLMUINT uiTerm; - FLMUINT64 ui64Divisor = 1; - FLMBOOL bFirstPass = TRUE; - - while ((FLMUINT64) (ui64Num / (ui64Divisor * (FLMUINT64) 1000))) - { - ui64Divisor *= 1000; - } - - while (ui64Divisor) - { - uiTerm = (FLMUINT) (ui64Num / ui64Divisor); - ui64Num -= ((FLMUINT64) uiTerm) * ui64Divisor; - - if (bFirstPass) - { - fnPrintf( m_pHRequest, "%u", (unsigned) uiTerm); - bFirstPass = FALSE; - } - else - { - fnPrintf( m_pHRequest, "%03u", (unsigned) uiTerm); - } - - if ((ui64Divisor /= (FLMUINT64) 1000) > (FLMUINT64) 0) - { - fnPrintf( m_pHRequest, ","); - } - } -} - -/**************************************************************************** -Desc: Outputs a number with commas, for easier reading. -****************************************************************************/ -void F_WebPage::printCommaNum( - FLMUINT64 ui64Num, - JustificationType eJustify, - FLMBOOL bChangedValue) -{ - printTableDataStart( TRUE, eJustify); - if (bChangedValue) - { - fnPrintf( m_pHRequest, ""); - } - - printCommaNumText( ui64Num); - - if (bChangedValue) - { - fnPrintf( m_pHRequest, ""); - } - - printTableDataEnd(); -} - -/**************************************************************************** -Desc: -****************************************************************************/ -RCODE F_WebPage::acquireSession(void) -{ - RCODE rc = FERR_OK; - FLMBOOL bHttpSessionMutexLocked = FALSE; - FLMUINT uiSize; - void * pvHttpSession = NULL; - char szSessionKey[F_SESSION_KEY_LEN]; - - m_pFlmSession = NULL; - - if (!gv_FlmSysData.HttpConfigParms.fnAcquireSession) - { - rc = RC_SET( FERR_NOT_IMPLEMENTED); - goto Exit; - } - - if ((pvHttpSession = fnAcquireSession()) == NULL) - { - rc = RC_SET( FERR_MEM); - goto Exit; - } - - f_mutexLock( gv_FlmSysData.hHttpSessionMutex); - bHttpSessionMutexLocked = TRUE; - - uiSize = sizeof(szSessionKey); - if (fnGetSessionValue( pvHttpSession, FLM_SESSION_ID_NAME, - (void*) szSessionKey, (size_t*) &uiSize) != 0) - { -CreateSession: - - if (RC_BAD( rc = gv_FlmSysData.pSessionMgr->createSession( &m_pFlmSession))) - { - goto Exit; - } - - fnSetSessionValue( pvHttpSession, FLM_SESSION_ID_NAME, - m_pFlmSession->getKey(), sizeof(szSessionKey)); - } - else - { - if (RC_BAD( rc = gv_FlmSysData.pSessionMgr->getSession( szSessionKey, - &m_pFlmSession))) - { - if (rc == FERR_NOT_FOUND) - { - goto CreateSession; - } - } - } - -Exit: - - if (RC_BAD( rc)) - { - if (m_pFlmSession) - { - releaseSession(); - } - } - - if (pvHttpSession) - { - fnReleaseSession( pvHttpSession); - } - - if (bHttpSessionMutexLocked) - { - f_mutexUnlock( gv_FlmSysData.hHttpSessionMutex); - } - - return (rc); -} - -/**************************************************************************** -Desc: -****************************************************************************/ -void F_WebPage::releaseSession(void) -{ - if (m_pFlmSession) - { - gv_FlmSysData.pSessionMgr->releaseSession( &m_pFlmSession); - m_pFlmSession = NULL; - } -} - -/**************************************************************************** -Desc: -****************************************************************************/ -void F_WebPage::printSpaces( - FLMUINT uiCount) -{ - while (uiCount--) - { - fnPrintf( m_pHRequest, " "); - } -} - -/**************************************************************************** -Desc: Outputs elapsed milliseconds as seconds.milli. The optional parameter - pszBuffer will cause the time to be written to pszBuffer instead of the - web page. That way it can be incorporated into more complex structures - if desired. -****************************************************************************/ -void F_WebPage::printElapTime( - FLMUINT64 ui64ElapTime, - char* pszBuffer, - JustificationType eJustify, - FLMBOOL bTimeIsMilli) -{ - FLMUINT uiHours; - FLMUINT uiMinutes; - FLMUINT uiSeconds; - FLMUINT uiMilli = 0; - - if (bTimeIsMilli) - { - uiHours = (FLMUINT) (ui64ElapTime / (FLMUINT64) (1000 * 3600)); - uiMinutes = (FLMUINT) ((ui64ElapTime / (FLMUINT64) (1000 * 60)) % (FLMUINT64) 60); - uiSeconds = (FLMUINT) ((ui64ElapTime / (FLMUINT64) 1000) % (FLMUINT64) 60); - uiMilli = (FLMUINT) (ui64ElapTime % (FLMUINT64) 1000); - } - else - { - uiHours = (FLMUINT) (ui64ElapTime / (FLMUINT64) 3600); - uiMinutes = (FLMUINT) ((ui64ElapTime / (FLMUINT64) 60) % (FLMUINT64) 60); - uiSeconds = (FLMUINT) (ui64ElapTime % (FLMUINT64) 60); - } - - if (!pszBuffer) - { - printTableDataStart( TRUE, eJustify); - } - - if (pszBuffer) - { - f_sprintf( (char*) pszBuffer, "%02u:%02u:%02u", (unsigned) uiHours, - (unsigned) uiMinutes, (unsigned) uiSeconds); - } - else - { - fnPrintf( m_pHRequest, "%02u:%02u:%02u", (unsigned) uiHours, - (unsigned) uiMinutes, (unsigned) uiSeconds); - } - - if (bTimeIsMilli) - { - if (pszBuffer) - { - char szTemp[5]; - - f_sprintf( szTemp, ".%03u", (unsigned) uiMilli); - f_strncat( (char*) pszBuffer, szTemp, 4); - } - else - { - fnPrintf( m_pHRequest, ".%03u", (unsigned) uiMilli); - } - } - - if (!pszBuffer) - { - printTableDataEnd(); - } -} - -/**************************************************************************** -Desc: This function will output a form in an already existing page that will - present a formatted display of the record passed in. An optional parameter - bReadOnly will determine if the form should allow editing of the record - or not. The default value is TRUE (No editing capability). A null may - be passed in for the FlmRecord pointer, in which case an empty display - will be created. Normally, a bReadOnly value of false would accompany - a null record so that a record can be created. The printRecordStyle() - must be called in the header section of the page prior to calling this - function. Multiple calls may be made to display multiple records per page. - The puiContext parameter must be initialized to zero before the first - (and possibly the only) call, otherwise the scripts need to run this page - will not be loaded. -****************************************************************************/ -void F_WebPage::printRecord( - const char * pszDbKey, - FlmRecord * pRec, - F_NameTable * pNameTable, - FLMUINT * puiContext, - FLMBOOL bReadOnly, - FLMUINT uiSelectedField, - FLMUINT uiFlags) -{ - #define SP " " - - FLMBOOL bEmpty = FALSE; - FLMUINT uiContainer; - FLMUINT uiDrn; - void * pvField; - FLMUINT uiFieldCounter; - FLMUINT uiFldCnt; - FLMUINT uiTagNum; - FLMUINT uiLevel; - FLMUINT uiType; - FLMUINT uiContext = 0; - char szNameBuf[128]; - - flmAssert( pNameTable); - - if (puiContext) - { - uiContext = *puiContext; - (*puiContext)++; - } - - // See if we need to write out the scripts - - if (uiContext == 0) - { - printRecordScripts(); - } - - if (!pRec) - { - bEmpty = TRUE; - uiDrn = 0; - uiContainer = 0; - } - else - { - - // Get the Drn & Container - - uiDrn = pRec->getID(); - uiContainer = pRec->getContainerID(); - } - - // Count the fields - - uiFldCnt = 0; - if (pRec != NULL) - { - pvField = pRec->root(); - while (pvField) - { - pvField = pRec->next( pvField); - uiFldCnt++; - } - } - - // Begin the form that displays the record data (if any) - - fnPrintf( m_pHRequest, - "
\n", - uiContext, gv_FlmSysData.HttpConfigParms.pszURLString); - printHiddenField( "ReadOnly", (char*) (bReadOnly ? "TRUE" : "FALSE")); - - if (pszDbKey) - { - printHiddenField( "dbhandle", (char*) (pszDbKey)); - } - - printHiddenField( (char*) "Action", "none"); - printHiddenField( (char*) "FieldLevel", (FLMUINT) 0); - printHiddenField( (char*) "FieldNumber", (FLMUINT) 0); - printHiddenField( (char*) "FieldCount", uiFldCnt); - - // Print out the block that displays the DRN and Container list - - fnPrintf( m_pHRequest, "
\n"); - fnPrintf( m_pHRequest, - "\n"); - fnPrintf( m_pHRequest, "\n\n\n", - uiDrn, pszDbKey == NULL ? "disabled" : ""); - - if (pszDbKey != NULL) - { - fnPrintf( m_pHRequest, "\n\n\n"); - } - - fnPrintf( m_pHRequest, "\n\n\n", uiContainer); - } - - fnPrintf( m_pHRequest, "\n\n"); - - if (pszDbKey != NULL) - { - - // Print out the field list drop down box. - - fnPrintf( m_pHRequest, "\n\n\n"); - - // Print out the Add, Modify, Delete action buttons. - - fnPrintf( m_pHRequest, - "\n"); - } - - fnPrintf( m_pHRequest, "
DRN  
Flags \n"); - printRetrievalFlagsPulldown( uiFlags); - fnPrintf( m_pHRequest, "
Container \n"); - if (pszDbKey != NULL) - { - printContainerPulldown( pNameTable, uiContainer); - } - else - { - fnPrintf( m_pHRequest, - " 
Field list "); - printFieldPulldown( pNameTable, uiSelectedField); - fnPrintf( m_pHRequest, "
", uiContext); - if (pRec != NULL) - { - if (!bReadOnly) - { - fnPrintf( m_pHRequest, "", uiContext); - fnPrintf( m_pHRequest, "", uiContext); - } - - fnPrintf( m_pHRequest, "", uiContext); - } - - fnPrintf( m_pHRequest, "", uiContext); - if (pRec != NULL && bReadOnly) - { - fnPrintf( m_pHRequest, "", uiContext); - } - - fnPrintf( m_pHRequest, "
\n
\n"); - - // Print out the record fields (if there are any) - - if (pRec != NULL) - { - fnPrintf( m_pHRequest, "
\n"); - if (!bReadOnly) - { - fnPrintf( m_pHRequest, "", uiContext); - if (uiFldCnt > 1) - { - fnPrintf( m_pHRequest, "", uiContext); - fnPrintf( m_pHRequest, "", uiContext); - fnPrintf( m_pHRequest, "\n", uiContext); - } - } - - fnPrintf( m_pHRequest, "
\n");
-
-		// Now for the actual data. Start with the root field.
-
-		pvField = pRec->root();
-
-		uiFieldCounter = 0;
-
-		while (pvField)
-		{
-			uiTagNum = pRec->getFieldID( pvField);
-			uiLevel = pRec->getLevel( pvField);
-			uiType = pRec->getDataType( pvField);
-
-			if (uiLevel != 0 && !bReadOnly)
-			{
-				fnPrintf( m_pHRequest,
-							"",
-						uiFieldCounter, uiContext, uiFieldCounter, uiLevel);
-			}
-
-			pNameTable->getFromTagNum( uiTagNum, NULL, szNameBuf, sizeof(szNameBuf));
-			printSpaces( uiLevel + 5);
-
-			fnPrintf( m_pHRequest, "%s%d%s%s%s", SP,
-						uiLevel, SP, szNameBuf, SP);
-
-			if (pRec->getDataLength( pvField))
-			{
-				switch (uiType)
-				{
-					case FLM_TEXT_TYPE:
-						printTextField( pRec, pvField, uiFieldCounter, bReadOnly);
-						break;
-					case FLM_NUMBER_TYPE:
-						printNumberField( pRec, pvField, uiFieldCounter, bReadOnly);
-						break;
-					case FLM_BINARY_TYPE:
-						printBinaryField( pRec, pvField, uiFieldCounter, bReadOnly);
-						break;
-					case FLM_CONTEXT_TYPE:
-						printContextField( pRec, pvField, uiFieldCounter, bReadOnly);
-						break;
-					case FLM_BLOB_TYPE:
-						printBlobField( pRec, pvField, uiFieldCounter, bReadOnly);
-						break;
-					default:
-						printDefaultField( pRec, pvField, uiFieldCounter, bReadOnly);
-						break;
-				}
-			}
-			else if (!bReadOnly)
-			{
-				fnPrintf( m_pHRequest,
-							"",
-						uiFieldCounter, MAX_FIELD_SIZE( 0));
-			}
-
-			// Print the hidden field Ids
-
-			printFieldIds( uiFieldCounter, uiLevel, uiType, uiTagNum);
-			fnPrintf( m_pHRequest, "\n");
-
-			pvField = pRec->next( pvField);
-			uiFieldCounter++;
-		}
-
-		fnPrintf( m_pHRequest, "
\n
\n
\n"); - } - - fnPrintf( m_pHRequest, "
\n"); - - return; -} - - -/**************************************************************************** -Desc: Prints out a style sheet specific to displaying records. -****************************************************************************/ -void F_WebPage::printRecordStyle(void) -{ - fnPrintf( m_pHRequest, "\n"); -} - -/**************************************************************************** -Desc: Prints out the required scripts for displaying and updating records. -****************************************************************************/ -void F_WebPage::printRecordScripts( void) -{ - fnPrintf( m_pHRequest, "\n"); -} - -/**************************************************************************** -Desc: Prints out a hidden field with a character string value -****************************************************************************/ -void F_WebPage::printHiddenField( - const char * pszName, - const char * pszValue) -{ - fnPrintf( m_pHRequest, "", - pszName, pszValue); -} - -// -/**************************************************************************** -Desc: Prints out a hidden with an unsigned long value -****************************************************************************/ -void F_WebPage::printHiddenField( - const char * pszName, - FLMUINT uiValue) -{ - fnPrintf( m_pHRequest, "", - pszName, (unsigned) uiValue); -} - -// -/**************************************************************************** -Desc: Prints out the value for the field, assuming the field is a text field. -****************************************************************************/ -void F_WebPage::printTextField( - FlmRecord * pRec, - void * pvField, - FLMUINT uiFieldCounter, - FLMBOOL bReadOnly) -{ - RCODE rc = FERR_OK; - FLMUNICODE * puzBuf = NULL; - FLMUNICODE * puzTmp = NULL; - F_DynamicBuffer * pBuffer = NULL; - FLMUINT uiLen; - - if (RC_BAD( rc = pRec->getUnicodeLength( pvField, &uiLen))) - { - fnPrintf( m_pHRequest, - "** Error retrieving Unicode field length (Return Code = 0x%04X, %s) **", - (unsigned) rc, FlmErrorString( rc)); - goto Exit; - } - - // The length returned does not allow for 2 NULL terminators. We must - // allow for them when allocating a buffer. - - uiLen += 2; - if (RC_BAD( rc = f_alloc( uiLen, &puzBuf))) - { - fnPrintf( m_pHRequest, - "** Error allocating memory buffer (Return Code = 0x%04X, %s) **", - (unsigned) rc, FlmErrorString( rc)); - goto Exit; - } - - if (RC_BAD( rc = pRec->getUnicode( pvField, puzBuf, &uiLen))) - { - fnPrintf( m_pHRequest, - "** Error retrieving Unicode field (Return Code = 0x%04X, %s) **", - (unsigned) rc, FlmErrorString( rc)); - goto Exit; - } - - puzTmp = puzBuf; - if ((pBuffer = f_new F_DynamicBuffer) == NULL) - { - fnPrintf( m_pHRequest, "** Error allocating memory **"); - goto Exit; - } - - // Start the text field if not read only mode. - - if (!bReadOnly) - { - fnPrintf( m_pHRequest, - ""); - } - - while (*puzTmp) - { - - // Check for ASCII characters - - if ((*puzTmp >= 32) && (*puzTmp <= 126)) - { - if (RC_BAD( rc = pBuffer->addChar( (char) *puzTmp))) - { - fnPrintf( m_pHRequest, - "** Error adding Unicode character to buffer (Return Code = 0x%04X, %s) **", - (unsigned) rc, FlmErrorString( rc)); - goto Exit; - } - } - else - { - - // Treat as though these are NON-ASCII. They will be printed in - // the form ~[0x ] - - char szTempBuff[20]; - - f_sprintf( szTempBuff, "~[0x%04X]", (unsigned) (*puzTmp)); - - if (RC_BAD( rc = pBuffer->addString( szTempBuff))) - { - fnPrintf( m_pHRequest, - "** Error formatting Unicode string (Return Code = 0x%04X, %s) **", - (unsigned) rc, FlmErrorString( rc)); - goto Exit; - } - } - - // We are attempting to not let our buffer get any larger than the - // Http stack buffer size. We don't really know what the limit is, - // but we are using what seems to reasonable to us... - - if ((pBuffer->getBufferSize() + 9) >= RESP_WRITE_BUF_SIZE) - { - fnPrintf( m_pHRequest, "%s", pBuffer->printBuffer()); - pBuffer->reset(); - } - - puzTmp++; - } - - if (bReadOnly) - { - fnPrintf( m_pHRequest, "%s", pBuffer->printBuffer()); - } - else - { - fnPrintf( m_pHRequest, "%s\" size=\"%d\">", pBuffer->printBuffer(), - MAX_FIELD_SIZE( uiLen)); - } - -Exit: - - if (puzBuf) - { - f_free( &puzBuf); - } - - if (pBuffer) - { - pBuffer->Release(); - } -} - -// -/**************************************************************************** -Desc: Prints out the value for the field, assuming the field is a number field. -****************************************************************************/ -void F_WebPage::printNumberField( - FlmRecord * pRec, - void * pvField, - FLMUINT uiFieldCounter, - FLMBOOL bReadOnly) -{ - RCODE rc = FERR_OK; - FLMINT iVal; - FLMUINT uiVal; - - if (RC_BAD( rc = pRec->getUINT( pvField, &uiVal))) - { - if (RC_OK( rc = pRec->getINT( pvField, &iVal))) - { - if (bReadOnly) - { - fnPrintf( m_pHRequest, "%d", (int) iVal); - } - else - { - fnPrintf( m_pHRequest, - "", - uiFieldCounter, (int) iVal, MAX_FIELD_SIZE( 0)); - } - } - else - { - fnPrintf( m_pHRequest, - "** Error retrieving number field (Return Code = 0x%04X, %s)**\n", - (unsigned) rc, FlmErrorString( rc)); - } - } - else - { - if (bReadOnly) - { - fnPrintf( m_pHRequest, "%lu", - (unsigned long) uiVal); - } - else - { - fnPrintf( m_pHRequest, - "", - uiFieldCounter, (unsigned long) uiVal); - } - } -} - -/**************************************************************************** -Desc: Prints out the value for the field, assuming the field is a binary field. -****************************************************************************/ -void F_WebPage::printBinaryField( - FlmRecord * pRec, - void * pvField, - FLMUINT uiFieldCounter, - FLMBOOL bReadOnly) -{ - RCODE rc = FERR_OK; - FLMBYTE * pucBuf = NULL; - FLMBYTE * pszTmpBuf = NULL; - FLMBYTE * pszTmp = NULL; - FLMUINT uiLoop; - FLMUINT uiLen; - FLMUINT uiBufLen; - - uiLen = pRec->getDataLength( pvField); - - if (RC_BAD( rc = f_alloc( uiLen, &pucBuf))) - { - fnPrintf( m_pHRequest, - "** Error occured allocating memory to retrieve binary field (Return Code = 0x%04X, %s) **\n", - (unsigned) rc, FlmErrorString( rc)); - goto Exit; - } - - if (RC_BAD( rc = pRec->getBinary( pvField, pucBuf, &uiLen))) - { - if (rc != FERR_NOT_FOUND) - { - fnPrintf( m_pHRequest, - "** Error occured retrieving binary field (Return Code = 0x%04X, %s) **\n", - (unsigned) rc, FlmErrorString( rc)); - goto Exit; - } - } - - if (RC_BAD( rc = f_alloc( RESP_WRITE_BUF_SIZE + 1, &pszTmpBuf))) - { - fnPrintf( m_pHRequest, - "** Error occured allocating memory to format binary field (Return Code = 0x%04X, %s) **\n", - (unsigned) rc, FlmErrorString( rc)); - goto Exit; - } - - if (!bReadOnly) - { - fnPrintf( m_pHRequest, - ""); - } - - // Scan through the binary data, present all data as Hex. - - for (pszTmp = pszTmpBuf, uiLoop = 0, uiBufLen = 0; uiLoop < uiLen; uiLoop++) - { - if (uiLoop) - { - *pszTmp++ = ' '; - uiBufLen++; - } - - f_sprintf( (char*) pszTmp, "%2.2X", (unsigned) pucBuf[uiLoop]); - - pszTmp += 2; - uiBufLen += 2; - - if ((uiBufLen + 3) >= RESP_WRITE_BUF_SIZE) - { - - // Flush the current buffer - - *pszTmp = '\0'; - fnPrintf( m_pHRequest, "%s", pszTmpBuf); - pszTmp = pszTmpBuf; - uiBufLen = 0; - } - } - - *pszTmp = '\0'; - - if (bReadOnly) - { - fnPrintf( m_pHRequest, "%s", pszTmpBuf); - } - else - { - fnPrintf( m_pHRequest, "%s\" size=\"%d\">", pszTmpBuf, - MAX_FIELD_SIZE( uiLen * 3)); - } - -Exit: - - if (pucBuf) - { - f_free( &pucBuf); - } - - if (pszTmpBuf) - { - f_free( &pszTmpBuf); - } -} - -/**************************************************************************** -Desc: Prints out the value for the field, assuming the field is a context field. -****************************************************************************/ -void F_WebPage::printContextField( - FlmRecord * pRec, - void * pvField, - FLMUINT uiFieldCounter, - FLMBOOL bReadOnly) -{ - RCODE rc = FERR_OK; - FLMUINT uiRecPointer; - - if (RC_OK( rc = pRec->getRecPointer( pvField, &uiRecPointer))) - { - if (bReadOnly) - { - fnPrintf( m_pHRequest, "%lu", - (unsigned long) uiRecPointer); - } - else - { - fnPrintf( m_pHRequest, - "", uiFieldCounter, - (unsigned long) uiRecPointer, MAX_FIELD_SIZE( 0)); - } - } - else - { - fnPrintf( m_pHRequest, - "** Error retrieving context field (Return Code = 0x%04X, %s) **", - (unsigned) rc, FlmErrorString( rc)); - } -} - -/**************************************************************************** -Desc: Prints out the value for the field, assuming the field is a blob field. -****************************************************************************/ -void F_WebPage::printBlobField( - FlmRecord * pRec, - void * pvField, - FLMUINT uiFieldCounter, - FLMBOOL bReadOnly) -{ - RCODE rc = FERR_OK; - FlmBlob * pBlob = NULL; - char szPath[F_PATH_MAX_SIZE]; - FLMUINT uiLen; - - if (RC_BAD( rc = pRec->getBlob( pvField, &pBlob))) - { - fnPrintf( m_pHRequest, - "** Failed to retrieve Blob object (Return Code = 0x%04X, %s) **", - (unsigned long) rc, FlmErrorString( rc)); - goto Exit; - } - - uiLen = ((FlmBlobImp *) pBlob)->getDataLength(); - if (uiLen == 0) - { - if (!bReadOnly) - { - fnPrintf( m_pHRequest, - "", uiFieldCounter, - MAX_FIELD_SIZE( 0)); - } - - goto Exit; - } - - if (RC_BAD( rc = pBlob->buildFileName( szPath))) - { - fnPrintf( m_pHRequest, - "** Failed to retrieve Blob filename (Return Code = 0x%04X, %s) **", - (unsigned) rc, FlmErrorString( rc)); - goto Exit; - } - - if (bReadOnly) - { - fnPrintf( m_pHRequest, ""); - printEncodedString( szPath, HTML_ENCODING); - fnPrintf( m_pHRequest, ""); - } - else - { - fnPrintf( m_pHRequest, - ""); - } - -Exit: - - if (pBlob) - { - pBlob->Release(); - } -} - -/**************************************************************************** -Desc: Prints out a string identifying this as a default field - error condition. -****************************************************************************/ -void F_WebPage::printDefaultField( - FlmRecord *, - void *, - FLMUINT, - FLMBOOL) -{ - fnPrintf( m_pHRequest, "**Default Field**"); -} - -/**************************************************************************** -Desc: Prints out the hidden field identifiers -****************************************************************************/ -void F_WebPage::printFieldIds( - FLMUINT uiFieldCounter, - FLMUINT uiFieldLevel, - FLMUINT uiType, - FLMUINT uiTagNum) -{ - char szTmp[20]; - - f_sprintf( szTmp, "fieldLevel%u", (unsigned) uiFieldCounter); - printHiddenField( szTmp, uiFieldLevel); - f_sprintf( szTmp, "fieldType%u", (unsigned) uiFieldCounter); - printHiddenField( szTmp, uiType); - f_sprintf( szTmp, "fieldTag%u", (unsigned) uiFieldCounter); - printHiddenField( szTmp, uiTagNum); -} - -/**************************************************************************** -Desc: Prints a table listing the fields of the supplied Log Headers. Any of - the log header pointers may be null, in which case a series of blank - entries will be created in the table for that entry. -****************************************************************************/ -void F_WebPage::printLogHeaders( - FLMBYTE * pucLastCommitted, - FLMBYTE * pucCheckpoint, - FLMBYTE * pucUncommitted) -{ - FLMBOOL bHighlight = FALSE; - - // Start the table and headings... - - printTableStart( NULL, 5, 100); - - printTableRowStart( FALSE); - printColumnHeading( "Offset (hex)"); - printColumnHeading( "Field"); - printColumnHeading( "Last Committed"); - printColumnHeading( "Checkpoint"); - printColumnHeading( "Uncommitted"); - printTableRowEnd(); - - // Fill in the table here. LOG_RFL_FILE_NUM - - printTableRowStart( bHighlight = !bHighlight); - fnPrintf( m_pHRequest, "0x%X", LOG_RFL_FILE_NUM); - fnPrintf( m_pHRequest, "Current RFL file"); - printLogFileEntryUD( pucLastCommitted, LOG_RFL_FILE_NUM); - printLogFileEntryUD( pucCheckpoint, LOG_RFL_FILE_NUM); - printLogFileEntryUD( pucUncommitted, LOG_RFL_FILE_NUM); - printTableRowEnd(); - - // LOG_RFL_LAST_TRANS_OFFSET - - printTableRowStart( bHighlight = !bHighlight); - fnPrintf( m_pHRequest, "0x%X", LOG_RFL_LAST_TRANS_OFFSET); - fnPrintf( m_pHRequest, "Current RFL offset"); - printLogFileEntryUD( pucLastCommitted, LOG_RFL_LAST_TRANS_OFFSET); - printLogFileEntryUD( pucCheckpoint, LOG_RFL_LAST_TRANS_OFFSET); - printLogFileEntryUD( pucUncommitted, LOG_RFL_LAST_TRANS_OFFSET); - printTableRowEnd(); - - // LOG_RFL_LAST_CP_FILE_NUM - - printTableRowStart( bHighlight = !bHighlight); - fnPrintf( m_pHRequest, "0x%X", LOG_RFL_LAST_CP_FILE_NUM); - fnPrintf( m_pHRequest, "Last CP RFL file"); - printLogFileEntryUD( pucLastCommitted, LOG_RFL_LAST_CP_FILE_NUM); - printLogFileEntryUD( pucCheckpoint, LOG_RFL_LAST_CP_FILE_NUM); - printLogFileEntryUD( pucUncommitted, LOG_RFL_LAST_CP_FILE_NUM); - printTableRowEnd(); - - // LOG_RFL_LAST_CP_OFFSET - - printTableRowStart( bHighlight = !bHighlight); - fnPrintf( m_pHRequest, "0x%X", LOG_RFL_LAST_CP_OFFSET); - fnPrintf( m_pHRequest, "Last CP RFL offset"); - printLogFileEntryUD( pucLastCommitted, LOG_RFL_LAST_CP_OFFSET); - printLogFileEntryUD( pucCheckpoint, LOG_RFL_LAST_CP_OFFSET); - printLogFileEntryUD( pucUncommitted, LOG_RFL_LAST_CP_OFFSET); - printTableRowEnd(); - - // LOG_ROLLBACK_EOF - - printTableRowStart( bHighlight = !bHighlight); - fnPrintf( m_pHRequest, "0x%X", LOG_ROLLBACK_EOF); - fnPrintf( m_pHRequest, "End of file"); - printLogFileEntryUD( pucLastCommitted, LOG_ROLLBACK_EOF); - printLogFileEntryUD( pucCheckpoint, LOG_ROLLBACK_EOF); - printLogFileEntryUD( pucUncommitted, LOG_ROLLBACK_EOF); - printTableRowEnd(); - - // LOG_INC_BACKUP_SEQ_NUM - - printTableRowStart( bHighlight = !bHighlight); - fnPrintf( m_pHRequest, "0x%X", LOG_INC_BACKUP_SEQ_NUM); - fnPrintf( m_pHRequest, "Incremental backup sequence number"); - printLogFileEntryUD( pucLastCommitted, LOG_INC_BACKUP_SEQ_NUM); - printLogFileEntryUD( pucCheckpoint, LOG_INC_BACKUP_SEQ_NUM); - printLogFileEntryUD( pucUncommitted, LOG_INC_BACKUP_SEQ_NUM); - printTableRowEnd(); - - // LOG_CURR_TRANS_ID - - printTableRowStart( bHighlight = !bHighlight); - fnPrintf( m_pHRequest, "0x%X", LOG_CURR_TRANS_ID); - fnPrintf( m_pHRequest, "Transaction ID"); - printLogFileEntryUD( pucLastCommitted, LOG_CURR_TRANS_ID); - printLogFileEntryUD( pucCheckpoint, LOG_CURR_TRANS_ID); - printLogFileEntryUD( pucUncommitted, LOG_CURR_TRANS_ID); - printTableRowEnd(); - - // LOG_COMMIT_COUNT - - printTableRowStart( bHighlight = !bHighlight); - fnPrintf( m_pHRequest, "0x%X", LOG_COMMIT_COUNT); - fnPrintf( m_pHRequest, "Commit count"); - printLogFileEntryUD( pucLastCommitted, LOG_COMMIT_COUNT); - printLogFileEntryUD( pucCheckpoint, LOG_COMMIT_COUNT); - printLogFileEntryUD( pucUncommitted, LOG_COMMIT_COUNT); - printTableRowEnd(); - - // LOG_PL_FIRST_CP_BLOCK_ADDR - - printTableRowStart( bHighlight = !bHighlight); - fnPrintf( m_pHRequest, "0x%X", LOG_PL_FIRST_CP_BLOCK_ADDR); - fnPrintf( m_pHRequest, "First CP block address"); - printLogFileEntryUDX( pucLastCommitted, LOG_PL_FIRST_CP_BLOCK_ADDR); - printLogFileEntryUDX( pucCheckpoint, LOG_PL_FIRST_CP_BLOCK_ADDR); - printLogFileEntryUDX( pucUncommitted, LOG_PL_FIRST_CP_BLOCK_ADDR); - printTableRowEnd(); - - // LOG_LAST_RFL_FILE_DELETED - - printTableRowStart( bHighlight = !bHighlight); - fnPrintf( m_pHRequest, "0x%X", LOG_LAST_RFL_FILE_DELETED); - fnPrintf( m_pHRequest, "Last RFL file deleted"); - printLogFileEntryUD( pucLastCommitted, LOG_LAST_RFL_FILE_DELETED); - printLogFileEntryUD( pucCheckpoint, LOG_LAST_RFL_FILE_DELETED); - printLogFileEntryUD( pucUncommitted, LOG_LAST_RFL_FILE_DELETED); - printTableRowEnd(); - - // LOG_RFL_MIN_FILE_SIZE - - printTableRowStart( bHighlight = !bHighlight); - fnPrintf( m_pHRequest, "0x%X", LOG_RFL_MIN_FILE_SIZE); - fnPrintf( m_pHRequest, "Minimum RFL file size"); - printLogFileEntryUD( pucLastCommitted, LOG_RFL_MIN_FILE_SIZE); - printLogFileEntryUD( pucCheckpoint, LOG_RFL_MIN_FILE_SIZE); - printLogFileEntryUD( pucUncommitted, LOG_RFL_MIN_FILE_SIZE); - printTableRowEnd(); - - // LOG_HDR_CHECKSUM - - printTableRowStart( bHighlight = !bHighlight); - fnPrintf( m_pHRequest, "0x%X", LOG_HDR_CHECKSUM); - fnPrintf( m_pHRequest, "Header checksum"); - printLogFileEntryUW( pucLastCommitted, LOG_HDR_CHECKSUM); - printLogFileEntryUW( pucCheckpoint, LOG_HDR_CHECKSUM); - printLogFileEntryUW( pucUncommitted, LOG_HDR_CHECKSUM); - printTableRowEnd(); - - // LOG_FLAIM_VERSION - - printTableRowStart( bHighlight = !bHighlight); - fnPrintf( m_pHRequest, "0x%X", LOG_FLAIM_VERSION); - fnPrintf( m_pHRequest, "Flaim version"); - printLogFileEntryUW( pucLastCommitted, LOG_FLAIM_VERSION); - printLogFileEntryUW( pucCheckpoint, LOG_FLAIM_VERSION); - printLogFileEntryUW( pucUncommitted, LOG_FLAIM_VERSION); - printTableRowEnd(); - - // LOG_LAST_BACKUP_TRANS_ID - - printTableRowStart( bHighlight = !bHighlight); - fnPrintf( m_pHRequest, "0x%X", LOG_LAST_BACKUP_TRANS_ID); - fnPrintf( m_pHRequest, "Last backup trans ID"); - printLogFileEntryUD( pucLastCommitted, LOG_LAST_BACKUP_TRANS_ID); - printLogFileEntryUD( pucCheckpoint, LOG_LAST_BACKUP_TRANS_ID); - printLogFileEntryUD( pucUncommitted, LOG_LAST_BACKUP_TRANS_ID); - printTableRowEnd(); - - // LOG_BLK_CHG_SINCE_BACKUP - - printTableRowStart( bHighlight = !bHighlight); - fnPrintf( m_pHRequest, "0x%X", LOG_BLK_CHG_SINCE_BACKUP); - fnPrintf( m_pHRequest, "Blocks changed since backup"); - printLogFileEntryUD( pucLastCommitted, LOG_BLK_CHG_SINCE_BACKUP); - printLogFileEntryUD( pucCheckpoint, LOG_BLK_CHG_SINCE_BACKUP); - printLogFileEntryUD( pucUncommitted, LOG_BLK_CHG_SINCE_BACKUP); - printTableRowEnd(); - - // LOG_LAST_CP_TRANS_ID - - printTableRowStart( bHighlight = !bHighlight); - fnPrintf( m_pHRequest, "0x%X", LOG_LAST_CP_TRANS_ID); - fnPrintf( m_pHRequest, "Last CP trans ID"); - printLogFileEntryUD( pucLastCommitted, LOG_LAST_CP_TRANS_ID); - printLogFileEntryUD( pucCheckpoint, LOG_LAST_CP_TRANS_ID); - printLogFileEntryUD( pucUncommitted, LOG_LAST_CP_TRANS_ID); - printTableRowEnd(); - - // LOG_PF_FIRST_BACKCHAIN - - printTableRowStart( bHighlight = !bHighlight); - fnPrintf( m_pHRequest, "0x%X", LOG_PF_FIRST_BACKCHAIN); - fnPrintf( m_pHRequest, "Backchain block address"); - if (pucLastCommitted && - FB2UD( &pucLastCommitted[LOG_PF_FIRST_BACKCHAIN]) == BT_END) - { - fnPrintf( m_pHRequest, "none"); - } - else - { - printLogFileEntryUDX( pucLastCommitted, LOG_PF_FIRST_BACKCHAIN); - } - - if (pucCheckpoint && - FB2UD( &pucCheckpoint[LOG_PF_FIRST_BACKCHAIN]) == BT_END) - { - fnPrintf( m_pHRequest, "none"); - } - else - { - printLogFileEntryUDX( pucCheckpoint, LOG_PF_FIRST_BACKCHAIN); - } - - if (pucUncommitted && - FB2UD( &pucUncommitted[LOG_PF_FIRST_BACKCHAIN]) == BT_END) - { - fnPrintf( m_pHRequest, "none"); - } - else - { - printLogFileEntryUDX( pucUncommitted, LOG_PF_FIRST_BACKCHAIN); - } - - printTableRowEnd(); - - // LOG_PF_AVAIL_BLKS - - printTableRowStart( bHighlight = !bHighlight); - fnPrintf( m_pHRequest, "0x%X", LOG_PF_AVAIL_BLKS); - fnPrintf( m_pHRequest, "Available blocks"); - if (pucLastCommitted && - FB2UD( &pucLastCommitted[LOG_PF_AVAIL_BLKS]) == BT_END) - { - fnPrintf( m_pHRequest, "none"); - } - else - { - printLogFileEntryUDX( pucLastCommitted, LOG_PF_AVAIL_BLKS); - } - - if (pucCheckpoint && FB2UD( &pucCheckpoint[LOG_PF_AVAIL_BLKS]) == BT_END) - { - fnPrintf( m_pHRequest, "none"); - } - else - { - printLogFileEntryUDX( pucCheckpoint, LOG_PF_AVAIL_BLKS); - } - - if (pucUncommitted && FB2UD( &pucUncommitted[LOG_PF_AVAIL_BLKS]) == BT_END) - { - fnPrintf( m_pHRequest, "none"); - } - else - { - printLogFileEntryUDX( pucUncommitted, LOG_PF_AVAIL_BLKS); - } - - printTableRowEnd(); - - // LOG_LOGICAL_EOF - - printTableRowStart( bHighlight = !bHighlight); - fnPrintf( m_pHRequest, "0x%X", LOG_LOGICAL_EOF); - fnPrintf( m_pHRequest, "Logical EOF"); - printLogFileEntryUD_X( pucLastCommitted, LOG_LOGICAL_EOF); - printLogFileEntryUD_X( pucCheckpoint, LOG_LOGICAL_EOF); - printLogFileEntryUD_X( pucUncommitted, LOG_LOGICAL_EOF); - printTableRowEnd(); - - // LOG_LAST_RFL_COMMIT_ID - - printTableRowStart( bHighlight = !bHighlight); - fnPrintf( m_pHRequest, "0x%X", LOG_LAST_RFL_COMMIT_ID); - fnPrintf( m_pHRequest, "Last RFL commit ID"); - printLogFileEntryUD( pucLastCommitted, LOG_LAST_RFL_COMMIT_ID); - printLogFileEntryUD( pucCheckpoint, LOG_LAST_RFL_COMMIT_ID); - printLogFileEntryUD( pucUncommitted, LOG_LAST_RFL_COMMIT_ID); - printTableRowEnd(); - - // LOG_KEEP_ABORTED_TRANS_IN_RFL - - printTableRowStart( bHighlight = !bHighlight); - fnPrintf( m_pHRequest, "0x%X", LOG_KEEP_ABORTED_TRANS_IN_RFL); - fnPrintf( m_pHRequest, "Keep aborted trans in RFL"); - printLogFileEntryBool( pucLastCommitted, LOG_KEEP_ABORTED_TRANS_IN_RFL); - printLogFileEntryBool( pucCheckpoint, LOG_KEEP_ABORTED_TRANS_IN_RFL); - printLogFileEntryBool( pucUncommitted, LOG_KEEP_ABORTED_TRANS_IN_RFL); - printTableRowEnd(); - - // LOG_PF_FIRST_BC_CNT - - printTableRowStart( bHighlight = !bHighlight); - fnPrintf( m_pHRequest, "0x%X", LOG_PF_FIRST_BC_CNT); - fnPrintf( m_pHRequest, "First BC count"); - printLogFileEntryUC( pucLastCommitted, LOG_PF_FIRST_BC_CNT); - printLogFileEntryUC( pucCheckpoint, LOG_PF_FIRST_BC_CNT); - printLogFileEntryUC( pucUncommitted, LOG_PF_FIRST_BC_CNT); - printTableRowEnd(); - - // LOG_KEEP_RFL_FILES - - printTableRowStart( bHighlight = !bHighlight); - fnPrintf( m_pHRequest, "0x%X", LOG_KEEP_RFL_FILES); - fnPrintf( m_pHRequest, "Keep RFL files"); - printLogFileEntryBool( pucLastCommitted, LOG_KEEP_RFL_FILES); - printLogFileEntryBool( pucCheckpoint, LOG_KEEP_RFL_FILES); - printLogFileEntryBool( pucUncommitted, LOG_KEEP_RFL_FILES); - printTableRowEnd(); - - // LOG_AUTO_TURN_OFF_KEEP_RFL - - printTableRowStart( bHighlight = !bHighlight); - fnPrintf( m_pHRequest, "0x%X", LOG_AUTO_TURN_OFF_KEEP_RFL); - fnPrintf( m_pHRequest, "Auto turn off keep RFL"); - printLogFileEntryBool( pucLastCommitted, LOG_AUTO_TURN_OFF_KEEP_RFL); - printLogFileEntryBool( pucCheckpoint, LOG_AUTO_TURN_OFF_KEEP_RFL); - printLogFileEntryBool( pucUncommitted, LOG_AUTO_TURN_OFF_KEEP_RFL); - printTableRowEnd(); - - // LOG_PF_NUM_AVAIL_BLKS - - printTableRowStart( bHighlight = !bHighlight); - fnPrintf( m_pHRequest, "0x%X", LOG_PF_NUM_AVAIL_BLKS); - fnPrintf( m_pHRequest, "Avail Blocks"); - printLogFileEntryUD( pucLastCommitted, LOG_PF_NUM_AVAIL_BLKS); - printLogFileEntryUD( pucCheckpoint, LOG_PF_NUM_AVAIL_BLKS); - printLogFileEntryUD( pucUncommitted, LOG_PF_NUM_AVAIL_BLKS); - printTableRowEnd(); - - // LOG_RFL_MAX_FILE_SIZE - - printTableRowStart( bHighlight = !bHighlight); - fnPrintf( m_pHRequest, "0x%X", LOG_RFL_MAX_FILE_SIZE); - fnPrintf( m_pHRequest, "Max file size"); - printLogFileEntryUD( pucLastCommitted, LOG_RFL_MAX_FILE_SIZE); - printLogFileEntryUD( pucCheckpoint, LOG_RFL_MAX_FILE_SIZE); - printLogFileEntryUD( pucUncommitted, LOG_RFL_MAX_FILE_SIZE); - printTableRowEnd(); - - // LOG_DB_SERIAL_NUM - - printTableRowStart( bHighlight = !bHighlight); - fnPrintf( m_pHRequest, "0x%X", LOG_DB_SERIAL_NUM); - fnPrintf( m_pHRequest, "DB serial number"); - printSerialNum( pucLastCommitted ? &pucLastCommitted[LOG_DB_SERIAL_NUM] : NULL); - printSerialNum( pucCheckpoint ? &pucCheckpoint[LOG_DB_SERIAL_NUM] : NULL); - printSerialNum( pucUncommitted ? &pucUncommitted[LOG_DB_SERIAL_NUM] : NULL); - printTableRowEnd(); - - // LOG_LAST_TRANS_RFL_SERIAL_NUM - - printTableRowStart( bHighlight = !bHighlight); - fnPrintf( m_pHRequest, "0x%X", LOG_LAST_TRANS_RFL_SERIAL_NUM); - fnPrintf( m_pHRequest, "Last Trans RFL serial number"); - printSerialNum( pucLastCommitted ? &pucLastCommitted[LOG_LAST_TRANS_RFL_SERIAL_NUM] : - NULL); - printSerialNum( pucCheckpoint ? &pucCheckpoint[LOG_LAST_TRANS_RFL_SERIAL_NUM] : NULL); - printSerialNum( pucUncommitted ? &pucUncommitted[LOG_LAST_TRANS_RFL_SERIAL_NUM] : NULL); - printTableRowEnd(); - - // LOG_RFL_NEXT_SERIAL_NUM - - printTableRowStart( bHighlight = !bHighlight); - fnPrintf( m_pHRequest, "0x%X", LOG_RFL_NEXT_SERIAL_NUM); - fnPrintf( m_pHRequest, "Next RFL serial number"); - printSerialNum( pucLastCommitted ? &pucLastCommitted[LOG_RFL_NEXT_SERIAL_NUM] : NULL); - printSerialNum( pucCheckpoint ? &pucCheckpoint[LOG_RFL_NEXT_SERIAL_NUM] : NULL); - printSerialNum( pucUncommitted ? &pucUncommitted[LOG_RFL_NEXT_SERIAL_NUM] : NULL); - printTableRowEnd(); - - // LOG_INC_BACKUP_SERIAL_NUM - - printTableRowStart( bHighlight = !bHighlight); - fnPrintf( m_pHRequest, "0x%X", LOG_INC_BACKUP_SERIAL_NUM); - fnPrintf( m_pHRequest, "Incremental backup serial number"); - printSerialNum( pucLastCommitted ? &pucLastCommitted[LOG_INC_BACKUP_SERIAL_NUM] : NULL); - printSerialNum( pucCheckpoint ? &pucCheckpoint[LOG_INC_BACKUP_SERIAL_NUM] : NULL); - printSerialNum( pucUncommitted ? &pucUncommitted[LOG_INC_BACKUP_SERIAL_NUM] : NULL); - printTableRowEnd(); - - // LOG_MAX_FILE_SIZE - - printTableRowStart( bHighlight = !bHighlight); - fnPrintf( m_pHRequest, "0x%X", LOG_MAX_FILE_SIZE); - fnPrintf( m_pHRequest, "Maximum file size (64K units)"); - printLogFileEntryUW( pucLastCommitted, LOG_MAX_FILE_SIZE); - printLogFileEntryUW( pucCheckpoint, LOG_MAX_FILE_SIZE); - printLogFileEntryUW( pucUncommitted, LOG_MAX_FILE_SIZE); - printTableRowEnd(); - - printTableEnd(); -} - -/******************************************************************* -Desc: -********************************************************************/ -void F_WebPage::printSerialNum( - FLMBYTE * pucSerialNum) -{ - if (pucSerialNum) - { - printTableDataStart( FALSE, JUSTIFY_LEFT); - - // fnPrintf( m_pHRequest, "0x"); - - for (int iLoop = 0; iLoop < F_SERIAL_NUM_SIZE; iLoop++) - { - fnPrintf( m_pHRequest, "%02X ", pucSerialNum[iLoop]); - } - - printTableDataEnd(); - } - else - { - fnPrintf( m_pHRequest, "-"); - } -} - -/******************************************************************* -Desc: Print a table entry as unsigned 4 digit hex minimum. -********************************************************************/ -void F_WebPage::printLogFileEntryUDX( - FLMBYTE * pucLog, - FLMUINT uiOffset) -{ - if (pucLog) - { - fnPrintf( m_pHRequest, "0x%04X", FB2UD( &pucLog[uiOffset])); - } - else - { - fnPrintf( m_pHRequest, "-"); - } -} - -/******************************************************************* -Desc: Print a table entry as unsigned decimal hex in parenthesis. -********************************************************************/ -void F_WebPage::printLogFileEntryUD_X( - FLMBYTE * pucLog, - FLMUINT uiOffset) -{ - if (pucLog) - { - printTableDataStart( TRUE, JUSTIFY_LEFT); - printCommaNumText( (FLMUINT64) FB2UD( &pucLog[uiOffset])); - fnPrintf( m_pHRequest, " (0x%X)", FB2UD( &pucLog[uiOffset])); - printTableDataEnd(); - } - else - { - fnPrintf( m_pHRequest, "-"); - } -} - -/******************************************************************* -Desc: Print a table entry as unsigned decimal. -********************************************************************/ -void F_WebPage::printLogFileEntryUD( - FLMBYTE * pucLog, - FLMUINT uiOffset) -{ - if (pucLog) - { - printCommaNum( (FLMUINT64) FB2UD( &pucLog[uiOffset]), JUSTIFY_LEFT); - } - else - { - fnPrintf( m_pHRequest, "-"); - } -} - -/******************************************************************* -Desc: Print a table entry as unsigned word. -********************************************************************/ -void F_WebPage::printLogFileEntryUW( - FLMBYTE * pucLog, - FLMUINT uiOffset) -{ - if (pucLog) - { - printCommaNum( (FLMUINT64) FB2UW( &pucLog[uiOffset]), JUSTIFY_LEFT); - } - else - { - fnPrintf( m_pHRequest, "-"); - } -} - -/******************************************************************* -Desc: Print a table entry as unsigned char. -********************************************************************/ -void F_WebPage::printLogFileEntryUC( - FLMBYTE * pucLog, - FLMUINT uiOffset) -{ - if (pucLog) - { - fnPrintf( m_pHRequest, "%u", (unsigned char) pucLog[uiOffset]); - } - else - { - fnPrintf( m_pHRequest, "-"); - } -} - -/******************************************************************* -Desc: Print a table entry as yes or no (FLMBOOL) -********************************************************************/ -void F_WebPage::printLogFileEntryBool( - FLMBYTE * pucLog, - FLMUINT uiOffset) -{ - if (pucLog) - { - printTableDataStart( TRUE, JUSTIFY_LEFT); - printYesNo( (FLMBOOL) pucLog[uiOffset]); - printTableDataEnd(); - } - else - { - fnPrintf( m_pHRequest, "-"); - } -} +//------------------------------------------------------------------------- +// Desc: Base class for monitoring code. +// Tabs: 3 +// +// Copyright (c) 2001-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: imonbase.cpp 12334 2006-01-23 12:45:35 -0700 (Mon, 23 Jan 2006) dsanders $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +#define RESP_WRITE_BUF_SIZE 1024 +#define FLM_SESSION_ID_NAME "flmsessionid" + +#define MAX_FIELD_SIZE(uiSize) \ + (uiSize > 100 ? 100 : (uiSize < 20 ? 20 : uiSize)) + +/**************************************************************************** + Desc: Outputs a javascript function that, when called, causes a new + window to open and a page to be displayed in it. +****************************************************************************/ +void F_WebPage::popupFrame( void) +{ + fnPrintf( m_pHRequest, "\n"); +} + +/****************************************************************************** +Desc: This method will extract the value of the parameter in the form of + PARAMETER=VALUE +*******************************************************************************/ +RCODE F_WebPage::ExtractParameter( + FLMUINT uiNumParams, + const char ** ppszParams, + const char * pszParamName, + FLMUINT uiParamLen, + char* pszParamValue) +{ + RCODE rc = FERR_OK; + FLMUINT uiLoop; + FLMUINT uiParamNameLen; + const char* pszTemp; + FLMBOOL bFound = FALSE; + + uiParamNameLen = f_strlen( pszParamName); + for (uiLoop = 0; uiLoop < uiNumParams; uiLoop++) + { + if( f_strncmp( (char*) ppszParams[uiLoop], pszParamName, uiParamNameLen) == 0 && + (ppszParams[uiLoop][uiParamNameLen] == '\0' || + ppszParams[uiLoop][uiParamNameLen] == '=')) + { + pszTemp = &ppszParams[uiLoop][uiParamNameLen]; + if (*pszTemp == '=') + { + // Skip past the equal sign + + pszTemp++; + + f_strncpy( pszParamValue, pszTemp, uiParamLen - 1); + + // See if the param was too long to store + + if (f_strlen( pszTemp) >= uiParamLen) + { + pszParamValue[uiParamLen] = '\0'; + rc = RC_SET( FERR_MEM); + } + } + else + { + *pszParamValue = 0; + } + + bFound = TRUE; + break; + } + } + + return (bFound ? rc : RC_SET( FERR_NOT_FOUND)); +} + +/**************************************************************************** +Desc: This method will detect the presence of a parameter in the form + PARAMETER - no value will be checked. A TRUE or FALSE value will + be returned. +*****************************************************************************/ +FLMBOOL F_WebPage::DetectParameter( + FLMUINT uiNumParams, + const char ** ppszParams, + const char * pszParamName) +{ + for (FLMUINT uiLoop = 0; uiLoop < uiNumParams; uiLoop++) + { + if (f_strncmp( (char*) ppszParams[uiLoop], pszParamName, + f_strlen( (char*) pszParamName)) == 0) + { + return (TRUE); + } + } + + return (FALSE); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_WebPage::getDatabaseHandleParam( + FLMUINT uiNumParams, + const char ** ppszParams, + F_Session * pFlmSession, + HFDB* phDb, + char* pszKey) +{ + RCODE rc = FERR_OK; + HFDB hDb; + char szTmp[ 64]; + char * pTmp; + + if (phDb) + { + *phDb = hDb = HFDB_NULL; + } + + if (pszKey) + { + *pszKey = 0; + } + + // Need to memset the first F_SESSION_DB_KEY_LEN bytes of szTmp + // because the hash lookup algorithm expects the buffer to be padded + // with zeros at the end of the key. + + f_memset( szTmp, 0, F_SESSION_DB_KEY_LEN); + + if (RC_BAD( ExtractParameter( uiNumParams, ppszParams, "dbhandle", + sizeof(szTmp), szTmp))) + { + pTmp = &szTmp[0]; + if (RC_BAD( getFormValueByName( "dbhandle", &pTmp, sizeof(szTmp), NULL))) + { + rc = RC_SET( FERR_NOT_FOUND); + goto Exit; + } + } + + if (szTmp[0]) + { + if (RC_BAD( rc = pFlmSession->getDbHandle( szTmp, &hDb))) + { + goto Exit; + } + + if (pszKey) + { + f_memcpy( pszKey, szTmp, F_SESSION_DB_KEY_LEN); + } + } + + if (phDb) + { + *phDb = hDb; + } + +Exit: + + return (rc); +} + +/**************************************************************************** +Desc: Function to display the formatted time value in the form dd hh:mm:ss.ccc +*****************************************************************************/ +void F_WebPage::FormatTime( + FLMUINT uiTimerUnits, + char * pszFormattedTime) +{ + FLMUINT uiMilli; + FLMUINT uiSec; + FLMUINT uiMin; + FLMUINT uiHr; + FLMUINT uiDays; + FLMUINT uiTemp; + + // Initialize to NULL; + + pszFormattedTime[0] = '\0'; + + // Convert the timer units to milliseconds + + FLM_TIMER_UNITS_TO_MILLI( uiTimerUnits, uiMilli); + + // Determine the number of days + + uiDays = uiMilli / 86400000; + uiTemp = uiMilli % 86400000; + + // Now the hours + + uiHr = uiTemp / 3600000; + uiTemp = uiTemp % 3600000; + + // Determine the minutes + + uiMin = uiTemp / 60000; + uiTemp = uiTemp % 60000; + + // Determine seconds + + uiSec = uiTemp / 1000; + + // Determine the milliseconds + + uiMilli = uiTemp % 1000; + + // Put it all together - hh:mm:ss + + f_sprintf( (char *) pszFormattedTime, "%ld %2.2ld:%2.2ld:%2.2ld.%3.3ld", + uiDays, uiHr, uiMin, uiSec, uiMilli); +} + +/**************************************************************************** + Desc: Procedure to generate the HTML page that displays the usage statistics + structure. +****************************************************************************/ +RCODE F_WebPage::writeUsage( + FLM_CACHE_USAGE* pUsage, + FLMBOOL bRefresh, + const char* pszURL, + const char* pszTitle) +{ + RCODE rc = FERR_OK; + FLMBOOL bHighlight = FALSE; + char szTemp[100]; + + stdHdr(); + fnPrintf( m_pHRequest, HTML_DOCTYPE); + fnPrintf( m_pHRequest, "\n"); + + // Setup the page header & refresh control... Assuming the the first + // parameter ?Usage is already contained in the pszURL string. + + if (bRefresh) + { + fnPrintf( m_pHRequest, "" + "" + "%s\n", m_pszURLString, pszURL, pszTitle); + printStyle(); + fnPrintf( m_pHRequest, "\n\n"); + + f_sprintf( (char*) szTemp, "Stop Auto-refresh", + m_pszURLString, pszURL); + } + else + { + fnPrintf( m_pHRequest, "%s\n", pszTitle); + printStyle(); + fnPrintf( m_pHRequest, "\n\n"); + + f_sprintf( (char*) szTemp, + "Start Auto-refresh (5 sec.)", + m_pszURLString, pszURL); + } + + // Begin the table + + printTableStart( (char*) pszTitle, 4); + printTableRowStart(); + printColumnHeading( "", JUSTIFY_LEFT, FLM_IMON_COLOR_PUTTY_1, 4, 1, FALSE); + fnPrintf( m_pHRequest, "Refresh, ", m_pszURLString, pszURL); + fnPrintf( m_pHRequest, "%s\n", szTemp); + printColumnHeadingClose(); + printTableRowEnd(); + + // Write out the table headings. + + printTableRowStart(); + printColumnHeading( "Byte Offset (hex)"); + printColumnHeading( "Field Name"); + printColumnHeading( "Byte Offset"); + printColumnHeading( "Value"); + printTableRowEnd(); + + // uiMaxBytes + + printHTMLUint( (char*) "uiMaxBytes", (char*) "FLMUINT", (void*) pUsage, + (void*) &pUsage->uiMaxBytes, pUsage->uiMaxBytes, + (bHighlight = ~bHighlight)); + + // uiTotalBytesAllocated + + printHTMLUint( (char*) "uiTotalBytesAllocated", (char*) "FLMUINT", + (void*) pUsage, (void*) &pUsage->uiTotalBytesAllocated, + pUsage->uiTotalBytesAllocated, (bHighlight = ~bHighlight)); + + // uiCount + + printHTMLUint( (char*) "uiCount", (char*) "FLMUINT", (void*) pUsage, + (void*) &pUsage->uiCount, pUsage->uiCount, + (bHighlight = ~bHighlight)); + + // uiOldVerCount + + printHTMLUint( (char*) "uiOldVerCount", (char*) "FLMUINT", (void*) pUsage, + (void*) &pUsage->uiOldVerCount, pUsage->uiOldVerCount, + (bHighlight = ~bHighlight)); + + // uiOldVerBytes + + printHTMLUint( (char*) "uiOldVerBytes", (char*) "FLMUINT", (void*) pUsage, + (void*) &pUsage->uiOldVerBytes, pUsage->uiOldVerBytes, + (bHighlight = ~bHighlight)); + + // uiCacheHits + + printHTMLUint( (char*) "uiCacheHits", (char*) "FLMUINT", (void*) pUsage, + (void*) &pUsage->uiCacheHits, pUsage->uiCacheHits, + (bHighlight = ~bHighlight)); + + // uiCacheHitLooks + + printHTMLUint( (char*) "uiCacheHitLooks", (char*) "FLMUINT", (void*) pUsage, + (void*) &pUsage->uiCacheHitLooks, pUsage->uiCacheHitLooks, + (bHighlight = ~bHighlight)); + + // uiCacheFaults + + printHTMLUint( (char*) "uiCacheFaults", (char*) "FLMUINT", (void*) pUsage, + (void*) &pUsage->uiCacheFaults, pUsage->uiCacheFaults, + (bHighlight = ~bHighlight)); + + // uiCacheFaultLooks + + printHTMLUint( (char*) "uiCacheFaultLooks", (char*) "FLMUINT", + (void*) pUsage, (void*) &pUsage->uiCacheFaultLooks, + pUsage->uiCacheFaultLooks, (bHighlight = ~bHighlight)); + + printTableEnd(); + + fnPrintf( m_pHRequest, "
\n"); + fnPrintf( m_pHRequest, + "
\n"); + fnPrintf( m_pHRequest, "
\n"); + + fnPrintf( m_pHRequest, "\n"); + + fnEmit(); + + return (rc); +} + +/********************************************************************* +Desc: This function prints a linkable field in HTML +*********************************************************************/ +void F_WebPage::printHTMLLink( + const char * pszName, + const char * pszType, + void * pvBase, + void * pvAddress, + void * pvValue, + const char * pszLink, + FLMBOOL bHighlight) +{ + char szAddress[20]; + char szOffset[8]; + + printOffset( pvBase, pvAddress, szOffset); + printTableRowStart( bHighlight); + fnPrintf( m_pHRequest, TD_s, szOffset); // Field offset + + if (pvValue) + { + printAddress( pvValue, szAddress); + fnPrintf( m_pHRequest, TD_a_s_s, pszLink, pszName); // Link & Name + fnPrintf( m_pHRequest, TD_s, pszType); // Type + fnPrintf( m_pHRequest, TD_a_s_s, pszLink, szAddress); // Link & Value + } + else + { + fnPrintf( m_pHRequest, TD_s, pszName); + fnPrintf( m_pHRequest, TD_s, pszType); + fnPrintf( m_pHRequest, TD_s, "Null"); + } + + printTableRowEnd(); +} + +/********************************************************************* +Desc: This function prints a text field in HTML +*********************************************************************/ +void F_WebPage::printHTMLString( + const char * pszName, + const char * pszType, + void * pvBase, + void * pvAddress, + const char * pszValue, + FLMBOOL bHighlight) +{ + char szOffset[8]; + + printOffset( pvBase, pvAddress, szOffset); + printTableRowStart( bHighlight); + fnPrintf( m_pHRequest, TD_s, szOffset); // Field offset + fnPrintf( m_pHRequest, TD_s, pszName); // Name + fnPrintf( m_pHRequest, TD_s, pszType); // Type + fnPrintf( m_pHRequest, TD_s, pszValue); // Value + printTableRowEnd(); +} + +/********************************************************************* +Desc: This function prints a unsigned long (FLMUINT) field in HTML +*********************************************************************/ +void F_WebPage::printHTMLUint( + const char * pszName, + const char * pszType, + void * pvBase, + void * pvAddress, + FLMUINT uiValue, + FLMBOOL bHighlight) +{ + char szOffset[8]; + + printOffset( pvBase, pvAddress, szOffset); + printTableRowStart( bHighlight); + fnPrintf( m_pHRequest, TD_s, szOffset); // Field offset + fnPrintf( m_pHRequest, TD_s, pszName); // Name + fnPrintf( m_pHRequest, TD_s, pszType); // Type + fnPrintf( m_pHRequest, TD_ui, uiValue); // Value + printTableRowEnd(); +} + +/********************************************************************* +Desc: This function prints a signed long (FLMINT) field in HTML +*********************************************************************/ +void F_WebPage::printHTMLInt( + const char * pszName, + const char * pszType, + void * pvBase, + void * pvAddress, + FLMINT iValue, + FLMBOOL bHighlight) +{ + char szOffset[8]; + + printOffset( pvBase, pvAddress, szOffset); + printTableRowStart( bHighlight); + fnPrintf( m_pHRequest, TD_s, szOffset); // Field offset + fnPrintf( m_pHRequest, TD_s, pszName); // Name + fnPrintf( m_pHRequest, TD_s, pszType); // Type + fnPrintf( m_pHRequest, TD_i, iValue); // Value + printTableRowEnd(); +} + +/********************************************************************* +Desc: This function prints a unsigned long field in HTML +*********************************************************************/ +void F_WebPage::printHTMLUlong( + const char * pszName, + const char * pszType, + void * pvBase, + void * pvAddress, + unsigned long luValue, + FLMBOOL bHighlight) +{ + char szOffset[8]; + + printOffset( pvBase, pvAddress, szOffset); + printTableRowStart( bHighlight); + fnPrintf( m_pHRequest, TD_s, szOffset); // Field offset + fnPrintf( m_pHRequest, TD_s, pszName); // Name + fnPrintf( m_pHRequest, TD_s, pszType); // Type + fnPrintf( m_pHRequest, TD_lu, luValue); // Value + printTableRowEnd(); +} + +/********************************************************************* +Desc: This function takes the name of a form field, and returns a + pointer to it. This will allocate the buffer returned. The + calling function is responsible for freeing that buffer. +*********************************************************************/ +RCODE F_WebPage::getFormValueByName( + const char * pszValueTag, + char ** ppszBuf, + FLMUINT uiBufLen, + FLMUINT * puiDataLen) +{ + RCODE rc = FERR_OK; + char szTag[128]; + char * pszValue; + FLMUINT uiLen; + FLMBOOL bFreeFormData = FALSE; + FLMBOOL bFreeUserData = FALSE; + + if (puiDataLen) + { + *puiDataLen = 0; + } + +#ifdef FLM_DEBUG + if (!uiBufLen) + { + flmAssert( ppszBuf && *ppszBuf == NULL); + } +#endif + + if (f_strlen( pszValueTag) + 1 >= sizeof(szTag)) + { + flmAssert( 0); + rc = RC_SET( FERR_MEM); + goto Exit; + } + + f_sprintf( (char*) szTag, "%s=", pszValueTag); + + if (!m_pszFormData) + { + char * pszContentLength; + FLMUINT uiContentLength; + + // First we need to determine how much form data there is. + + if ((pszContentLength = (char*) fnReqHdrValue( "Content-Length")) == NULL) + { + rc = RC_SET( FERR_NOT_FOUND); + goto Exit; + } + + if ((uiContentLength = f_atoi( pszContentLength)) == 0) + { + rc = RC_SET( FERR_NOT_FOUND); + goto Exit; + } + + // Now allocate a buffer to hold the form data + + if (RC_BAD( rc = f_alloc( uiContentLength + 1, &m_pszFormData))) + { + goto Exit; + } + + bFreeFormData = TRUE; + + if (fnRecvBuffer( m_pszFormData, (size_t*) &uiContentLength) != 0) + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + + m_pszFormData[uiContentLength] = 0; + bFreeFormData = FALSE; + } + + // Now, parse through the buffer until we find the field we are + // looking for. The data is in the form name=value:name=value... + + if ((pszValue = f_strstr( m_pszFormData, szTag)) != NULL) + { + pszValue += f_strlen( szTag); + for (uiLen = 0; + pszValue[uiLen] && pszValue[uiLen] != ':' && pszValue[uiLen] != '&'; + uiLen++); + + if (ppszBuf) + { + if (!uiBufLen) + { + uiBufLen = uiLen + 1; + bFreeUserData = TRUE; + *ppszBuf = NULL; + if (RC_BAD( rc = f_alloc( uiBufLen, ppszBuf))) + { + goto Exit; + } + } + + if (uiLen >= uiBufLen) + { + rc = RC_SET( FERR_CONV_DEST_OVERFLOW); + goto Exit; + } + + f_memcpy( *ppszBuf, pszValue, uiLen); + (*ppszBuf)[uiLen] = 0; + bFreeUserData = FALSE; + } + + if (puiDataLen) + { + *puiDataLen = uiLen + 1; + } + + goto Exit; + } + else + { + rc = RC_SET( FERR_NOT_FOUND); + goto Exit; + } + +Exit: + + if (bFreeFormData) + { + f_free( &m_pszFormData); + } + + if (bFreeUserData && *ppszBuf) + { + f_free( ppszBuf); + } + + return (rc); +} + +/**************************************************************************** +Desc: Prints the standard style sheet +****************************************************************************/ +void F_WebPage::printStyle(void) +{ + fnPrintf( m_pHRequest, + "\n", + m_pszURLString); +} + +/**************************************************************************** +Desc: Outputs a column heading using elements from the standard style sheet +****************************************************************************/ +void F_WebPage::printColumnHeading( + const char * pszHeading, + JustificationType eJustification, + const char * pszBackground, + FLMUINT uiColSpan, + FLMUINT uiRowSpan, + FLMBOOL bClose, + FLMUINT uiWidth) +{ + fnPrintf( m_pHRequest, + "\n"); + + if (pszHeading) + { + printEncodedString( pszHeading); + } + + if (bClose) + { + fnPrintf( m_pHRequest, "\n"); + } +} + +/**************************************************************************** +Desc: Closes a column heading +****************************************************************************/ +void F_WebPage::printColumnHeadingClose(void) +{ + fnPrintf( m_pHRequest, "\n"); +} + +/**************************************************************************** +Desc: Encodes a string for rendering in an HTML page or for inclusion in + an URL +****************************************************************************/ +void F_WebPage::printEncodedString( + const char * pszString, + FStringEncodeType eEncodeType, + FLMBOOL bMapSlashes) +{ + char ucChar; + + while ((ucChar = *pszString) != 0) + { + if ((ucChar >= '0' && ucChar <= '9') || + (ucChar >= 'A' && ucChar <= 'Z') || + (ucChar >= 'a' && ucChar <= 'z') || + ucChar == '_' || + ( + eEncodeType == URL_PATH_ENCODING && + (ucChar == '.' || (bMapSlashes && (ucChar == '/' || ucChar == '\\'))) + )) + { + if (ucChar == '\\') + { + ucChar = '/'; + } + + fnPrintf( m_pHRequest, "%c", ucChar); + } + else if (eEncodeType == URL_PATH_ENCODING) + { + fnPrintf( m_pHRequest, "%%%02X", (unsigned) ucChar); + } + else if (eEncodeType == URL_QUERY_ENCODING) + { + if (ucChar == ' ') + { + ucChar = '+'; + } + + fnPrintf( m_pHRequest, "%%%02X", (unsigned) ucChar); + } + else // HTML encoding + { + fnPrintf( m_pHRequest, "&#%u;", (unsigned) ucChar); + } + + pszString++; + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_WebPage::printDocStart( + const char * pszTitle, + FLMBOOL bPrintTitle, + FLMBOOL bStdHeader, + const char * pszBackground) +{ + if (bStdHeader) + { + stdHdr(); + } + + fnPrintf( m_pHRequest, HTML_DOCTYPE); + fnPrintf( m_pHRequest, "\n"); + fnPrintf( m_pHRequest, "\n"); + printRecordStyle(); + printStyle(); + fnPrintf( m_pHRequest, "Database iMonitor - "); + printEncodedString( pszTitle); + fnPrintf( m_pHRequest, "\n"); + fnPrintf( m_pHRequest, "\n"); + fnPrintf( m_pHRequest, "\n", + pszBackground ? pszBackground : "white"); + + if (bPrintTitle) + { + printTableStart( pszTitle, 1); + printTableEnd(); + fnPrintf( m_pHRequest, "
\n"); + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_WebPage::printDocEnd(void) +{ + fnPrintf( m_pHRequest, "\n"); + fnPrintf( m_pHRequest, "\n"); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_WebPage::printMenuReload(void) +{ + fnPrintf( m_pHRequest, "\n"); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_WebPage::printTableStart( + const char* pszTitle, + FLMUINT uiColumns, + FLMUINT uiWidthFactor) +{ + fnPrintf( m_pHRequest, "\n"); + + if (pszTitle) + { + printTableRowStart(); + fnPrintf( m_pHRequest, ""); + printTableRowEnd(); + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_WebPage::printTableEnd(void) +{ + fnPrintf( m_pHRequest, "
\n"); + printEncodedString( pszTitle); + fnPrintf( m_pHRequest, "
\n"); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_WebPage::printTableRowStart( + FLMBOOL bHighlight) +{ + fnPrintf( m_pHRequest, "\n"); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_WebPage::printTableRowEnd(void) +{ + fnPrintf( m_pHRequest, "\n"); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_WebPage::printTableDataStart( + FLMBOOL bNoWrap, + JustificationType eJustification, + FLMUINT uiWidth) +{ + fnPrintf( m_pHRequest, "\n"); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_WebPage::printTableDataEnd(void) +{ + fnPrintf( m_pHRequest, "\n"); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_WebPage::printTableDataEmpty(void) +{ + fnPrintf( m_pHRequest, " "); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_WebPage::printErrorPage( + RCODE rc, + FLMBOOL bStdHeader, + const char * pszWhat) +{ + printDocStart( "Error", TRUE, bStdHeader); + + fnPrintf( m_pHRequest, "

\n"); + fnPrintf( m_pHRequest, "%s
%s (0x%04X).\n", pszWhat, FlmErrorString( rc), + (unsigned) rc); + fnPrintf( m_pHRequest, "

\n"); + + printDocEnd(); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_WebPage::printErrorPage( + const char * pszErrStr, + const char * pszErrStr2, + FLMBOOL bStdHeader) +{ + printDocStart( "Error", TRUE, bStdHeader); + + fnPrintf( m_pHRequest, "

\n"); + fnPrintf( m_pHRequest, "%s\n", pszErrStr); + if (pszErrStr2 && *pszErrStr2) + { + fnPrintf( m_pHRequest, "
%s\n", pszErrStr2); + } + + fnPrintf( m_pHRequest, "

\n"); + + printDocEnd(); +} + +/**************************************************************************** +Desc: Start an input form +****************************************************************************/ +void F_WebPage::printStartInputForm( + const char * pszFormName, + const char * pszPage, + FLMUINT uiFormValue) +{ + fnPrintf( m_pHRequest, + "
\n" + "\n", + pszFormName, m_pszURLString, pszPage, (unsigned) uiFormValue); +} + +/**************************************************************************** +Desc: End an input form +****************************************************************************/ +void F_WebPage::printEndInputForm(void) +{ + fnPrintf( m_pHRequest, "
"); +} + +/**************************************************************************** +Desc: Generic function to output HTML that gererates a button +****************************************************************************/ +void F_WebPage::printButton( + const char * pszContents, + ButtonTypes eBType, + const char * pszName, + const char * pszValue, + const char * pszExtra, + FLMBOOL bDisabled, + FLMBYTE ucAccessKey, + FLMUINT uiTabIndex) +{ + fnPrintf( m_pHRequest, "\n", + (char*) (pszContents ? pszContents : "")); +} + + +/**************************************************************************** +Desc: Format and output date. +****************************************************************************/ +void F_WebPage::printDate( + FLMUINT uiGMTTime, + char * pszBuffer) +{ + F_TMSTAMP timeStamp; + FLMUINT uiLocalTime; + char * pszAmPm; + const char * pszMonth; + + uiLocalTime = (FLMUINT) (uiGMTTime - f_timeGetLocalOffset()); + f_timeSecondsToDate( uiLocalTime, &timeStamp); + + pszAmPm = (char*) ((timeStamp.hour >= 12) ? (char*) "pm" : (char*) "am"); + if (timeStamp.hour > 12) + { + timeStamp.hour -= 12; + } + + if (timeStamp.hour == 0) + { + timeStamp.hour = 12; + } + + switch (timeStamp.month) + { + case 0: + pszMonth = "Jan"; + break; + case 1: + pszMonth = "Feb"; + break; + case 2: + pszMonth = "Mar"; + break; + case 3: + pszMonth = "Apr"; + break; + case 4: + pszMonth = "May"; + break; + case 5: + pszMonth = "Jun"; + break; + case 6: + pszMonth = "Jul"; + break; + case 7: + pszMonth = "Aug"; + break; + case 8: + pszMonth = "Sep"; + break; + case 9: + pszMonth = "Oct"; + break; + case 10: + pszMonth = "Nov"; + break; + default: + case 11: + pszMonth = "Dec"; + break; + } + + if (pszBuffer != NULL) + { + f_sprintf( (char*) pszBuffer, "%s %u, %u %u:%02u:%02u %s", pszMonth, + (unsigned) timeStamp.day, (unsigned) timeStamp.year, + (unsigned) timeStamp.hour, (unsigned) timeStamp.minute, + (unsigned) timeStamp.second, pszAmPm); + } + else + { + fnPrintf( m_pHRequest, "%s %u, %u %u:%02u:%02u %s", pszMonth, + (unsigned) timeStamp.day, (unsigned) timeStamp.year, + (unsigned) timeStamp.hour, (unsigned) timeStamp.minute, + (unsigned) timeStamp.second, pszAmPm); + } +} + +/**************************************************************************** +Desc: Outputs a Yes or No value based on the passed-in boolean +****************************************************************************/ +void F_WebPage::printYesNo( + FLMBOOL bYes) +{ + fnPrintf( m_pHRequest, "%s", bYes ? "Yes" : "No"); +} + +/**************************************************************************** +Desc: Outputs a number with commas, for easier reading. +****************************************************************************/ +void F_WebPage::printCommaNumText( + FLMUINT64 ui64Num) +{ + FLMUINT uiTerm; + FLMUINT64 ui64Divisor = 1; + FLMBOOL bFirstPass = TRUE; + + while ((FLMUINT64) (ui64Num / (ui64Divisor * (FLMUINT64) 1000))) + { + ui64Divisor *= 1000; + } + + while (ui64Divisor) + { + uiTerm = (FLMUINT) (ui64Num / ui64Divisor); + ui64Num -= ((FLMUINT64) uiTerm) * ui64Divisor; + + if (bFirstPass) + { + fnPrintf( m_pHRequest, "%u", (unsigned) uiTerm); + bFirstPass = FALSE; + } + else + { + fnPrintf( m_pHRequest, "%03u", (unsigned) uiTerm); + } + + if ((ui64Divisor /= (FLMUINT64) 1000) > (FLMUINT64) 0) + { + fnPrintf( m_pHRequest, ","); + } + } +} + +/**************************************************************************** +Desc: Outputs a number with commas, for easier reading. +****************************************************************************/ +void F_WebPage::printCommaNum( + FLMUINT64 ui64Num, + JustificationType eJustify, + FLMBOOL bChangedValue) +{ + printTableDataStart( TRUE, eJustify); + if (bChangedValue) + { + fnPrintf( m_pHRequest, ""); + } + + printCommaNumText( ui64Num); + + if (bChangedValue) + { + fnPrintf( m_pHRequest, ""); + } + + printTableDataEnd(); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_WebPage::acquireSession(void) +{ + RCODE rc = FERR_OK; + FLMBOOL bHttpSessionMutexLocked = FALSE; + FLMUINT uiSize; + void * pvHttpSession = NULL; + char szSessionKey[F_SESSION_KEY_LEN]; + + m_pFlmSession = NULL; + + if (!gv_FlmSysData.HttpConfigParms.fnAcquireSession) + { + rc = RC_SET( FERR_NOT_IMPLEMENTED); + goto Exit; + } + + if ((pvHttpSession = fnAcquireSession()) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + f_mutexLock( gv_FlmSysData.hHttpSessionMutex); + bHttpSessionMutexLocked = TRUE; + + uiSize = sizeof(szSessionKey); + if (fnGetSessionValue( pvHttpSession, FLM_SESSION_ID_NAME, + (void*) szSessionKey, (size_t*) &uiSize) != 0) + { +CreateSession: + + if (RC_BAD( rc = gv_FlmSysData.pSessionMgr->createSession( &m_pFlmSession))) + { + goto Exit; + } + + fnSetSessionValue( pvHttpSession, FLM_SESSION_ID_NAME, + m_pFlmSession->getKey(), sizeof(szSessionKey)); + } + else + { + if (RC_BAD( rc = gv_FlmSysData.pSessionMgr->getSession( szSessionKey, + &m_pFlmSession))) + { + if (rc == FERR_NOT_FOUND) + { + goto CreateSession; + } + } + } + +Exit: + + if (RC_BAD( rc)) + { + if (m_pFlmSession) + { + releaseSession(); + } + } + + if (pvHttpSession) + { + fnReleaseSession( pvHttpSession); + } + + if (bHttpSessionMutexLocked) + { + f_mutexUnlock( gv_FlmSysData.hHttpSessionMutex); + } + + return (rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_WebPage::releaseSession(void) +{ + if (m_pFlmSession) + { + gv_FlmSysData.pSessionMgr->releaseSession( &m_pFlmSession); + m_pFlmSession = NULL; + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_WebPage::printSpaces( + FLMUINT uiCount) +{ + while (uiCount--) + { + fnPrintf( m_pHRequest, " "); + } +} + +/**************************************************************************** +Desc: Outputs elapsed milliseconds as seconds.milli. The optional parameter + pszBuffer will cause the time to be written to pszBuffer instead of the + web page. That way it can be incorporated into more complex structures + if desired. +****************************************************************************/ +void F_WebPage::printElapTime( + FLMUINT64 ui64ElapTime, + char* pszBuffer, + JustificationType eJustify, + FLMBOOL bTimeIsMilli) +{ + FLMUINT uiHours; + FLMUINT uiMinutes; + FLMUINT uiSeconds; + FLMUINT uiMilli = 0; + + if (bTimeIsMilli) + { + uiHours = (FLMUINT) (ui64ElapTime / (FLMUINT64) (1000 * 3600)); + uiMinutes = (FLMUINT) ((ui64ElapTime / (FLMUINT64) (1000 * 60)) % (FLMUINT64) 60); + uiSeconds = (FLMUINT) ((ui64ElapTime / (FLMUINT64) 1000) % (FLMUINT64) 60); + uiMilli = (FLMUINT) (ui64ElapTime % (FLMUINT64) 1000); + } + else + { + uiHours = (FLMUINT) (ui64ElapTime / (FLMUINT64) 3600); + uiMinutes = (FLMUINT) ((ui64ElapTime / (FLMUINT64) 60) % (FLMUINT64) 60); + uiSeconds = (FLMUINT) (ui64ElapTime % (FLMUINT64) 60); + } + + if (!pszBuffer) + { + printTableDataStart( TRUE, eJustify); + } + + if (pszBuffer) + { + f_sprintf( (char*) pszBuffer, "%02u:%02u:%02u", (unsigned) uiHours, + (unsigned) uiMinutes, (unsigned) uiSeconds); + } + else + { + fnPrintf( m_pHRequest, "%02u:%02u:%02u", (unsigned) uiHours, + (unsigned) uiMinutes, (unsigned) uiSeconds); + } + + if (bTimeIsMilli) + { + if (pszBuffer) + { + char szTemp[5]; + + f_sprintf( szTemp, ".%03u", (unsigned) uiMilli); + f_strncat( (char*) pszBuffer, szTemp, 4); + } + else + { + fnPrintf( m_pHRequest, ".%03u", (unsigned) uiMilli); + } + } + + if (!pszBuffer) + { + printTableDataEnd(); + } +} + +/**************************************************************************** +Desc: This function will output a form in an already existing page that will + present a formatted display of the record passed in. An optional parameter + bReadOnly will determine if the form should allow editing of the record + or not. The default value is TRUE (No editing capability). A null may + be passed in for the FlmRecord pointer, in which case an empty display + will be created. Normally, a bReadOnly value of false would accompany + a null record so that a record can be created. The printRecordStyle() + must be called in the header section of the page prior to calling this + function. Multiple calls may be made to display multiple records per page. + The puiContext parameter must be initialized to zero before the first + (and possibly the only) call, otherwise the scripts need to run this page + will not be loaded. +****************************************************************************/ +void F_WebPage::printRecord( + const char * pszDbKey, + FlmRecord * pRec, + F_NameTable * pNameTable, + FLMUINT * puiContext, + FLMBOOL bReadOnly, + FLMUINT uiSelectedField, + FLMUINT uiFlags) +{ + #define SP " " + + FLMBOOL bEmpty = FALSE; + FLMUINT uiContainer; + FLMUINT uiDrn; + void * pvField; + FLMUINT uiFieldCounter; + FLMUINT uiFldCnt; + FLMUINT uiTagNum; + FLMUINT uiLevel; + FLMUINT uiType; + FLMUINT uiContext = 0; + char szNameBuf[128]; + + flmAssert( pNameTable); + + if (puiContext) + { + uiContext = *puiContext; + (*puiContext)++; + } + + // See if we need to write out the scripts + + if (uiContext == 0) + { + printRecordScripts(); + } + + if (!pRec) + { + bEmpty = TRUE; + uiDrn = 0; + uiContainer = 0; + } + else + { + + // Get the Drn & Container + + uiDrn = pRec->getID(); + uiContainer = pRec->getContainerID(); + } + + // Count the fields + + uiFldCnt = 0; + if (pRec != NULL) + { + pvField = pRec->root(); + while (pvField) + { + pvField = pRec->next( pvField); + uiFldCnt++; + } + } + + // Begin the form that displays the record data (if any) + + fnPrintf( m_pHRequest, + "
\n", + uiContext, gv_FlmSysData.HttpConfigParms.pszURLString); + printHiddenField( "ReadOnly", (char*) (bReadOnly ? "TRUE" : "FALSE")); + + if (pszDbKey) + { + printHiddenField( "dbhandle", (char*) (pszDbKey)); + } + + printHiddenField( (char*) "Action", "none"); + printHiddenField( (char*) "FieldLevel", (FLMUINT) 0); + printHiddenField( (char*) "FieldNumber", (FLMUINT) 0); + printHiddenField( (char*) "FieldCount", uiFldCnt); + + // Print out the block that displays the DRN and Container list + + fnPrintf( m_pHRequest, "
\n"); + fnPrintf( m_pHRequest, + "\n"); + fnPrintf( m_pHRequest, "\n\n\n", + uiDrn, pszDbKey == NULL ? "disabled" : ""); + + if (pszDbKey != NULL) + { + fnPrintf( m_pHRequest, "\n\n\n"); + } + + fnPrintf( m_pHRequest, "\n\n\n", uiContainer); + } + + fnPrintf( m_pHRequest, "\n\n"); + + if (pszDbKey != NULL) + { + + // Print out the field list drop down box. + + fnPrintf( m_pHRequest, "\n\n\n"); + + // Print out the Add, Modify, Delete action buttons. + + fnPrintf( m_pHRequest, + "\n"); + } + + fnPrintf( m_pHRequest, "
DRN  
Flags \n"); + printRetrievalFlagsPulldown( uiFlags); + fnPrintf( m_pHRequest, "
Container \n"); + if (pszDbKey != NULL) + { + printContainerPulldown( pNameTable, uiContainer); + } + else + { + fnPrintf( m_pHRequest, + " 
Field list "); + printFieldPulldown( pNameTable, uiSelectedField); + fnPrintf( m_pHRequest, "
", uiContext); + if (pRec != NULL) + { + if (!bReadOnly) + { + fnPrintf( m_pHRequest, "", uiContext); + fnPrintf( m_pHRequest, "", uiContext); + } + + fnPrintf( m_pHRequest, "", uiContext); + } + + fnPrintf( m_pHRequest, "", uiContext); + if (pRec != NULL && bReadOnly) + { + fnPrintf( m_pHRequest, "", uiContext); + } + + fnPrintf( m_pHRequest, "
\n
\n"); + + // Print out the record fields (if there are any) + + if (pRec != NULL) + { + fnPrintf( m_pHRequest, "
\n"); + if (!bReadOnly) + { + fnPrintf( m_pHRequest, "", uiContext); + if (uiFldCnt > 1) + { + fnPrintf( m_pHRequest, "", uiContext); + fnPrintf( m_pHRequest, "", uiContext); + fnPrintf( m_pHRequest, "\n", uiContext); + } + } + + fnPrintf( m_pHRequest, "
\n");
+
+		// Now for the actual data. Start with the root field.
+
+		pvField = pRec->root();
+
+		uiFieldCounter = 0;
+
+		while (pvField)
+		{
+			uiTagNum = pRec->getFieldID( pvField);
+			uiLevel = pRec->getLevel( pvField);
+			uiType = pRec->getDataType( pvField);
+
+			if (uiLevel != 0 && !bReadOnly)
+			{
+				fnPrintf( m_pHRequest,
+							"",
+						uiFieldCounter, uiContext, uiFieldCounter, uiLevel);
+			}
+
+			pNameTable->getFromTagNum( uiTagNum, NULL, szNameBuf, sizeof(szNameBuf));
+			printSpaces( uiLevel + 5);
+
+			fnPrintf( m_pHRequest, "%s%d%s%s%s", SP,
+						uiLevel, SP, szNameBuf, SP);
+
+			if (pRec->getDataLength( pvField))
+			{
+				switch (uiType)
+				{
+					case FLM_TEXT_TYPE:
+						printTextField( pRec, pvField, uiFieldCounter, bReadOnly);
+						break;
+					case FLM_NUMBER_TYPE:
+						printNumberField( pRec, pvField, uiFieldCounter, bReadOnly);
+						break;
+					case FLM_BINARY_TYPE:
+						printBinaryField( pRec, pvField, uiFieldCounter, bReadOnly);
+						break;
+					case FLM_CONTEXT_TYPE:
+						printContextField( pRec, pvField, uiFieldCounter, bReadOnly);
+						break;
+					case FLM_BLOB_TYPE:
+						printBlobField( pRec, pvField, uiFieldCounter, bReadOnly);
+						break;
+					default:
+						printDefaultField( pRec, pvField, uiFieldCounter, bReadOnly);
+						break;
+				}
+			}
+			else if (!bReadOnly)
+			{
+				fnPrintf( m_pHRequest,
+							"",
+						uiFieldCounter, MAX_FIELD_SIZE( 0));
+			}
+
+			// Print the hidden field Ids
+
+			printFieldIds( uiFieldCounter, uiLevel, uiType, uiTagNum);
+			fnPrintf( m_pHRequest, "\n");
+
+			pvField = pRec->next( pvField);
+			uiFieldCounter++;
+		}
+
+		fnPrintf( m_pHRequest, "
\n
\n
\n"); + } + + fnPrintf( m_pHRequest, "
\n"); + + return; +} + + +/**************************************************************************** +Desc: Prints out a style sheet specific to displaying records. +****************************************************************************/ +void F_WebPage::printRecordStyle(void) +{ + fnPrintf( m_pHRequest, "\n"); +} + +/**************************************************************************** +Desc: Prints out the required scripts for displaying and updating records. +****************************************************************************/ +void F_WebPage::printRecordScripts( void) +{ + fnPrintf( m_pHRequest, "\n"); +} + +/**************************************************************************** +Desc: Prints out a hidden field with a character string value +****************************************************************************/ +void F_WebPage::printHiddenField( + const char * pszName, + const char * pszValue) +{ + fnPrintf( m_pHRequest, "", + pszName, pszValue); +} + +// +/**************************************************************************** +Desc: Prints out a hidden with an unsigned long value +****************************************************************************/ +void F_WebPage::printHiddenField( + const char * pszName, + FLMUINT uiValue) +{ + fnPrintf( m_pHRequest, "", + pszName, (unsigned) uiValue); +} + +// +/**************************************************************************** +Desc: Prints out the value for the field, assuming the field is a text field. +****************************************************************************/ +void F_WebPage::printTextField( + FlmRecord * pRec, + void * pvField, + FLMUINT uiFieldCounter, + FLMBOOL bReadOnly) +{ + RCODE rc = FERR_OK; + FLMUNICODE * puzBuf = NULL; + FLMUNICODE * puzTmp = NULL; + F_DynamicBuffer * pBuffer = NULL; + FLMUINT uiLen; + + if (RC_BAD( rc = pRec->getUnicodeLength( pvField, &uiLen))) + { + fnPrintf( m_pHRequest, + "** Error retrieving Unicode field length (Return Code = 0x%04X, %s) **", + (unsigned) rc, FlmErrorString( rc)); + goto Exit; + } + + // The length returned does not allow for 2 NULL terminators. We must + // allow for them when allocating a buffer. + + uiLen += 2; + if (RC_BAD( rc = f_alloc( uiLen, &puzBuf))) + { + fnPrintf( m_pHRequest, + "** Error allocating memory buffer (Return Code = 0x%04X, %s) **", + (unsigned) rc, FlmErrorString( rc)); + goto Exit; + } + + if (RC_BAD( rc = pRec->getUnicode( pvField, puzBuf, &uiLen))) + { + fnPrintf( m_pHRequest, + "** Error retrieving Unicode field (Return Code = 0x%04X, %s) **", + (unsigned) rc, FlmErrorString( rc)); + goto Exit; + } + + puzTmp = puzBuf; + if ((pBuffer = f_new F_DynamicBuffer) == NULL) + { + fnPrintf( m_pHRequest, "** Error allocating memory **"); + goto Exit; + } + + // Start the text field if not read only mode. + + if (!bReadOnly) + { + fnPrintf( m_pHRequest, + ""); + } + + while (*puzTmp) + { + + // Check for ASCII characters + + if ((*puzTmp >= 32) && (*puzTmp <= 126)) + { + if (RC_BAD( rc = pBuffer->addChar( (char) *puzTmp))) + { + fnPrintf( m_pHRequest, + "** Error adding Unicode character to buffer (Return Code = 0x%04X, %s) **", + (unsigned) rc, FlmErrorString( rc)); + goto Exit; + } + } + else + { + + // Treat as though these are NON-ASCII. They will be printed in + // the form ~[0x ] + + char szTempBuff[20]; + + f_sprintf( szTempBuff, "~[0x%04X]", (unsigned) (*puzTmp)); + + if (RC_BAD( rc = pBuffer->addString( szTempBuff))) + { + fnPrintf( m_pHRequest, + "** Error formatting Unicode string (Return Code = 0x%04X, %s) **", + (unsigned) rc, FlmErrorString( rc)); + goto Exit; + } + } + + // We are attempting to not let our buffer get any larger than the + // Http stack buffer size. We don't really know what the limit is, + // but we are using what seems to reasonable to us... + + if ((pBuffer->getBufferSize() + 9) >= RESP_WRITE_BUF_SIZE) + { + fnPrintf( m_pHRequest, "%s", pBuffer->printBuffer()); + pBuffer->reset(); + } + + puzTmp++; + } + + if (bReadOnly) + { + fnPrintf( m_pHRequest, "%s", pBuffer->printBuffer()); + } + else + { + fnPrintf( m_pHRequest, "%s\" size=\"%d\">", pBuffer->printBuffer(), + MAX_FIELD_SIZE( uiLen)); + } + +Exit: + + if (puzBuf) + { + f_free( &puzBuf); + } + + if (pBuffer) + { + pBuffer->Release(); + } +} + +// +/**************************************************************************** +Desc: Prints out the value for the field, assuming the field is a number field. +****************************************************************************/ +void F_WebPage::printNumberField( + FlmRecord * pRec, + void * pvField, + FLMUINT uiFieldCounter, + FLMBOOL bReadOnly) +{ + RCODE rc = FERR_OK; + FLMINT iVal; + FLMUINT uiVal; + + if (RC_BAD( rc = pRec->getUINT( pvField, &uiVal))) + { + if (RC_OK( rc = pRec->getINT( pvField, &iVal))) + { + if (bReadOnly) + { + fnPrintf( m_pHRequest, "%d", (int) iVal); + } + else + { + fnPrintf( m_pHRequest, + "", + uiFieldCounter, (int) iVal, MAX_FIELD_SIZE( 0)); + } + } + else + { + fnPrintf( m_pHRequest, + "** Error retrieving number field (Return Code = 0x%04X, %s)**\n", + (unsigned) rc, FlmErrorString( rc)); + } + } + else + { + if (bReadOnly) + { + fnPrintf( m_pHRequest, "%lu", + (unsigned long) uiVal); + } + else + { + fnPrintf( m_pHRequest, + "", + uiFieldCounter, (unsigned long) uiVal); + } + } +} + +/**************************************************************************** +Desc: Prints out the value for the field, assuming the field is a binary field. +****************************************************************************/ +void F_WebPage::printBinaryField( + FlmRecord * pRec, + void * pvField, + FLMUINT uiFieldCounter, + FLMBOOL bReadOnly) +{ + RCODE rc = FERR_OK; + FLMBYTE * pucBuf = NULL; + FLMBYTE * pszTmpBuf = NULL; + FLMBYTE * pszTmp = NULL; + FLMUINT uiLoop; + FLMUINT uiLen; + FLMUINT uiBufLen; + + uiLen = pRec->getDataLength( pvField); + + if (RC_BAD( rc = f_alloc( uiLen, &pucBuf))) + { + fnPrintf( m_pHRequest, + "** Error occured allocating memory to retrieve binary field (Return Code = 0x%04X, %s) **\n", + (unsigned) rc, FlmErrorString( rc)); + goto Exit; + } + + if (RC_BAD( rc = pRec->getBinary( pvField, pucBuf, &uiLen))) + { + if (rc != FERR_NOT_FOUND) + { + fnPrintf( m_pHRequest, + "** Error occured retrieving binary field (Return Code = 0x%04X, %s) **\n", + (unsigned) rc, FlmErrorString( rc)); + goto Exit; + } + } + + if (RC_BAD( rc = f_alloc( RESP_WRITE_BUF_SIZE + 1, &pszTmpBuf))) + { + fnPrintf( m_pHRequest, + "** Error occured allocating memory to format binary field (Return Code = 0x%04X, %s) **\n", + (unsigned) rc, FlmErrorString( rc)); + goto Exit; + } + + if (!bReadOnly) + { + fnPrintf( m_pHRequest, + ""); + } + + // Scan through the binary data, present all data as Hex. + + for (pszTmp = pszTmpBuf, uiLoop = 0, uiBufLen = 0; uiLoop < uiLen; uiLoop++) + { + if (uiLoop) + { + *pszTmp++ = ' '; + uiBufLen++; + } + + f_sprintf( (char*) pszTmp, "%2.2X", (unsigned) pucBuf[uiLoop]); + + pszTmp += 2; + uiBufLen += 2; + + if ((uiBufLen + 3) >= RESP_WRITE_BUF_SIZE) + { + + // Flush the current buffer + + *pszTmp = '\0'; + fnPrintf( m_pHRequest, "%s", pszTmpBuf); + pszTmp = pszTmpBuf; + uiBufLen = 0; + } + } + + *pszTmp = '\0'; + + if (bReadOnly) + { + fnPrintf( m_pHRequest, "%s", pszTmpBuf); + } + else + { + fnPrintf( m_pHRequest, "%s\" size=\"%d\">", pszTmpBuf, + MAX_FIELD_SIZE( uiLen * 3)); + } + +Exit: + + if (pucBuf) + { + f_free( &pucBuf); + } + + if (pszTmpBuf) + { + f_free( &pszTmpBuf); + } +} + +/**************************************************************************** +Desc: Prints out the value for the field, assuming the field is a context field. +****************************************************************************/ +void F_WebPage::printContextField( + FlmRecord * pRec, + void * pvField, + FLMUINT uiFieldCounter, + FLMBOOL bReadOnly) +{ + RCODE rc = FERR_OK; + FLMUINT uiRecPointer; + + if (RC_OK( rc = pRec->getRecPointer( pvField, &uiRecPointer))) + { + if (bReadOnly) + { + fnPrintf( m_pHRequest, "%lu", + (unsigned long) uiRecPointer); + } + else + { + fnPrintf( m_pHRequest, + "", uiFieldCounter, + (unsigned long) uiRecPointer, MAX_FIELD_SIZE( 0)); + } + } + else + { + fnPrintf( m_pHRequest, + "** Error retrieving context field (Return Code = 0x%04X, %s) **", + (unsigned) rc, FlmErrorString( rc)); + } +} + +/**************************************************************************** +Desc: Prints out the value for the field, assuming the field is a blob field. +****************************************************************************/ +void F_WebPage::printBlobField( + FlmRecord * pRec, + void * pvField, + FLMUINT uiFieldCounter, + FLMBOOL bReadOnly) +{ + RCODE rc = FERR_OK; + FlmBlob * pBlob = NULL; + char szPath[F_PATH_MAX_SIZE]; + FLMUINT uiLen; + + if (RC_BAD( rc = pRec->getBlob( pvField, &pBlob))) + { + fnPrintf( m_pHRequest, + "** Failed to retrieve Blob object (Return Code = 0x%04X, %s) **", + (unsigned long) rc, FlmErrorString( rc)); + goto Exit; + } + + uiLen = ((FlmBlobImp *) pBlob)->getDataLength(); + if (uiLen == 0) + { + if (!bReadOnly) + { + fnPrintf( m_pHRequest, + "", uiFieldCounter, + MAX_FIELD_SIZE( 0)); + } + + goto Exit; + } + + if (RC_BAD( rc = pBlob->buildFileName( szPath))) + { + fnPrintf( m_pHRequest, + "** Failed to retrieve Blob filename (Return Code = 0x%04X, %s) **", + (unsigned) rc, FlmErrorString( rc)); + goto Exit; + } + + if (bReadOnly) + { + fnPrintf( m_pHRequest, ""); + printEncodedString( szPath, HTML_ENCODING); + fnPrintf( m_pHRequest, ""); + } + else + { + fnPrintf( m_pHRequest, + ""); + } + +Exit: + + if (pBlob) + { + pBlob->Release(); + } +} + +/**************************************************************************** +Desc: Prints out a string identifying this as a default field - error condition. +****************************************************************************/ +void F_WebPage::printDefaultField( + FlmRecord *, + void *, + FLMUINT, + FLMBOOL) +{ + fnPrintf( m_pHRequest, "**Default Field**"); +} + +/**************************************************************************** +Desc: Prints out the hidden field identifiers +****************************************************************************/ +void F_WebPage::printFieldIds( + FLMUINT uiFieldCounter, + FLMUINT uiFieldLevel, + FLMUINT uiType, + FLMUINT uiTagNum) +{ + char szTmp[20]; + + f_sprintf( szTmp, "fieldLevel%u", (unsigned) uiFieldCounter); + printHiddenField( szTmp, uiFieldLevel); + f_sprintf( szTmp, "fieldType%u", (unsigned) uiFieldCounter); + printHiddenField( szTmp, uiType); + f_sprintf( szTmp, "fieldTag%u", (unsigned) uiFieldCounter); + printHiddenField( szTmp, uiTagNum); +} + +/**************************************************************************** +Desc: Prints a table listing the fields of the supplied Log Headers. Any of + the log header pointers may be null, in which case a series of blank + entries will be created in the table for that entry. +****************************************************************************/ +void F_WebPage::printLogHeaders( + FLMBYTE * pucLastCommitted, + FLMBYTE * pucCheckpoint, + FLMBYTE * pucUncommitted) +{ + FLMBOOL bHighlight = FALSE; + + // Start the table and headings... + + printTableStart( NULL, 5, 100); + + printTableRowStart( FALSE); + printColumnHeading( "Offset (hex)"); + printColumnHeading( "Field"); + printColumnHeading( "Last Committed"); + printColumnHeading( "Checkpoint"); + printColumnHeading( "Uncommitted"); + printTableRowEnd(); + + // Fill in the table here. LOG_RFL_FILE_NUM + + printTableRowStart( bHighlight = !bHighlight); + fnPrintf( m_pHRequest, "0x%X", LOG_RFL_FILE_NUM); + fnPrintf( m_pHRequest, "Current RFL file"); + printLogFileEntryUD( pucLastCommitted, LOG_RFL_FILE_NUM); + printLogFileEntryUD( pucCheckpoint, LOG_RFL_FILE_NUM); + printLogFileEntryUD( pucUncommitted, LOG_RFL_FILE_NUM); + printTableRowEnd(); + + // LOG_RFL_LAST_TRANS_OFFSET + + printTableRowStart( bHighlight = !bHighlight); + fnPrintf( m_pHRequest, "0x%X", LOG_RFL_LAST_TRANS_OFFSET); + fnPrintf( m_pHRequest, "Current RFL offset"); + printLogFileEntryUD( pucLastCommitted, LOG_RFL_LAST_TRANS_OFFSET); + printLogFileEntryUD( pucCheckpoint, LOG_RFL_LAST_TRANS_OFFSET); + printLogFileEntryUD( pucUncommitted, LOG_RFL_LAST_TRANS_OFFSET); + printTableRowEnd(); + + // LOG_RFL_LAST_CP_FILE_NUM + + printTableRowStart( bHighlight = !bHighlight); + fnPrintf( m_pHRequest, "0x%X", LOG_RFL_LAST_CP_FILE_NUM); + fnPrintf( m_pHRequest, "Last CP RFL file"); + printLogFileEntryUD( pucLastCommitted, LOG_RFL_LAST_CP_FILE_NUM); + printLogFileEntryUD( pucCheckpoint, LOG_RFL_LAST_CP_FILE_NUM); + printLogFileEntryUD( pucUncommitted, LOG_RFL_LAST_CP_FILE_NUM); + printTableRowEnd(); + + // LOG_RFL_LAST_CP_OFFSET + + printTableRowStart( bHighlight = !bHighlight); + fnPrintf( m_pHRequest, "0x%X", LOG_RFL_LAST_CP_OFFSET); + fnPrintf( m_pHRequest, "Last CP RFL offset"); + printLogFileEntryUD( pucLastCommitted, LOG_RFL_LAST_CP_OFFSET); + printLogFileEntryUD( pucCheckpoint, LOG_RFL_LAST_CP_OFFSET); + printLogFileEntryUD( pucUncommitted, LOG_RFL_LAST_CP_OFFSET); + printTableRowEnd(); + + // LOG_ROLLBACK_EOF + + printTableRowStart( bHighlight = !bHighlight); + fnPrintf( m_pHRequest, "0x%X", LOG_ROLLBACK_EOF); + fnPrintf( m_pHRequest, "End of file"); + printLogFileEntryUD( pucLastCommitted, LOG_ROLLBACK_EOF); + printLogFileEntryUD( pucCheckpoint, LOG_ROLLBACK_EOF); + printLogFileEntryUD( pucUncommitted, LOG_ROLLBACK_EOF); + printTableRowEnd(); + + // LOG_INC_BACKUP_SEQ_NUM + + printTableRowStart( bHighlight = !bHighlight); + fnPrintf( m_pHRequest, "0x%X", LOG_INC_BACKUP_SEQ_NUM); + fnPrintf( m_pHRequest, "Incremental backup sequence number"); + printLogFileEntryUD( pucLastCommitted, LOG_INC_BACKUP_SEQ_NUM); + printLogFileEntryUD( pucCheckpoint, LOG_INC_BACKUP_SEQ_NUM); + printLogFileEntryUD( pucUncommitted, LOG_INC_BACKUP_SEQ_NUM); + printTableRowEnd(); + + // LOG_CURR_TRANS_ID + + printTableRowStart( bHighlight = !bHighlight); + fnPrintf( m_pHRequest, "0x%X", LOG_CURR_TRANS_ID); + fnPrintf( m_pHRequest, "Transaction ID"); + printLogFileEntryUD( pucLastCommitted, LOG_CURR_TRANS_ID); + printLogFileEntryUD( pucCheckpoint, LOG_CURR_TRANS_ID); + printLogFileEntryUD( pucUncommitted, LOG_CURR_TRANS_ID); + printTableRowEnd(); + + // LOG_COMMIT_COUNT + + printTableRowStart( bHighlight = !bHighlight); + fnPrintf( m_pHRequest, "0x%X", LOG_COMMIT_COUNT); + fnPrintf( m_pHRequest, "Commit count"); + printLogFileEntryUD( pucLastCommitted, LOG_COMMIT_COUNT); + printLogFileEntryUD( pucCheckpoint, LOG_COMMIT_COUNT); + printLogFileEntryUD( pucUncommitted, LOG_COMMIT_COUNT); + printTableRowEnd(); + + // LOG_PL_FIRST_CP_BLOCK_ADDR + + printTableRowStart( bHighlight = !bHighlight); + fnPrintf( m_pHRequest, "0x%X", LOG_PL_FIRST_CP_BLOCK_ADDR); + fnPrintf( m_pHRequest, "First CP block address"); + printLogFileEntryUDX( pucLastCommitted, LOG_PL_FIRST_CP_BLOCK_ADDR); + printLogFileEntryUDX( pucCheckpoint, LOG_PL_FIRST_CP_BLOCK_ADDR); + printLogFileEntryUDX( pucUncommitted, LOG_PL_FIRST_CP_BLOCK_ADDR); + printTableRowEnd(); + + // LOG_LAST_RFL_FILE_DELETED + + printTableRowStart( bHighlight = !bHighlight); + fnPrintf( m_pHRequest, "0x%X", LOG_LAST_RFL_FILE_DELETED); + fnPrintf( m_pHRequest, "Last RFL file deleted"); + printLogFileEntryUD( pucLastCommitted, LOG_LAST_RFL_FILE_DELETED); + printLogFileEntryUD( pucCheckpoint, LOG_LAST_RFL_FILE_DELETED); + printLogFileEntryUD( pucUncommitted, LOG_LAST_RFL_FILE_DELETED); + printTableRowEnd(); + + // LOG_RFL_MIN_FILE_SIZE + + printTableRowStart( bHighlight = !bHighlight); + fnPrintf( m_pHRequest, "0x%X", LOG_RFL_MIN_FILE_SIZE); + fnPrintf( m_pHRequest, "Minimum RFL file size"); + printLogFileEntryUD( pucLastCommitted, LOG_RFL_MIN_FILE_SIZE); + printLogFileEntryUD( pucCheckpoint, LOG_RFL_MIN_FILE_SIZE); + printLogFileEntryUD( pucUncommitted, LOG_RFL_MIN_FILE_SIZE); + printTableRowEnd(); + + // LOG_HDR_CHECKSUM + + printTableRowStart( bHighlight = !bHighlight); + fnPrintf( m_pHRequest, "0x%X", LOG_HDR_CHECKSUM); + fnPrintf( m_pHRequest, "Header checksum"); + printLogFileEntryUW( pucLastCommitted, LOG_HDR_CHECKSUM); + printLogFileEntryUW( pucCheckpoint, LOG_HDR_CHECKSUM); + printLogFileEntryUW( pucUncommitted, LOG_HDR_CHECKSUM); + printTableRowEnd(); + + // LOG_FLAIM_VERSION + + printTableRowStart( bHighlight = !bHighlight); + fnPrintf( m_pHRequest, "0x%X", LOG_FLAIM_VERSION); + fnPrintf( m_pHRequest, "Flaim version"); + printLogFileEntryUW( pucLastCommitted, LOG_FLAIM_VERSION); + printLogFileEntryUW( pucCheckpoint, LOG_FLAIM_VERSION); + printLogFileEntryUW( pucUncommitted, LOG_FLAIM_VERSION); + printTableRowEnd(); + + // LOG_LAST_BACKUP_TRANS_ID + + printTableRowStart( bHighlight = !bHighlight); + fnPrintf( m_pHRequest, "0x%X", LOG_LAST_BACKUP_TRANS_ID); + fnPrintf( m_pHRequest, "Last backup trans ID"); + printLogFileEntryUD( pucLastCommitted, LOG_LAST_BACKUP_TRANS_ID); + printLogFileEntryUD( pucCheckpoint, LOG_LAST_BACKUP_TRANS_ID); + printLogFileEntryUD( pucUncommitted, LOG_LAST_BACKUP_TRANS_ID); + printTableRowEnd(); + + // LOG_BLK_CHG_SINCE_BACKUP + + printTableRowStart( bHighlight = !bHighlight); + fnPrintf( m_pHRequest, "0x%X", LOG_BLK_CHG_SINCE_BACKUP); + fnPrintf( m_pHRequest, "Blocks changed since backup"); + printLogFileEntryUD( pucLastCommitted, LOG_BLK_CHG_SINCE_BACKUP); + printLogFileEntryUD( pucCheckpoint, LOG_BLK_CHG_SINCE_BACKUP); + printLogFileEntryUD( pucUncommitted, LOG_BLK_CHG_SINCE_BACKUP); + printTableRowEnd(); + + // LOG_LAST_CP_TRANS_ID + + printTableRowStart( bHighlight = !bHighlight); + fnPrintf( m_pHRequest, "0x%X", LOG_LAST_CP_TRANS_ID); + fnPrintf( m_pHRequest, "Last CP trans ID"); + printLogFileEntryUD( pucLastCommitted, LOG_LAST_CP_TRANS_ID); + printLogFileEntryUD( pucCheckpoint, LOG_LAST_CP_TRANS_ID); + printLogFileEntryUD( pucUncommitted, LOG_LAST_CP_TRANS_ID); + printTableRowEnd(); + + // LOG_PF_FIRST_BACKCHAIN + + printTableRowStart( bHighlight = !bHighlight); + fnPrintf( m_pHRequest, "0x%X", LOG_PF_FIRST_BACKCHAIN); + fnPrintf( m_pHRequest, "Backchain block address"); + if (pucLastCommitted && + FB2UD( &pucLastCommitted[LOG_PF_FIRST_BACKCHAIN]) == BT_END) + { + fnPrintf( m_pHRequest, "none"); + } + else + { + printLogFileEntryUDX( pucLastCommitted, LOG_PF_FIRST_BACKCHAIN); + } + + if (pucCheckpoint && + FB2UD( &pucCheckpoint[LOG_PF_FIRST_BACKCHAIN]) == BT_END) + { + fnPrintf( m_pHRequest, "none"); + } + else + { + printLogFileEntryUDX( pucCheckpoint, LOG_PF_FIRST_BACKCHAIN); + } + + if (pucUncommitted && + FB2UD( &pucUncommitted[LOG_PF_FIRST_BACKCHAIN]) == BT_END) + { + fnPrintf( m_pHRequest, "none"); + } + else + { + printLogFileEntryUDX( pucUncommitted, LOG_PF_FIRST_BACKCHAIN); + } + + printTableRowEnd(); + + // LOG_PF_AVAIL_BLKS + + printTableRowStart( bHighlight = !bHighlight); + fnPrintf( m_pHRequest, "0x%X", LOG_PF_AVAIL_BLKS); + fnPrintf( m_pHRequest, "Available blocks"); + if (pucLastCommitted && + FB2UD( &pucLastCommitted[LOG_PF_AVAIL_BLKS]) == BT_END) + { + fnPrintf( m_pHRequest, "none"); + } + else + { + printLogFileEntryUDX( pucLastCommitted, LOG_PF_AVAIL_BLKS); + } + + if (pucCheckpoint && FB2UD( &pucCheckpoint[LOG_PF_AVAIL_BLKS]) == BT_END) + { + fnPrintf( m_pHRequest, "none"); + } + else + { + printLogFileEntryUDX( pucCheckpoint, LOG_PF_AVAIL_BLKS); + } + + if (pucUncommitted && FB2UD( &pucUncommitted[LOG_PF_AVAIL_BLKS]) == BT_END) + { + fnPrintf( m_pHRequest, "none"); + } + else + { + printLogFileEntryUDX( pucUncommitted, LOG_PF_AVAIL_BLKS); + } + + printTableRowEnd(); + + // LOG_LOGICAL_EOF + + printTableRowStart( bHighlight = !bHighlight); + fnPrintf( m_pHRequest, "0x%X", LOG_LOGICAL_EOF); + fnPrintf( m_pHRequest, "Logical EOF"); + printLogFileEntryUD_X( pucLastCommitted, LOG_LOGICAL_EOF); + printLogFileEntryUD_X( pucCheckpoint, LOG_LOGICAL_EOF); + printLogFileEntryUD_X( pucUncommitted, LOG_LOGICAL_EOF); + printTableRowEnd(); + + // LOG_LAST_RFL_COMMIT_ID + + printTableRowStart( bHighlight = !bHighlight); + fnPrintf( m_pHRequest, "0x%X", LOG_LAST_RFL_COMMIT_ID); + fnPrintf( m_pHRequest, "Last RFL commit ID"); + printLogFileEntryUD( pucLastCommitted, LOG_LAST_RFL_COMMIT_ID); + printLogFileEntryUD( pucCheckpoint, LOG_LAST_RFL_COMMIT_ID); + printLogFileEntryUD( pucUncommitted, LOG_LAST_RFL_COMMIT_ID); + printTableRowEnd(); + + // LOG_KEEP_ABORTED_TRANS_IN_RFL + + printTableRowStart( bHighlight = !bHighlight); + fnPrintf( m_pHRequest, "0x%X", LOG_KEEP_ABORTED_TRANS_IN_RFL); + fnPrintf( m_pHRequest, "Keep aborted trans in RFL"); + printLogFileEntryBool( pucLastCommitted, LOG_KEEP_ABORTED_TRANS_IN_RFL); + printLogFileEntryBool( pucCheckpoint, LOG_KEEP_ABORTED_TRANS_IN_RFL); + printLogFileEntryBool( pucUncommitted, LOG_KEEP_ABORTED_TRANS_IN_RFL); + printTableRowEnd(); + + // LOG_PF_FIRST_BC_CNT + + printTableRowStart( bHighlight = !bHighlight); + fnPrintf( m_pHRequest, "0x%X", LOG_PF_FIRST_BC_CNT); + fnPrintf( m_pHRequest, "First BC count"); + printLogFileEntryUC( pucLastCommitted, LOG_PF_FIRST_BC_CNT); + printLogFileEntryUC( pucCheckpoint, LOG_PF_FIRST_BC_CNT); + printLogFileEntryUC( pucUncommitted, LOG_PF_FIRST_BC_CNT); + printTableRowEnd(); + + // LOG_KEEP_RFL_FILES + + printTableRowStart( bHighlight = !bHighlight); + fnPrintf( m_pHRequest, "0x%X", LOG_KEEP_RFL_FILES); + fnPrintf( m_pHRequest, "Keep RFL files"); + printLogFileEntryBool( pucLastCommitted, LOG_KEEP_RFL_FILES); + printLogFileEntryBool( pucCheckpoint, LOG_KEEP_RFL_FILES); + printLogFileEntryBool( pucUncommitted, LOG_KEEP_RFL_FILES); + printTableRowEnd(); + + // LOG_AUTO_TURN_OFF_KEEP_RFL + + printTableRowStart( bHighlight = !bHighlight); + fnPrintf( m_pHRequest, "0x%X", LOG_AUTO_TURN_OFF_KEEP_RFL); + fnPrintf( m_pHRequest, "Auto turn off keep RFL"); + printLogFileEntryBool( pucLastCommitted, LOG_AUTO_TURN_OFF_KEEP_RFL); + printLogFileEntryBool( pucCheckpoint, LOG_AUTO_TURN_OFF_KEEP_RFL); + printLogFileEntryBool( pucUncommitted, LOG_AUTO_TURN_OFF_KEEP_RFL); + printTableRowEnd(); + + // LOG_PF_NUM_AVAIL_BLKS + + printTableRowStart( bHighlight = !bHighlight); + fnPrintf( m_pHRequest, "0x%X", LOG_PF_NUM_AVAIL_BLKS); + fnPrintf( m_pHRequest, "Avail Blocks"); + printLogFileEntryUD( pucLastCommitted, LOG_PF_NUM_AVAIL_BLKS); + printLogFileEntryUD( pucCheckpoint, LOG_PF_NUM_AVAIL_BLKS); + printLogFileEntryUD( pucUncommitted, LOG_PF_NUM_AVAIL_BLKS); + printTableRowEnd(); + + // LOG_RFL_MAX_FILE_SIZE + + printTableRowStart( bHighlight = !bHighlight); + fnPrintf( m_pHRequest, "0x%X", LOG_RFL_MAX_FILE_SIZE); + fnPrintf( m_pHRequest, "Max file size"); + printLogFileEntryUD( pucLastCommitted, LOG_RFL_MAX_FILE_SIZE); + printLogFileEntryUD( pucCheckpoint, LOG_RFL_MAX_FILE_SIZE); + printLogFileEntryUD( pucUncommitted, LOG_RFL_MAX_FILE_SIZE); + printTableRowEnd(); + + // LOG_DB_SERIAL_NUM + + printTableRowStart( bHighlight = !bHighlight); + fnPrintf( m_pHRequest, "0x%X", LOG_DB_SERIAL_NUM); + fnPrintf( m_pHRequest, "DB serial number"); + printSerialNum( pucLastCommitted ? &pucLastCommitted[LOG_DB_SERIAL_NUM] : NULL); + printSerialNum( pucCheckpoint ? &pucCheckpoint[LOG_DB_SERIAL_NUM] : NULL); + printSerialNum( pucUncommitted ? &pucUncommitted[LOG_DB_SERIAL_NUM] : NULL); + printTableRowEnd(); + + // LOG_LAST_TRANS_RFL_SERIAL_NUM + + printTableRowStart( bHighlight = !bHighlight); + fnPrintf( m_pHRequest, "0x%X", LOG_LAST_TRANS_RFL_SERIAL_NUM); + fnPrintf( m_pHRequest, "Last Trans RFL serial number"); + printSerialNum( pucLastCommitted ? &pucLastCommitted[LOG_LAST_TRANS_RFL_SERIAL_NUM] : + NULL); + printSerialNum( pucCheckpoint ? &pucCheckpoint[LOG_LAST_TRANS_RFL_SERIAL_NUM] : NULL); + printSerialNum( pucUncommitted ? &pucUncommitted[LOG_LAST_TRANS_RFL_SERIAL_NUM] : NULL); + printTableRowEnd(); + + // LOG_RFL_NEXT_SERIAL_NUM + + printTableRowStart( bHighlight = !bHighlight); + fnPrintf( m_pHRequest, "0x%X", LOG_RFL_NEXT_SERIAL_NUM); + fnPrintf( m_pHRequest, "Next RFL serial number"); + printSerialNum( pucLastCommitted ? &pucLastCommitted[LOG_RFL_NEXT_SERIAL_NUM] : NULL); + printSerialNum( pucCheckpoint ? &pucCheckpoint[LOG_RFL_NEXT_SERIAL_NUM] : NULL); + printSerialNum( pucUncommitted ? &pucUncommitted[LOG_RFL_NEXT_SERIAL_NUM] : NULL); + printTableRowEnd(); + + // LOG_INC_BACKUP_SERIAL_NUM + + printTableRowStart( bHighlight = !bHighlight); + fnPrintf( m_pHRequest, "0x%X", LOG_INC_BACKUP_SERIAL_NUM); + fnPrintf( m_pHRequest, "Incremental backup serial number"); + printSerialNum( pucLastCommitted ? &pucLastCommitted[LOG_INC_BACKUP_SERIAL_NUM] : NULL); + printSerialNum( pucCheckpoint ? &pucCheckpoint[LOG_INC_BACKUP_SERIAL_NUM] : NULL); + printSerialNum( pucUncommitted ? &pucUncommitted[LOG_INC_BACKUP_SERIAL_NUM] : NULL); + printTableRowEnd(); + + // LOG_MAX_FILE_SIZE + + printTableRowStart( bHighlight = !bHighlight); + fnPrintf( m_pHRequest, "0x%X", LOG_MAX_FILE_SIZE); + fnPrintf( m_pHRequest, "Maximum file size (64K units)"); + printLogFileEntryUW( pucLastCommitted, LOG_MAX_FILE_SIZE); + printLogFileEntryUW( pucCheckpoint, LOG_MAX_FILE_SIZE); + printLogFileEntryUW( pucUncommitted, LOG_MAX_FILE_SIZE); + printTableRowEnd(); + + printTableEnd(); +} + +/******************************************************************* +Desc: +********************************************************************/ +void F_WebPage::printSerialNum( + FLMBYTE * pucSerialNum) +{ + if (pucSerialNum) + { + printTableDataStart( FALSE, JUSTIFY_LEFT); + + // fnPrintf( m_pHRequest, "0x"); + + for (int iLoop = 0; iLoop < F_SERIAL_NUM_SIZE; iLoop++) + { + fnPrintf( m_pHRequest, "%02X ", pucSerialNum[iLoop]); + } + + printTableDataEnd(); + } + else + { + fnPrintf( m_pHRequest, "-"); + } +} + +/******************************************************************* +Desc: Print a table entry as unsigned 4 digit hex minimum. +********************************************************************/ +void F_WebPage::printLogFileEntryUDX( + FLMBYTE * pucLog, + FLMUINT uiOffset) +{ + if (pucLog) + { + fnPrintf( m_pHRequest, "0x%04X", FB2UD( &pucLog[uiOffset])); + } + else + { + fnPrintf( m_pHRequest, "-"); + } +} + +/******************************************************************* +Desc: Print a table entry as unsigned decimal hex in parenthesis. +********************************************************************/ +void F_WebPage::printLogFileEntryUD_X( + FLMBYTE * pucLog, + FLMUINT uiOffset) +{ + if (pucLog) + { + printTableDataStart( TRUE, JUSTIFY_LEFT); + printCommaNumText( (FLMUINT64) FB2UD( &pucLog[uiOffset])); + fnPrintf( m_pHRequest, " (0x%X)", FB2UD( &pucLog[uiOffset])); + printTableDataEnd(); + } + else + { + fnPrintf( m_pHRequest, "-"); + } +} + +/******************************************************************* +Desc: Print a table entry as unsigned decimal. +********************************************************************/ +void F_WebPage::printLogFileEntryUD( + FLMBYTE * pucLog, + FLMUINT uiOffset) +{ + if (pucLog) + { + printCommaNum( (FLMUINT64) FB2UD( &pucLog[uiOffset]), JUSTIFY_LEFT); + } + else + { + fnPrintf( m_pHRequest, "-"); + } +} + +/******************************************************************* +Desc: Print a table entry as unsigned word. +********************************************************************/ +void F_WebPage::printLogFileEntryUW( + FLMBYTE * pucLog, + FLMUINT uiOffset) +{ + if (pucLog) + { + printCommaNum( (FLMUINT64) FB2UW( &pucLog[uiOffset]), JUSTIFY_LEFT); + } + else + { + fnPrintf( m_pHRequest, "-"); + } +} + +/******************************************************************* +Desc: Print a table entry as unsigned char. +********************************************************************/ +void F_WebPage::printLogFileEntryUC( + FLMBYTE * pucLog, + FLMUINT uiOffset) +{ + if (pucLog) + { + fnPrintf( m_pHRequest, "%u", (unsigned char) pucLog[uiOffset]); + } + else + { + fnPrintf( m_pHRequest, "-"); + } +} + +/******************************************************************* +Desc: Print a table entry as yes or no (FLMBOOL) +********************************************************************/ +void F_WebPage::printLogFileEntryBool( + FLMBYTE * pucLog, + FLMUINT uiOffset) +{ + if (pucLog) + { + printTableDataStart( TRUE, JUSTIFY_LEFT); + printYesNo( (FLMBOOL) pucLog[uiOffset]); + printTableDataEnd(); + } + else + { + fnPrintf( m_pHRequest, "-"); + } +} diff --git a/flaim/src/rcache.cpp b/flaim/src/rcache.cpp index 98bc7a4..8414114 100644 --- a/flaim/src/rcache.cpp +++ b/flaim/src/rcache.cpp @@ -1,3074 +1,3074 @@ -//------------------------------------------------------------------------- -// Desc: Record caching -// Tabs: 3 -// -// Copyright (c) 1999-2006 Novell, Inc. All Rights Reserved. -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of version 2 of the GNU General Public -// License as published by the Free Software Foundation. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, contact Novell, Inc. -// -// To contact Novell about this file by physical or electronic mail, -// you may find current contact information at www.novell.com -// -// $Id: rcache.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ -//------------------------------------------------------------------------- - -#include "flaimsys.h" - -#if defined( FLM_NLM) && !defined( __MWERKS__) - // Disable "Warning! W549: col(XX) 'sizeof' operand contains - // compiler generated information" - - #pragma warning 549 9 -#endif - -FSTATIC FLMBOOL rcaCanRelocate( - void * pvAlloc); - -FSTATIC void rcaRelocate( - void * pvOldAlloc, - void * pvNewAlloc); - -/**************************************************************************** -Desc: Extended record object for accessing private members of FlmRecord -****************************************************************************/ -struct FlmRecordExt -{ - static FINLINE void clearCached( - FlmRecord * pRec) - { - pRec->clearCached(); - } - - static FINLINE void setCached( - FlmRecord * pRec) - { - pRec->setCached(); - } - - static FINLINE void setReadOnly( - FlmRecord * pRec) - { - pRec->setReadOnly(); - } - - static FINLINE FLMINT AddRef( - FlmRecord * pRec, - FLMBOOL bMutexLocked) - { - return( pRec->AddRef( bMutexLocked)); - } - - static FINLINE FLMINT Release( - FlmRecord * pRec, - FLMBOOL bMutexLocked) - { - return( pRec->Release( bMutexLocked)); - } - - static FINLINE void setOldVersion( - FlmRecord * pRec) - { - pRec->setOldVersion(); - } - - static FINLINE void clearOldVersion( - FlmRecord * pRec) - { - pRec->clearOldVersion(); - } - - static FINLINE FLMUINT getFlags( - FlmRecord * pRec) - { - return( pRec->m_uiFlags); - } - - static FLMBOOL canRelocateRec( - void * pvAlloc); - - static void relocateRec( - void * pvOldAlloc, - void * pvNewAlloc); - - static FLMBOOL canRelocateRecBuffer( - void * pvAlloc); - - static void relocateRecBuffer( - void * pvOldAlloc, - void * pvNewAlloc); -}; - -// Functions for calculating minimum and maximum record counts for a -// given hash table size. - -#define FLM_RCA_MIN_REC_CNT(uiHashTblSz) \ - ((uiHashTblSz) / 4) - -#define FLM_RCA_MAX_REC_CNT(uiHashTblSz) \ - ((uiHashTblSz) * 4) - -// Hash function for hashing to records in record cache. - -#define FLM_RCA_HASH( uiDrn) \ - (RCACHE **)(&(gv_FlmSysData.RCacheMgr.ppHashBuckets[(uiDrn) & \ - (gv_FlmSysData.RCacheMgr.uiHashMask)])) - -// Local functions - -FSTATIC void flmRcaFreePurged( - RCACHE * pRCache); - -FSTATIC void flmRcaFreeCache( - RCACHE * pRCache, - FLMBOOL bPutInPurgeList); - -FSTATIC FLMUINT flmRcaGetBestHashTblSize( - FLMUINT uiCurrRecCount); - -FSTATIC RCODE flmRcaRehash( void); - -FSTATIC RCODE flmRcaSetMemLimit( - FLMUINT uiMaxCacheBytes); - -FSTATIC RCODE flmRcaWaitNotify( - FNOTIFY ** ppNotifyListRV); - -FSTATIC void flmRcaNotify( - FNOTIFY * pNotify, - RCACHE * pUseRCache, - RCODE NotifyRc); - -FSTATIC RCODE flmRcaAllocCacheStruct( - RCACHE ** ppRCache); - -FSTATIC void flmRcaFreeCacheStruct( - RCACHE ** ppRCache); - -FSTATIC void flmRcaSetRecord( - RCACHE * pRCache, - FlmRecord * pNewRecord); - -FSTATIC void flmRcaLinkIntoRCache( - RCACHE * pNewerRCache, - RCACHE * pOlderRCache, - RCACHE * pRCache, - FLMBOOL bLinkAsMRU); - -FSTATIC void flmRcaLinkToFFILE( - RCACHE * pRCache, - FFILE * pFile, - FDB * pDb, - FLMUINT uiLowTransId, - FLMBOOL bMostCurrent); - -#ifdef FLM_DEBUG -FSTATIC RCODE flmRcaCheck( - FDB * pDb, - FLMUINT uiContainer, - FLMUINT uiDrn); -#endif - -/**************************************************************************** -Desc: This inline assumes that the global mutex is locked, because - it potentially updates the cache usage statistics. -****************************************************************************/ -FINLINE void flmRcaSetTransID( - RCACHE * pRCache, - FLMUINT uiNewTransID) -{ - if (pRCache->uiHighTransId == 0xFFFFFFFF && - uiNewTransID != 0xFFFFFFFF) - { - FLMUINT uiSize = (FLMUINT)((pRCache->pRecord) - ? pRCache->pRecord->getTotalMemory() - : (FLMUINT)0) + sizeof( RCACHE); - gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes += uiSize; - gv_FlmSysData.RCacheMgr.Usage.uiOldVerCount++; - - if( pRCache->pRecord) - { - FlmRecordExt::setOldVersion( pRCache->pRecord); - } - } - else if (pRCache->uiHighTransId != 0xFFFFFFFF && - uiNewTransID == 0xFFFFFFFF) - { - FLMUINT uiSize = (FLMUINT)((pRCache->pRecord) - ? pRCache->pRecord->getTotalMemory() - : (FLMUINT)0) + sizeof( RCACHE); - flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes >= uiSize); - flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiOldVerCount); - gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes -= uiSize; - gv_FlmSysData.RCacheMgr.Usage.uiOldVerCount--; - if( pRCache->pRecord) - { - FlmRecordExt::clearOldVersion( pRCache->pRecord); - } - } - pRCache->uiHighTransId = uiNewTransID; -} - -/**************************************************************************** -Desc: This routine links a record into the global list as the MRU record. - This routine assumes that the record cache mutex has already - been locked. -****************************************************************************/ -FINLINE void flmRcaLinkToGlobalAsMRU( - RCACHE * pRCache) -{ - pRCache->pPrevInGlobal = NULL; - if ((pRCache->pNextInGlobal = gv_FlmSysData.RCacheMgr.pMRURecord) != NULL) - { - gv_FlmSysData.RCacheMgr.pMRURecord->pPrevInGlobal = pRCache; - } - else - { - gv_FlmSysData.RCacheMgr.pLRURecord = pRCache; - } - gv_FlmSysData.RCacheMgr.pMRURecord = pRCache; -} - -/**************************************************************************** -Desc: This routine links a record into the global list as the LRU record. - This routine assumes that the record cache mutex has already - been locked. -****************************************************************************/ -FINLINE void flmRcaLinkToGlobalAsLRU( - RCACHE * pRCache) -{ - pRCache->pNextInGlobal = NULL; - if ((pRCache->pPrevInGlobal = gv_FlmSysData.RCacheMgr.pLRURecord) != NULL) - { - gv_FlmSysData.RCacheMgr.pLRURecord->pNextInGlobal = pRCache; - } - else - { - gv_FlmSysData.RCacheMgr.pMRURecord = pRCache; - } - gv_FlmSysData.RCacheMgr.pLRURecord = pRCache; -} - -/**************************************************************************** -Desc: Moves a record one step closer to the MRU slot in the global list. - This routine assumes that the record cache mutex has already - been locked. -****************************************************************************/ -FINLINE void flmRcaStepUpInGlobalList( - RCACHE * pRCache) -{ - RCACHE * pPrevRCache; - - if( (pPrevRCache = pRCache->pPrevInGlobal) != NULL) - { - if( pPrevRCache->pPrevInGlobal) - { - pPrevRCache->pPrevInGlobal->pNextInGlobal = pRCache; - } - else - { - gv_FlmSysData.RCacheMgr.pMRURecord = pRCache; - } - - pRCache->pPrevInGlobal = pPrevRCache->pPrevInGlobal; - pPrevRCache->pPrevInGlobal = pRCache; - pPrevRCache->pNextInGlobal = pRCache->pNextInGlobal; - - if( pRCache->pNextInGlobal) - { - pRCache->pNextInGlobal->pPrevInGlobal = pPrevRCache; - } - else - { - gv_FlmSysData.RCacheMgr.pLRURecord = pPrevRCache; - } - pRCache->pNextInGlobal = pPrevRCache; - } -} - -/**************************************************************************** -Desc: This routine unlinks a record from the global list This routine - assumes that the record cache mutex has already been locked. -****************************************************************************/ -FINLINE void flmRcaUnlinkFromGlobal( - RCACHE * pRCache) -{ - if (pRCache->pNextInGlobal) - { - pRCache->pNextInGlobal->pPrevInGlobal = pRCache->pPrevInGlobal; - } - else - { - gv_FlmSysData.RCacheMgr.pLRURecord = pRCache->pPrevInGlobal; - } - if (pRCache->pPrevInGlobal) - { - pRCache->pPrevInGlobal->pNextInGlobal = pRCache->pNextInGlobal; - } - else - { - gv_FlmSysData.RCacheMgr.pMRURecord = pRCache->pNextInGlobal; - } - pRCache->pPrevInGlobal = pRCache->pNextInGlobal = NULL; -} - -/**************************************************************************** -Desc: This routine unlinks a record from the global purged list This routine - assumes that the record cache mutex has already been locked. -****************************************************************************/ -FINLINE void flmRcaUnlinkFromPurged( - RCACHE * pRCache) -{ - if (pRCache->pNextInGlobal) - { - pRCache->pNextInGlobal->pPrevInGlobal = pRCache->pPrevInGlobal; - } - - if (pRCache->pPrevInGlobal) - { - pRCache->pPrevInGlobal->pNextInGlobal = pRCache->pNextInGlobal; - } - else - { - gv_FlmSysData.RCacheMgr.pPurgeList = pRCache->pNextInGlobal; - } - - pRCache->pPrevInGlobal = NULL; - pRCache->pNextInGlobal = NULL; -} - -/**************************************************************************** -Desc: -****************************************************************************/ -FINLINE void flmRcaLinkToHeapList( - RCACHE * pRCache) -{ - flmAssert( !pRCache->pPrevInHeapList); - flmAssert( !pRCache->pNextInHeapList); - flmAssert( !RCA_IS_IN_HEAP_LIST( pRCache->uiFlags)); - flmAssert( FlmRecordExt::getFlags( pRCache->pRecord) & RCA_HEAP_BUFFER); - - if( (pRCache->pNextInHeapList = gv_FlmSysData.RCacheMgr.pHeapList) != NULL) - { - pRCache->pNextInHeapList->pPrevInHeapList = pRCache; - } - gv_FlmSysData.RCacheMgr.pHeapList = pRCache; - RCA_SET_IN_HEAP_LIST( pRCache->uiFlags); -} - -/**************************************************************************** -Desc: -****************************************************************************/ -FINLINE void flmRcaUnlinkFromHeapList( - RCACHE * pRCache) -{ - flmAssert( RCA_IS_IN_HEAP_LIST( pRCache->uiFlags)); - - if( pRCache->pNextInHeapList) - { - pRCache->pNextInHeapList->pPrevInHeapList = pRCache->pPrevInHeapList; - } - - if( pRCache->pPrevInHeapList) - { - pRCache->pPrevInHeapList->pNextInHeapList = pRCache->pNextInHeapList; - } - else - { - gv_FlmSysData.RCacheMgr.pHeapList = pRCache->pNextInHeapList; - } - - pRCache->pPrevInHeapList = NULL; - pRCache->pNextInHeapList = NULL; - - RCA_UNSET_IN_HEAP_LIST( pRCache->uiFlags); -} - -/**************************************************************************** -Desc: This routine links a record to an FFILE list at the head of the list. - This routine assumes that the record cache mutex has already been - locked. -****************************************************************************/ -FINLINE void flmRcaLinkToFileAtHead( - RCACHE * pRCache, - FFILE * pFile) -{ - pRCache->pPrevInFile = NULL; - if ((pRCache->pNextInFile = pFile->pFirstRecord) != NULL) - { - pFile->pFirstRecord->pPrevInFile = pRCache; - } - else - { - pFile->pLastRecord = pRCache; - } - - pFile->pFirstRecord = pRCache; - pRCache->pFile = pFile; - RCA_SET_LINKED_TO_FILE( pRCache->uiFlags); -} - -/**************************************************************************** -Desc: This routine links a record to an FFILE list at the end of the list. - This routine assumes that the record cache mutex has already been - locked. -****************************************************************************/ -FINLINE void flmRcaLinkToFileAtEnd( - RCACHE * pRCache, - FFILE * pFile) -{ - pRCache->pNextInFile = NULL; - if( (pRCache->pPrevInFile = pFile->pLastRecord) != NULL) - { - pFile->pLastRecord->pNextInFile = pRCache; - } - else - { - pFile->pFirstRecord = pRCache; - } - pFile->pLastRecord = pRCache; - pRCache->pFile = pFile; - RCA_SET_LINKED_TO_FILE( pRCache->uiFlags); -} - -/**************************************************************************** -Desc: This routine unlinks a record from its FFILE list. This routine - assumes that the record cache mutex has already been locked. -****************************************************************************/ -FINLINE void flmRcaUnlinkFromFile( - RCACHE * pRCache) -{ - if( RCA_IS_LINKED_TO_FILE( pRCache->uiFlags)) - { - if( pRCache->pNextInFile) - { - pRCache->pNextInFile->pPrevInFile = pRCache->pPrevInFile; - } - else - { - pRCache->pFile->pLastRecord = pRCache->pPrevInFile; - } - - if( pRCache->pPrevInFile) - { - pRCache->pPrevInFile->pNextInFile = pRCache->pNextInFile; - } - else - { - pRCache->pFile->pFirstRecord = pRCache->pNextInFile; - } - - pRCache->pPrevInFile = pRCache->pNextInFile = NULL; - RCA_UNSET_LINKED_TO_FILE( pRCache->uiFlags); - } -} - -/**************************************************************************** -Desc: This routine links a record into its hash bucket. This routine - assumes that the record cache mutex has already been locked. -****************************************************************************/ -FINLINE void flmRcaLinkToHashBucket( - RCACHE * pRCache) -{ - RCACHE ** ppHashBucket = FLM_RCA_HASH( pRCache->uiDrn); - - flmAssert( pRCache->pNewerVersion == NULL); - - pRCache->pPrevInBucket = NULL; - if( (pRCache->pNextInBucket = *ppHashBucket) != NULL) - { - pRCache->pNextInBucket->pPrevInBucket = pRCache; - } - *ppHashBucket = pRCache; -} - -/**************************************************************************** -Desc: This routine unlinks a record from its hash bucket. This routine - assumes that the record cache mutex has already been locked. -****************************************************************************/ -FINLINE void flmRcaUnlinkFromHashBucket( - RCACHE * pRCache) -{ - flmAssert( pRCache->pNewerVersion == NULL); - if (pRCache->pNextInBucket) - { - pRCache->pNextInBucket->pPrevInBucket = pRCache->pPrevInBucket; - } - - if (pRCache->pPrevInBucket) - { - pRCache->pPrevInBucket->pNextInBucket = pRCache->pNextInBucket; - } - else - { - RCACHE ** ppHashBucket = FLM_RCA_HASH( pRCache->uiDrn); - *ppHashBucket = pRCache->pNextInBucket; - } - pRCache->pPrevInBucket = pRCache->pNextInBucket = NULL; -} - -/**************************************************************************** -Desc: This routine unlinks a record from its version list. This routine - assumes that the record cache mutex has already been locked. -****************************************************************************/ -FINLINE void flmRcaLinkToVerList( - RCACHE * pRCache, - RCACHE * pNewerVer, - RCACHE * pOlderVer) -{ - if( (pRCache->pNewerVersion = pNewerVer) != NULL) - { - pNewerVer->pOlderVersion = pRCache; - } - - if ((pRCache->pOlderVersion = pOlderVer) != NULL) - { - pOlderVer->pNewerVersion = pRCache; - } -} - -/**************************************************************************** -Desc: This routine unlinks a record from its version list. This routine - assumes that the record cache mutex has already been locked. -****************************************************************************/ -FINLINE void flmRcaUnlinkFromVerList( - RCACHE * pRCache) -{ - if (pRCache->pNewerVersion) - { - pRCache->pNewerVersion->pOlderVersion = pRCache->pOlderVersion; - } - - if (pRCache->pOlderVersion) - { - pRCache->pOlderVersion->pNewerVersion = pRCache->pNewerVersion; - } - pRCache->pNewerVersion = pRCache->pOlderVersion = NULL; -} - -/**************************************************************************** -Desc: This routine frees a purged from record cache. This routine assumes - that the record cache mutex has already been locked. -****************************************************************************/ -FSTATIC void flmRcaFreePurged( - RCACHE * pRCache) -{ - FLMUINT uiTotalMemory; - FLMUINT uiFreeMemory; - - // Release the record data object we are pointing to. - - if (pRCache->pRecord) - { - if( RCA_IS_IN_HEAP_LIST( pRCache->uiFlags)) - { - flmRcaUnlinkFromHeapList( pRCache); - } - - uiTotalMemory = pRCache->pRecord->getTotalMemory(); - uiFreeMemory = pRCache->pRecord->getFreeMemory(); - flmAssert( uiTotalMemory >= uiFreeMemory); - FlmRecordExt::clearCached( pRCache->pRecord); - FlmRecordExt::Release( pRCache->pRecord, TRUE); - pRCache->pRecord = NULL; - } - else - { - uiTotalMemory = 0; - uiFreeMemory = 0; - } - - // If this is an old version, decrement the old version counters. - - if (pRCache->uiHighTransId != 0xFFFFFFFF) - { - FLMUINT uiTotalOldMemory = uiTotalMemory + sizeof( RCACHE); - - flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes >= - uiTotalOldMemory); - flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiOldVerCount); - gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes -= uiTotalOldMemory; - gv_FlmSysData.RCacheMgr.Usage.uiOldVerCount--; - } - - // Unlink the RCACHE from the purged list. - - flmRcaUnlinkFromPurged( pRCache); - - // Free the RCACHE structure. - - RCA_UNSET_PURGED( pRCache->uiFlags); - flmRcaFreeCacheStruct( &pRCache); -} - -/**************************************************************************** -Desc: This routine frees a record in the record cache. This routine assumes - that the record cache mutex has already been locked. -****************************************************************************/ -FSTATIC void flmRcaFreeCache( - RCACHE * pRCache, - FLMBOOL bPutInPurgeList) -{ - FLMUINT uiTotalMemory; - FLMUINT uiFreeMemory; - FLMBOOL bOldVersion; -#ifdef FLM_DBG_LOG - char szTmpBuf[ 80]; -#endif - - // Release the record data object we are pointing to. - - if (pRCache->pRecord && !bPutInPurgeList) - { - if( RCA_IS_IN_HEAP_LIST( pRCache->uiFlags)) - { - flmRcaUnlinkFromHeapList( pRCache); - } - - uiTotalMemory = pRCache->pRecord->getTotalMemory(); - uiFreeMemory = pRCache->pRecord->getFreeMemory(); - flmAssert( uiTotalMemory >= uiFreeMemory); - FlmRecordExt::clearCached( pRCache->pRecord); - FlmRecordExt::Release( pRCache->pRecord, TRUE); - pRCache->pRecord = NULL; - } - else - { - uiTotalMemory = 0; - uiFreeMemory = 0; - } - bOldVersion = (FLMBOOL)((pRCache->uiHighTransId != 0xFFFFFFFF) - ? TRUE - : FALSE); - -#ifdef FLM_DBG_LOG - f_sprintf( szTmpBuf, "RCD:H%X", - (unsigned)pRCache->uiHighTransId); - - flmDbgLogWrite( pRCache->pFile ? pRCache->pFile->uiFFileId : 0, pRCache->uiContainer, - pRCache->uiDrn, pRCache->uiLowTransId, szTmpBuf); -#endif - - // Unlink the RCACHE from its various lists. - - flmRcaUnlinkFromGlobal( pRCache); - flmRcaUnlinkFromFile( pRCache); - if (!pRCache->pNewerVersion) - { - RCACHE * pOlderVersion = pRCache->pOlderVersion; - - flmRcaUnlinkFromHashBucket( pRCache); - - // If there was an older version, it now needs to be - // put into the hash bucket. - - if (pOlderVersion) - { - flmRcaUnlinkFromVerList( pRCache); - flmRcaLinkToHashBucket( pOlderVersion); - } - } - else - { - flmRcaUnlinkFromVerList( pRCache); - } - - // Free the RCACHE structure if not putting in purge list. - - if (!bPutInPurgeList) - { - // If this was an older version, decrement the old version counters. - - if (bOldVersion) - { - FLMUINT uiTotalOldMemory = uiTotalMemory + sizeof( RCACHE); - - flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes >= - uiTotalOldMemory); - flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiOldVerCount); - gv_FlmSysData.RCacheMgr.Usage.uiOldVerCount--; - gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes -= - uiTotalOldMemory; - } - flmRcaFreeCacheStruct( &pRCache); - } - else - { - if ((pRCache->pNextInGlobal = gv_FlmSysData.RCacheMgr.pPurgeList) != NULL) - { - pRCache->pNextInGlobal->pPrevInGlobal = pRCache; - } - gv_FlmSysData.RCacheMgr.pPurgeList = pRCache; - RCA_SET_PURGED( pRCache->uiFlags); - } -} - -/**************************************************************************** -Desc: This routine initializes record cache manager. -****************************************************************************/ -RCODE flmRcaInit( - FLMUINT uiMaxRecordCacheBytes) -{ - RCODE rc = FERR_OK; - - f_memset( &gv_FlmSysData.RCacheMgr, 0, sizeof( RCACHE_MGR)); - gv_FlmSysData.RCacheMgr.Usage.uiMaxBytes = uiMaxRecordCacheBytes; - gv_FlmSysData.RCacheMgr.hMutex = F_MUTEX_NULL; - - // Allocate the hash buckets. - - if (RC_BAD( rc = f_calloc( - (FLMUINT)sizeof( RCACHE *) * - (FLMUINT)MIN_RCACHE_BUCKETS, - &gv_FlmSysData.RCacheMgr.ppHashBuckets))) - { - goto Exit; - } - gv_FlmSysData.RCacheMgr.uiNumBuckets = MIN_RCACHE_BUCKETS; - gv_FlmSysData.RCacheMgr.uiHashMask = - gv_FlmSysData.RCacheMgr.uiNumBuckets - 1; - gv_FlmSysData.RCacheMgr.Usage.uiTotalBytesAllocated += - (sizeof( RCACHE *) * gv_FlmSysData.RCacheMgr.uiNumBuckets); - - // Allocate the mutex for controlling access to the - // record cache. - - if (RC_BAD( rc = f_mutexCreate( &gv_FlmSysData.RCacheMgr.hMutex))) - { - goto Exit; - } - - // Set up the RCACHE struct allocator - - if( (gv_FlmSysData.RCacheMgr.pRCacheAlloc = f_new F_FixedAlloc) == NULL) - { - rc = RC_SET( FERR_MEM); - goto Exit; - } - - if( RC_BAD( rc = gv_FlmSysData.RCacheMgr.pRCacheAlloc->setup( - gv_FlmSysData.pSlabManager, TRUE, sizeof( RCACHE), - &gv_FlmSysData.RCacheMgr.Usage.uiTotalBytesAllocated))) - { - goto Exit; - } - - gv_FlmSysData.RCacheMgr.pRCacheAlloc->setRelocationFuncs( - rcaCanRelocate, rcaRelocate); - - // Set up the record object allocator - - if( (gv_FlmSysData.RCacheMgr.pRecAlloc = f_new F_FixedAlloc) == NULL) - { - rc = RC_SET( FERR_MEM); - goto Exit; - } - - if( RC_BAD( rc = gv_FlmSysData.RCacheMgr.pRecAlloc->setup( - gv_FlmSysData.pSlabManager, - gv_FlmSysData.RCacheMgr.pRCacheAlloc->getMutex(), - sizeof( FlmRecord), - &gv_FlmSysData.RCacheMgr.Usage.uiTotalBytesAllocated))) - { - goto Exit; - } - - gv_FlmSysData.RCacheMgr.pRecAlloc->setRelocationFuncs( - FlmRecordExt::canRelocateRec, FlmRecordExt::relocateRec); - - // Set up the record buffer allocator - - if( (gv_FlmSysData.RCacheMgr.pRecBufAlloc = f_new F_BufferAlloc) == NULL) - { - rc = RC_SET( FERR_MEM); - goto Exit; - } - - if( RC_BAD( rc = gv_FlmSysData.RCacheMgr.pRecBufAlloc->setup( - gv_FlmSysData.pSlabManager, - gv_FlmSysData.RCacheMgr.pRCacheAlloc->getMutex(), - &gv_FlmSysData.RCacheMgr.Usage.uiTotalBytesAllocated))) - { - goto Exit; - } - - gv_FlmSysData.RCacheMgr.pRecBufAlloc->setRelocationFuncs( - FlmRecordExt::canRelocateRecBuffer, - FlmRecordExt::relocateRecBuffer); - -#ifdef FLM_DEBUG - gv_FlmSysData.RCacheMgr.bDebug = TRUE; -#endif - -Exit: - if (RC_BAD( rc)) - { - flmRcaExit(); - } - - return( rc); -} - -/**************************************************************************** -Desc: This routine determines what hash table size best fits the current - record count. It finds the hash bucket size whose midpoint between - the minimum and maximum range is closest to the record count. -****************************************************************************/ -FSTATIC FLMUINT flmRcaGetBestHashTblSize( - FLMUINT uiCurrRecCount) -{ - FLMUINT uiHashTblSize; - FLMUINT uiMaxRecsForHashTblSize; - FLMUINT uiMinRecsForHashTblSize; - FLMUINT uiClosestHashTblSize = 0; - FLMUINT uiDistanceFromMidpoint; - FLMUINT uiLowestDistanceFromMidpoint; - FLMUINT uiHashTblRecsMidpoint; - - uiLowestDistanceFromMidpoint = 0xFFFFFFFF; - for (uiHashTblSize = MIN_RCACHE_BUCKETS; - uiHashTblSize <= MAX_RCACHE_BUCKETS; - uiHashTblSize *= 2) - { - - // Maximum desirable record count for a specific hash table size - // we have arbitrarily chosen to be four times the number of buckets. - // Minimum desirable record count we have arbitrarily chosen to be - // the hash table size divided by four. - - uiMaxRecsForHashTblSize = FLM_RCA_MAX_REC_CNT( uiHashTblSize); - uiMinRecsForHashTblSize = FLM_RCA_MIN_REC_CNT( uiHashTblSize); - - // Ignore any hash bucket sizes where the current record count - // is not between the desired minimum and maximum. - - if (uiCurrRecCount >= uiMinRecsForHashTblSize && - uiCurrRecCount <= uiMaxRecsForHashTblSize) - { - - // Calculate the midpoint between the minimum and maximum - // for this particular hash table size. - - uiHashTblRecsMidpoint = (uiMaxRecsForHashTblSize - - uiMinRecsForHashTblSize) / 2; - - // See how far our current record count is from this midpoint. - - uiDistanceFromMidpoint = (FLMUINT)((uiHashTblRecsMidpoint > uiCurrRecCount) - ? (uiHashTblRecsMidpoint - uiCurrRecCount) - : (uiCurrRecCount - uiHashTblRecsMidpoint)); - - // If the distance from the midpoint is closer than our previous - // lowest distance, save it. - - if (uiDistanceFromMidpoint < uiLowestDistanceFromMidpoint) - { - uiClosestHashTblSize = uiHashTblSize; - uiLowestDistanceFromMidpoint = uiDistanceFromMidpoint; - } - } - } - - // Take the number of buckets whose middle was closest to the - // current record count; - - if (uiLowestDistanceFromMidpoint == 0xFFFFFFFF) - { - // If we did not fall between any of the minimums or maximums, - // we are either below the lowest minimum, or higher than the - // highest maximum. - - uiHashTblSize = - (FLMUINT)((uiCurrRecCount < FLM_RCA_MIN_REC_CNT( MIN_RCACHE_BUCKETS)) - ? (FLMUINT)MIN_RCACHE_BUCKETS - : (FLMUINT)MAX_RCACHE_BUCKETS); - - } - else - { - uiHashTblSize = uiClosestHashTblSize; - } - return( uiHashTblSize); -} - -/**************************************************************************** -Desc: This routine resizes the hash table for the record cache manager. - NOTE: This routine assumes that the record cache mutex has been locked. -****************************************************************************/ -FSTATIC RCODE flmRcaRehash( void) -{ - RCODE rc = FERR_OK; - FLMUINT uiNewHashTblSize; - RCACHE ** ppOldHashTbl; - FLMUINT uiOldHashTblSize; - RCACHE ** ppBucket; - FLMUINT uiLoop; - RCACHE * pTmpRCache; - RCACHE * pTmpNextRCache; - - uiNewHashTblSize = flmRcaGetBestHashTblSize( - gv_FlmSysData.RCacheMgr.Usage.uiCount); - - // At this point we better have a different hash table size - // or something is mucked up! - - flmAssert( uiNewHashTblSize != - gv_FlmSysData.RCacheMgr.uiNumBuckets); - - // Save the old hash table and its size. - - ppOldHashTbl = gv_FlmSysData.RCacheMgr.ppHashBuckets; - uiOldHashTblSize = gv_FlmSysData.RCacheMgr.uiNumBuckets; - - // Allocate a new hash table. - - if (RC_BAD( rc = f_calloc( - (FLMUINT)sizeof( RCACHE *) * - (FLMUINT)uiNewHashTblSize, - &gv_FlmSysData.RCacheMgr.ppHashBuckets))) - { - gv_FlmSysData.RCacheMgr.ppHashBuckets = ppOldHashTbl; - goto Exit; - } - - // Subtract off old size and add in new size. - - gv_FlmSysData.RCacheMgr.pRCacheAlloc->decrementTotalBytesAllocated( - (sizeof( RCACHE *) * uiOldHashTblSize)); - gv_FlmSysData.RCacheMgr.pRCacheAlloc->incrementTotalBytesAllocated( - (sizeof( RCACHE *) * uiNewHashTblSize)); - - gv_FlmSysData.RCacheMgr.uiNumBuckets = uiNewHashTblSize; - gv_FlmSysData.RCacheMgr.uiHashMask = uiNewHashTblSize - 1; - - // Relink all of the records into the new - // hash table. - - for (uiLoop = 0, ppBucket = ppOldHashTbl; - uiLoop < uiOldHashTblSize; - uiLoop++, ppBucket++) - { - pTmpRCache = *ppBucket; - while (pTmpRCache) - { - pTmpNextRCache = pTmpRCache->pNextInBucket; - flmRcaLinkToHashBucket( pTmpRCache); - pTmpRCache = pTmpNextRCache; - } - } - - // Throw away the old hash table. - - f_free( &ppOldHashTbl); - -Exit: - - return( rc); -} - -/**************************************************************************** -Desc: This routine changes the cache size for the record cache manager. - If necessary, it will resize the hash table. NOTE: This routine - assumes that the record cache mutex has been locked. -****************************************************************************/ -FSTATIC RCODE flmRcaSetMemLimit( - FLMUINT uiMaxCacheBytes) -{ - RCODE rc = FERR_OK; - - // If we are shrinking the maximum cache, clean up and - // defragment cache first - - gv_FlmSysData.RCacheMgr.Usage.uiMaxBytes = uiMaxCacheBytes; - if (gv_FlmSysData.RCacheMgr.pRCacheAlloc->getTotalBytesAllocated() > - uiMaxCacheBytes) - { - flmRcaCleanupCache( ~((FLMUINT)0), TRUE); - } - - // If the current record count is below the minimum records for the - // number of buckets or is greater than the maximum records for the - // number of buckets, we want to resize the hash table. - - if ((gv_FlmSysData.RCacheMgr.Usage.uiCount > - FLM_RCA_MAX_REC_CNT( gv_FlmSysData.RCacheMgr.uiNumBuckets) && - gv_FlmSysData.RCacheMgr.uiNumBuckets < MAX_RCACHE_BUCKETS) || - (gv_FlmSysData.RCacheMgr.Usage.uiCount < - FLM_RCA_MIN_REC_CNT( gv_FlmSysData.RCacheMgr.uiNumBuckets) && - gv_FlmSysData.RCacheMgr.uiNumBuckets > MIN_RCACHE_BUCKETS)) - { - if (RC_BAD( rc = flmRcaRehash())) - { - goto Exit; - } - } - -Exit: - - return( rc); -} - -/**************************************************************************** -Desc: This routine configures the record cache manager. NOTE: This routine - assumes that the record cache mutex has been locked. -****************************************************************************/ -RCODE flmRcaConfig( - FLMUINT uiType, - void * Value1, - void * Value2) -{ - RCODE rc = FERR_OK; - - F_UNREFERENCED_PARM( Value2); - - switch (uiType) - { - case FLM_CACHE_LIMIT: - rc = flmRcaSetMemLimit( (FLMUINT)Value1); - break; - case FLM_SCACHE_DEBUG: -#ifdef FLM_DEBUG - gv_FlmSysData.RCacheMgr.bDebug = (FLMBOOL)(Value1 ? - (FLMBOOL)TRUE : (FLMBOOL)FALSE); -#endif - break; - default: - rc = RC_SET( FERR_NOT_IMPLEMENTED); - goto Exit; - } - -Exit: - - return( rc); -} - -/**************************************************************************** -Desc: This routine shuts down the record cache manager and frees all - resources allocated by it. -****************************************************************************/ -void flmRcaExit( void) -{ - if (gv_FlmSysData.RCacheMgr.hMutex != F_MUTEX_NULL) - { - f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); - } - - // Free all of the record cache objects. - - while (gv_FlmSysData.RCacheMgr.pMRURecord) - { - f_yieldCPU(); - flmRcaFreeCache( gv_FlmSysData.RCacheMgr.pMRURecord, FALSE); - } - - // Must free those in the purge list too. - - while (gv_FlmSysData.RCacheMgr.pPurgeList) - { - f_yieldCPU(); - flmRcaFreePurged( gv_FlmSysData.RCacheMgr.pPurgeList); - } - - // The math better be consistent! - - flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiCount == 0); - flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiOldVerCount == 0); - flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes == 0); - - // Free the hash bucket array - - if (gv_FlmSysData.RCacheMgr.ppHashBuckets) - { - f_free( &gv_FlmSysData.RCacheMgr.ppHashBuckets); - gv_FlmSysData.RCacheMgr.pRCacheAlloc->decrementTotalBytesAllocated( - (sizeof( RCACHE *) * gv_FlmSysData.RCacheMgr.uiNumBuckets)); - } - - // Free the mutex that controls access to record cache. - // NOTE: This should be done last so that the mutex is not - // unlocked until the very end. - - if (gv_FlmSysData.RCacheMgr.hMutex != F_MUTEX_NULL) - { - f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); - f_mutexDestroy( &gv_FlmSysData.RCacheMgr.hMutex); - } - - // Free the allocators - - if( gv_FlmSysData.RCacheMgr.pRecBufAlloc) - { - gv_FlmSysData.RCacheMgr.pRecBufAlloc->Release(); - gv_FlmSysData.RCacheMgr.pRecBufAlloc = NULL; - } - - if( gv_FlmSysData.RCacheMgr.pRecAlloc) - { - gv_FlmSysData.RCacheMgr.pRecAlloc->Release(); - gv_FlmSysData.RCacheMgr.pRecAlloc = NULL; - } - - if( gv_FlmSysData.RCacheMgr.pRCacheAlloc) - { - gv_FlmSysData.RCacheMgr.pRCacheAlloc->Release(); - gv_FlmSysData.RCacheMgr.pRCacheAlloc = NULL; - } - - // Zero the entire structure out, just for good measure. - - f_memset( &gv_FlmSysData.RCacheMgr, 0, sizeof( RCACHE_MGR)); -} - -/**************************************************************************** -Desc: This routine links a notify request into a notification list and - then waits to be notified that the event has occurred. - NOTE: This routine assumes that the record cache mutex is locked and that - it is supposed to unlock it. It will relock the mutex on its way out. -****************************************************************************/ -FSTATIC RCODE flmRcaWaitNotify( - FNOTIFY ** ppNotifyListRV) -{ - FNOTIFY * pNotify = NULL; - RCODE TempRc; - RCODE rc = FERR_OK; - F_SEM hSem; - - // First create a notify request and link it into the list. - - if (RC_OK( rc = f_calloc( (FLMUINT)(sizeof( FNOTIFY)), &pNotify))) - { - // Allocate a semaphore for the notify request. - - pNotify->uiThreadId = f_threadId(); - pNotify->hSem = F_SEM_NULL; - rc = f_semCreate( &pNotify->hSem); - } - - if (RC_BAD( rc)) - { - if (pNotify) - { - if (pNotify->hSem != F_SEM_NULL) - { - f_semDestroy( &pNotify->hSem); - } - - f_free( &pNotify); - } - - goto Exit; - } - - pNotify->pRc = &rc; - pNotify->UserData = NULL; - pNotify->pNext = *ppNotifyListRV; - *ppNotifyListRV = pNotify; - hSem = pNotify->hSem; - - // Unlock the mutex and wait on the semaphore. - - f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); - if (RC_BAD( TempRc = f_semWait( hSem, F_SEM_WAITFOREVER))) - { - rc = TempRc; - } - - // Free the semaphore and the notify structure. - - f_semDestroy( &hSem); - f_free( &pNotify); - - // Relock the record cache mutex - - f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); - -Exit: - - return( rc); -} - -/**************************************************************************** -Desc: This routine notifies threads waiting for a pending read to complete. - NOTE: This routine assumes that the record cache mutex is already - locked. -****************************************************************************/ -FSTATIC void flmRcaNotify( - FNOTIFY * pNotify, - RCACHE * pUseRCache, - RCODE NotifyRc) -{ - while (pNotify) - { - F_SEM hSem; - - *(pNotify->pRc) = NotifyRc; - if (RC_OK( NotifyRc)) - { - RCA_INCR_USE_COUNT( pUseRCache->uiFlags); - } - hSem = pNotify->hSem; - pNotify = pNotify->pNext; - f_semSignal( hSem); - } -} - -/**************************************************************************** -Desc: Allocate a new record cache structure. This routine assumes that the - record cache mutex has already been locked. -****************************************************************************/ -FSTATIC RCODE flmRcaAllocCacheStruct( - RCACHE ** ppRCache) -{ - RCODE rc = FERR_OK; - - if( (*ppRCache = - (RCACHE *)gv_FlmSysData.RCacheMgr.pRCacheAlloc->allocCell()) == NULL) - { - rc = RC_SET( FERR_MEM); - goto Exit; - } - - f_memset( *ppRCache, 0, sizeof( RCACHE)); - - // Increment the total records cached - - gv_FlmSysData.RCacheMgr.Usage.uiCount++; - - // Set the high transaction ID to 0xFFFFFFFF so that this will NOT - // be treated as one that had memory assigned to the old records. - - (*ppRCache)->uiHighTransId = 0xFFFFFFFF; - -Exit: - - return( rc); -} - -/**************************************************************************** -Desc: Free a record cache structure. This routine assumes that the record - cache mutex has already been locked. -****************************************************************************/ -FSTATIC void flmRcaFreeCacheStruct( - RCACHE ** ppRCache) -{ - flmAssert( !RCA_IS_IN_HEAP_LIST( (*ppRCache)->uiFlags)); - - gv_FlmSysData.RCacheMgr.pRCacheAlloc->freeCell( *ppRCache); - *ppRCache = NULL; - - flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiCount > 0); - gv_FlmSysData.RCacheMgr.Usage.uiCount--; -} - -/**************************************************************************** -Desc: Cleanup old records in cache that are no longer needed by any - transaction. -****************************************************************************/ -void flmRcaCleanupCache( - FLMUINT uiMaxLockTime, - FLMBOOL bMutexesLocked) -{ - RCACHE * pTmpRCache; - RCACHE * pPrevRCache; - RCACHE * pNextRCache; - FLMUINT uiRecordsExamined = 0; - FLMUINT uiLastTimePaused = FLM_GET_TIMER(); - FLMUINT uiCurrTime; - FLMBOOL bUnlockMutexes = FALSE; - - if( !bMutexesLocked) - { - f_mutexLock( gv_FlmSysData.hShareMutex); - f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); - bUnlockMutexes = TRUE; - } - - // Try to free everything in the heap list - - pTmpRCache = gv_FlmSysData.RCacheMgr.pHeapList; - - while( pTmpRCache) - { - uiRecordsExamined++; - - // Save the pointer to the next entry in the list because - // we may end up unlinking pTmpRCache below - - pNextRCache = pTmpRCache->pNextInHeapList; - - // Determine if the item can be freed - - flmAssert( RCA_IS_IN_HEAP_LIST( pTmpRCache->uiFlags)); - - if( !RCA_IS_IN_USE( pTmpRCache->uiFlags) && - !RCA_IS_READING_IN( pTmpRCache->uiFlags)) - { - flmRcaFreeCache( pTmpRCache, FALSE); - } - - pTmpRCache = pNextRCache; - } - - // Now, free any old versions that are no longer needed - - flmRcaReduceCache( TRUE); - pTmpRCache = gv_FlmSysData.RCacheMgr.pLRURecord; - - // Stay in the loop until we have freed all old records, or - // we have run through the entire list. - - for( ;;) - { - if( !pTmpRCache || !gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes) - { - break; - } - - // After each 200 records examined, see if our maximum - // time has elapsed for examining without a pause. - - if( uiRecordsExamined >= 200) - { - uiRecordsExamined = 0; - uiCurrTime = FLM_GET_TIMER(); - - if( FLM_ELAPSED_TIME( uiCurrTime, uiLastTimePaused) >= uiMaxLockTime) - { - // IMPORTANT! Don't stop and pause on one that is - // being read in. - - while( pTmpRCache && RCA_IS_READING_IN( pTmpRCache->uiFlags)) - { - pTmpRCache = pTmpRCache->pPrevInGlobal; - } - - if( !pTmpRCache) - { - break; - } - - if( bUnlockMutexes) - { - // Increment the use count so that this item will not - // go away while we are paused. - - RCA_INCR_USE_COUNT( pTmpRCache->uiFlags); - f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); - f_mutexUnlock( gv_FlmSysData.hShareMutex); - - // Shortest possible pause - to allow other threads - // to do work. - - #ifdef FLM_NLM - f_yieldCPU(); - #else - f_sleep( 0); - #endif - - // Relock the mutexes - - uiLastTimePaused = FLM_GET_TIMER(); - f_mutexLock( gv_FlmSysData.hShareMutex); - f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); - - // Decrement use count that was added above. - - RCA_DECR_USE_COUNT( pTmpRCache->uiFlags); - } - - // If the item was purged while we were paused, - // finish the job and then start again at the - // top of the list. - - if( RCA_IS_PURGED( pTmpRCache->uiFlags)) - { - if( !RCA_IS_IN_USE( pTmpRCache->uiFlags)) - { - flmRcaFreePurged( pTmpRCache); - } - - pTmpRCache = gv_FlmSysData.RCacheMgr.pLRURecord; - continue; - } - } - } - - uiRecordsExamined++; - - // Save the pointer to the previous entry in the list because - // we may end up unlinking pTmpRCache below, in which case we would - // have lost the previous entry. - - pPrevRCache = pTmpRCache->pPrevInGlobal; - - // Block must not currently be in use, - // Must not be the most current version of a block, - // Cannot be dirty in any way, - // Cannot be in the process of being read in from disk, - // And must not be needed by a read transaction. - - if( !RCA_IS_IN_USE( pTmpRCache->uiFlags) && - !RCA_IS_READING_IN( pTmpRCache->uiFlags) && - (RCA_IS_IN_HEAP_LIST( pTmpRCache->uiFlags) || - (pTmpRCache->uiHighTransId != 0xFFFFFFFF && - !flmNeededByReadTrans( pTmpRCache->pFile, - pTmpRCache->uiLowTransId, - pTmpRCache->uiHighTransId)))) - { - flmRcaFreeCache( pTmpRCache, FALSE); - } - - pTmpRCache = pPrevRCache; - } - - // Defragment memory - - gv_FlmSysData.RCacheMgr.pRCacheAlloc->defragmentMemory(); - gv_FlmSysData.RCacheMgr.pRecAlloc->defragmentMemory(); - gv_FlmSysData.RCacheMgr.pRecBufAlloc->defragmentMemory(); - - // Unlock the mutexes - - if( bUnlockMutexes) - { - f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); - f_mutexUnlock( gv_FlmSysData.hShareMutex); - } -} - -/**************************************************************************** -Desc: This routine reduces record cache down to the limit expected. -****************************************************************************/ -void flmRcaReduceCache( - FLMBOOL bMutexAlreadyLocked) -{ - RCACHE * pRCache; - RCACHE * pPrevRCache; - - // Make sure the mutex is locked. - - if (!bMutexAlreadyLocked) - { - f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); - } - - pRCache = gv_FlmSysData.RCacheMgr.pLRURecord; - - // Free things until we get down below our memory limit. - - while( gv_FlmSysData.RCacheMgr.pRCacheAlloc->getTotalBytesAllocated() > - gv_FlmSysData.RCacheMgr.Usage.uiMaxBytes) - { - if( !pRCache) - { - break; - } - - // If the total of block and record cache is below the global - // cache maximum, there is no need to reduce record cache. - - if( (gv_FlmSysData.RCacheMgr.pRCacheAlloc->getTotalBytesAllocated() + - gv_FlmSysData.SCacheMgr.Usage.uiTotalBytesAllocated) <= - gv_FlmSysData.uiMaxCache) - { - break; - } - - pPrevRCache = pRCache->pPrevInGlobal; - if (!(RCA_IS_IN_USE( pRCache->uiFlags)) && - !(RCA_IS_READING_IN( pRCache->uiFlags))) - { - flmRcaFreeCache( pRCache, FALSE); - } - pRCache = pPrevRCache; - } - - if (!bMutexAlreadyLocked) - { - f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); - } -} - -/**************************************************************************** -Desc: This routine finds a record in the record cache. If it cannot - find the record, it will return the position where the record should - be inserted. NOTE: This routine assumes that the record cache mutex - has been locked. -****************************************************************************/ -void flmRcaFindRec( - FLMUINT uiContainer, - FLMUINT uiDrn, - FFILE * pFile, - FLMUINT uiVersionNeeded, - FLMBOOL bDontPoisonCache, - FLMUINT * puiNumLooks, - RCACHE ** ppRCache, - RCACHE ** ppNewerRCache, - RCACHE ** ppOlderRCache) -{ - RCACHE * pRCache; - FLMUINT uiNumLooks = 0; - FLMBOOL bFound; - RCACHE * pNewerRCache; - RCACHE * pOlderRCache; - - // Search down the hash bucket for the matching item. - -Start_Find: - - // NOTE: Need to always calculate hash bucket because - // the hash table may have been changed while we - // were waiting to be notified below - mutex can - // be unlocked, but it is guaranteed to be locked - // here. - - pRCache = *(FLM_RCA_HASH( uiDrn)); - bFound = FALSE; - uiNumLooks = 1; - while ((pRCache) && - ((pRCache->uiDrn != uiDrn) || - (pRCache->uiContainer != uiContainer) || - (pRCache->pFile != pFile))) - { - if ((pRCache = pRCache->pNextInBucket) != NULL) - { - uiNumLooks++; - } - } - - // If we found the record, see if we have the right version. - - if (!pRCache) - { - pNewerRCache = pOlderRCache = NULL; - } - else - { - pNewerRCache = NULL; - pOlderRCache = pRCache; - for (;;) - { - - // If this one is being read in, we need to wait on it. - - if (RCA_IS_READING_IN( pRCache->uiFlags)) - { - // We need to wait for this record to be read in - // in case it coalesces with other versions, resulting - // in a version that satisfies our request. - - gv_FlmSysData.RCacheMgr.uiIoWaits++; - if (RC_BAD( flmRcaWaitNotify( &pRCache->pNotifyList))) - { - - // Don't care what error the other thread had reading - // the thing in from disk - we'll bail out and start - // our find again. - - goto Start_Find; - } - - // The thread doing the notify "uses" the record cache - // on behalf of this thread to prevent the record - // from being replaced after it unlocks the mutex. - // At this point, since we have locked the mutex, - // we need to release the record - because we - // will put a "use" on it below. - - RCA_DECR_USE_COUNT( pRCache->uiFlags); - - if (RCA_IS_PURGED( pRCache->uiFlags)) - { - if (!RCA_IS_IN_USE( pRCache->uiFlags)) - { - flmRcaFreePurged( pRCache); - } - } - - // Start over with the find because the list - // structure has changed. - - goto Start_Find; - } - - // See if this record version is the one we need. - - if (uiVersionNeeded < pRCache->uiLowTransId) - { - pNewerRCache = pRCache; - if ((pOlderRCache = pRCache = pRCache->pOlderVersion) == NULL) - { - break; - } - uiNumLooks++; - } - else if (uiVersionNeeded <= pRCache->uiHighTransId) - { - // Make this the MRU record. - - if (puiNumLooks) - { - if (bDontPoisonCache) - { - flmRcaStepUpInGlobalList( pRCache); - } - else if (pRCache->pPrevInGlobal) - { - flmRcaUnlinkFromGlobal( pRCache); - flmRcaLinkToGlobalAsMRU( pRCache); - } - - gv_FlmSysData.RCacheMgr.Usage.uiCacheHits++; - gv_FlmSysData.RCacheMgr.Usage.uiCacheHitLooks += uiNumLooks; - } - - bFound = TRUE; - break; - } - else - { - pOlderRCache = pRCache; - pNewerRCache = pRCache->pNewerVersion; - - // Set pRCache to NULL as an indicator that we did not - // find the version we needed. - - pRCache = NULL; - break; - } - } - } - - *ppRCache = pRCache; - *ppOlderRCache = pOlderRCache; - *ppNewerRCache = pNewerRCache; - - if (puiNumLooks) - { - *puiNumLooks = uiNumLooks; - } -} - -/**************************************************************************** -Desc: This routine replaces the FlmRecord that a record is pointing to - with a new one. NOTE: This routine assumes that the record cache - mutex is already locked. -****************************************************************************/ -FSTATIC void flmRcaSetRecord( - RCACHE * pRCache, - FlmRecord * pNewRecord) -{ - FLMUINT uiTotalMemory = 0; - FLMUINT uiFreeMemory; - FlmRecord * pOldRecord; - - // Release the cache's pointer to the old record data - - if ((pOldRecord = pRCache->pRecord) != NULL) - { - if( RCA_IS_IN_HEAP_LIST( pRCache->uiFlags)) - { - flmRcaUnlinkFromHeapList( pRCache); - } - - uiTotalMemory = pOldRecord->getTotalMemory(); - uiFreeMemory = pOldRecord->getFreeMemory(); - flmAssert( uiTotalMemory >= uiFreeMemory); - FlmRecordExt::clearCached( pOldRecord); - FlmRecordExt::Release( pOldRecord, TRUE); - pRCache->pRecord = NULL; - } - - if (pRCache->uiHighTransId != 0xFFFFFFFF) - { - FLMUINT uiTotalOldMemory = uiTotalMemory + sizeof( RCACHE); - - flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes >= - uiTotalOldMemory); - flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiOldVerCount); - gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes -= - uiTotalOldMemory; - gv_FlmSysData.RCacheMgr.Usage.uiOldVerCount--; - } - - // Point to the new record data. - - flmAssert( pNewRecord->getID() == pRCache->uiDrn); - flmAssert( pNewRecord->getContainerID() == pRCache->uiContainer); - pRCache->pRecord = pNewRecord; - flmAssert( !pNewRecord->isCached()); - FlmRecordExt::setCached( pNewRecord); - FlmRecordExt::AddRef( pNewRecord, TRUE); - FlmRecordExt::setReadOnly( pNewRecord); - - if( FlmRecordExt::getFlags( pNewRecord) & RCA_HEAP_BUFFER) - { - flmRcaLinkToHeapList( pRCache); - } - - uiTotalMemory = pNewRecord->getTotalMemory(); - - if (pRCache->uiHighTransId != 0xFFFFFFFF) - { - gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes += - (uiTotalMemory + sizeof( RCACHE)); - gv_FlmSysData.RCacheMgr.Usage.uiOldVerCount++; - } - uiFreeMemory = pNewRecord->getFreeMemory(); - flmAssert( uiTotalMemory >= uiFreeMemory); -} - -/**************************************************************************** -Desc: This routine links a new RCACHE structure into the global list and - into the correct place in its hash bucket. This routine assumes that - the record cache mutex is already locked. -****************************************************************************/ -FSTATIC void flmRcaLinkIntoRCache( - RCACHE * pNewerRCache, - RCACHE * pOlderRCache, - RCACHE * pRCache, - FLMBOOL bLinkAsMRU) -{ - if( bLinkAsMRU) - { - flmRcaLinkToGlobalAsMRU( pRCache); - } - else - { - flmRcaLinkToGlobalAsLRU( pRCache); - } - - if (pNewerRCache) - { - flmRcaLinkToVerList( pRCache, pNewerRCache, pOlderRCache); - } - else - { - RCACHE * pNull = NULL; - - if (pOlderRCache) - { - flmRcaUnlinkFromHashBucket( pOlderRCache); - } - flmRcaLinkToHashBucket( pRCache); - flmRcaLinkToVerList( pRCache, pNull, pOlderRCache); - } -} - -/**************************************************************************** -Desc: This routine links a new record to its FFILE according to whether - or not it is an update transaction or a read transaction. - It coalesces out any unnecessary versions. This routine assumes - that the record cache mutex is already locked. -****************************************************************************/ -FSTATIC void flmRcaLinkToFFILE( - RCACHE * pRCache, - FFILE * pFile, - FDB * pDb, - FLMUINT uiLowTransId, - FLMBOOL bMostCurrent) -{ - RCACHE * pTmpRCache; -#ifdef FLM_DBG_LOG - char szTmpBuf[ 80]; -#endif - - - pRCache->uiLowTransId = uiLowTransId; - - // Before coalescing, link to FFILE. - // The following test determines if the record is an - // uncommitted version generated by the update transaction. - // If so, we mark it as such, and link it at the head of the - // FFILE list - so we can get rid of it quickly if we abort - // the transaction. - - if (flmGetDbTransType( pDb) == FLM_UPDATE_TRANS) - { - - // If we are in an update transaction, there better not - // be any newer versions in the list and the high - // transaction ID returned better be 0xFFFFFFFF. - - flmAssert( pRCache->pNewerVersion == NULL); - flmRcaSetTransID( pRCache, 0xFFFFFFFF); - - // If the low transaction ID is the same as the transaction, - // we may have modified this record during the transaction. - // Unfortunately, there is no sure way to tell, so we are - // forced to assume it may have been modified. If the - // transaction aborts, we will get rid if this version out - // of cache. - - if (uiLowTransId == pDb->LogHdr.uiCurrTransID) - { - RCA_SET_UNCOMMITTED( pRCache->uiFlags); - flmRcaLinkToFileAtHead( pRCache, pFile); - } - else - { - RCA_UNSET_UNCOMMITTED( pRCache->uiFlags); - flmRcaLinkToFileAtEnd( pRCache, pFile); - } -#ifdef FLM_DBG_LOG - f_sprintf( szTmpBuf, "RCI:L%X,H%X", - (unsigned)pRCache->uiLowTransId, - (unsigned)pRCache->uiHighTransId); - - flmDbgLogWrite( pFile->uiFFileId, pRCache->uiContainer, pRCache->uiDrn, - pDb->LogHdr.uiCurrTransID, szTmpBuf); -#endif - } - else - { - // Adjust the high transaction ID to be the same as - // the transaction ID - we may have gotten a 0xFFFFFFF - // back, but that is possible even if the record is - // not the most current version. Besides that, it is - // possible that in the mean time one or more update - // transactions have come along and created one or - // more newer versions of the record. - - FLMUINT uiHighTransId = (FLMUINT)((bMostCurrent) - ? (FLMUINT)0xFFFFFFFF - : pDb->LogHdr.uiCurrTransID); - - flmRcaSetTransID( pRCache, uiHighTransId); - - // For a read transaction, if there is a newer version, - // it better have a higher "low transaction ID" - -#ifdef FLM_DBG_LOG - f_sprintf( szTmpBuf, "RCA:L%X,H%X", - (unsigned)pRCache->uiLowTransId, - (unsigned)pRCache->uiHighTransId); - - flmDbgLogWrite( pFile->uiFFileId, pRCache->uiContainer, pRCache->uiDrn, - pDb->LogHdr.uiCurrTransID, szTmpBuf); -#endif - -#ifdef FLM_DEBUG - if (pRCache->pNewerVersion && - !RCA_IS_READING_IN( pRCache->pNewerVersion->uiFlags)) - { - flmAssert( pRCache->uiHighTransId < - pRCache->pNewerVersion->uiLowTransId); - if( pRCache->uiHighTransId >= - pRCache->pNewerVersion->uiLowTransId) - { - flmRcaCheck( pDb, pRCache->uiContainer, pRCache->uiDrn); - } - } -#endif - RCA_UNSET_UNCOMMITTED( pRCache->uiFlags); - flmRcaLinkToFileAtEnd( pRCache, pFile); - } - - // Coalesce any versions that overlap - can only - // coalesce older versions. For an updater, there - // should not be any newer versions. For a reader, it - // is impossible to know how high up it can coalesce. - // The read operation that read the record may have - // gotten back a 0xFFFFFFFF for its high transaction - // ID - but after that point in time, it is possible - // that one or more update transactions may have come - // along and created one or more newer versions that - // it would be incorrect to coalesce with. - // In reality, a read transaction has to ignore the - // 0xFFFFFFFF in the high transaction ID anyway - // because there is no way to know if it is correct. - - // Coalesce older versions. - - for (;;) - { - if ((pTmpRCache = pRCache->pOlderVersion) == NULL) - { - break; - } - - // Stop if we encounter one that is being read in. - - if (RCA_IS_READING_IN( pTmpRCache->uiFlags)) - { - break; - } - - // If there is no overlap between these two, there is - // nothing more to coalesce. - - if (pRCache->uiLowTransId > pTmpRCache->uiHighTransId) - { - break; - } - - if (pRCache->uiHighTransId <= pTmpRCache->uiHighTransId) - { - // This assert represents the following case, - // which should not be possible to hit: - - // pOlder->uiHighTransId > pRCache->uiHighTransId. - // This cannot be, because if pOlder has a higher - // transaction ID, we would have found it up above and - // not tried to have read it in. - - flmAssert( 0); -#ifdef FLM_DEBUG - flmRcaCheck( pDb, pRCache->uiContainer, pRCache->uiDrn); -#endif - } - else if (pRCache->uiLowTransId >= pTmpRCache->uiLowTransId) - { - pRCache->uiLowTransId = pTmpRCache->uiLowTransId; - flmRcaFreeCache( pTmpRCache, - (FLMBOOL)((RCA_IS_IN_USE( pTmpRCache->uiFlags) || - RCA_IS_READING_IN( pTmpRCache->uiFlags)) - ? TRUE - : FALSE)); - } - else - { - // This assert represents the following case, - // which should not be possible to hit: - - // pRCache->uiLowTransId < pOlder->uiLowTransId. - // This cannot be, because pOlder has to have been read - // in to memory by a transaction whose transaction ID is - // less than or equal to our own. That being the case, - // it would be impossible for our transaction to have - // found a version of the record that is older than pOlder. - - flmAssert( 0); -#ifdef FLM_DEBUG - flmRcaCheck( pDb, pRCache->uiContainer, pRCache->uiDrn); -#endif - } - } -} - -/**************************************************************************** -Desc: This routine retrieves a record from the record cache. -****************************************************************************/ -RCODE flmRcaRetrieveRec( - FDB * pDb, - FLMBOOL * pbTransStarted, - FLMUINT uiContainer, // Container record is in. - FLMUINT uiDrn, // DRN of record. - FLMBOOL bOkToGetFromDisk, // If not in cache, OK to get from disk? - BTSK * pStack, // Use stack to retrieve, if NON-NULL. - LFILE * pLFile, // LFILE to use, if retrieving with stack - FlmRecord ** ppRecord) -{ - RCODE rc = FERR_OK; - FLMBOOL bRCacheMutexLocked = FALSE; - FFILE * pFile = pDb->pFile; - RCACHE * pRCache; - RCACHE * pNewerRCache; - RCACHE * pOlderRCache; - FlmRecord * pRecord = NULL; - FLMBOOL bGotFromDisk = FALSE; - FlmRecord * pNewRecord = NULL; - FlmRecord * pOldRecord = NULL; - FLMUINT uiLowTransId; - FLMBOOL bMostCurrent; - FLMUINT uiCurrTransId; - FNOTIFY * pNotify; - FLMUINT uiNumLooks; - FLMBOOL bInitializedFdb = FALSE; - FLMBOOL bDontPoisonCache = pDb->uiFlags & FDB_DONT_POISON_CACHE - ? TRUE - : FALSE; - - if( RC_BAD( rc = flmCheckDatabaseState( pDb))) - { - goto Exit; - } - - // Get the current transaction ID - - if( pDb->uiTransType != FLM_NO_TRANS) - { - uiCurrTransId = pDb->LogHdr.uiCurrTransID; - } - else - { - flmAssert( pbTransStarted != NULL); - - f_mutexLock( gv_FlmSysData.hShareMutex); - - // Get the last committed transaction ID. - - uiCurrTransId = (FLMUINT)FB2UD( - &pFile->ucLastCommittedLogHdr[ LOG_CURR_TRANS_ID]); - - f_mutexUnlock( gv_FlmSysData.hShareMutex); - } - - flmAssert( uiDrn != 0); - - // Lock the mutex - - f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); - bRCacheMutexLocked = TRUE; - - // Reset the FDB's inactive time - - pDb->uiInactiveTime = 0; - - // See if we should resize the hash table. - - if ((gv_FlmSysData.RCacheMgr.Usage.uiCount > - FLM_RCA_MAX_REC_CNT( gv_FlmSysData.RCacheMgr.uiNumBuckets) && - gv_FlmSysData.RCacheMgr.uiNumBuckets < MAX_RCACHE_BUCKETS) || - (gv_FlmSysData.RCacheMgr.Usage.uiCount < - FLM_RCA_MIN_REC_CNT( gv_FlmSysData.RCacheMgr.uiNumBuckets) && - gv_FlmSysData.RCacheMgr.uiNumBuckets > MIN_RCACHE_BUCKETS)) - { - if (RC_BAD( rc = flmRcaRehash())) - { - goto Exit; - } - } - -Start_Find: - - flmRcaFindRec( uiContainer, uiDrn, pFile, - uiCurrTransId, bDontPoisonCache, - &uiNumLooks, &pRCache, - &pNewerRCache, &pOlderRCache); - - if (pRCache) - { - if( ppRecord) - { - goto Found_Record; - } - goto Exit; - } - - // Did not find the record, fetch from disk, if OK to do so. - - if (!bOkToGetFromDisk || !ppRecord) - { - rc = RC_SET( FERR_NOT_FOUND); - goto Exit; - } - - // Code to handle case where we are not in a transaction. - // If we are already in a transaction, we will do the - // call to fdbInit below - AFTER allocating the RCACHE, etc. - - if( pbTransStarted && pDb->uiTransType == FLM_NO_TRANS) - { - f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); - bRCacheMutexLocked = FALSE; - - if ( RC_BAD( rc = fdbInit( pDb, FLM_READ_TRANS, - FDB_TRANS_GOING_OK, 0, pbTransStarted))) - { - fdbExit( pDb); - goto Exit; - } - bInitializedFdb = TRUE; - - f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); - bRCacheMutexLocked = TRUE; - - uiCurrTransId = pDb->LogHdr.uiCurrTransID; - goto Start_Find; - } - - // Increment the number of faults only if we retrieve the record from disk. - - gv_FlmSysData.RCacheMgr.Usage.uiCacheFaults++; - gv_FlmSysData.RCacheMgr.Usage.uiCacheFaultLooks += uiNumLooks; - - // Create a place holder for the object. - - if (RC_BAD( rc = flmRcaAllocCacheStruct( &pRCache))) - { - goto Exit; - } - pRCache->uiDrn = uiDrn; - pRCache->uiContainer = uiContainer; - - // Set the FFILE so that other threads looking for this record in - // cache will find it and wait until the read has completed. If - // the FFILE is not set, other threads will attempt their own read, - // because they won't match a NULL FFILE. The result of not setting - // the FFILE is that multiple copies of the same version of a particular - // record could end up in cache. - - pRCache->pFile = pFile; - - flmRcaLinkIntoRCache( pNewerRCache, pOlderRCache, - pRCache, !bDontPoisonCache); - - RCA_SET_READING_IN( pRCache->uiFlags); - RCA_INCR_USE_COUNT( pRCache->uiFlags); - pRCache->pNotifyList = NULL; - - // Unlock mutex before reading in from disk. - - f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); - bRCacheMutexLocked = FALSE; - - if( pbTransStarted && !bInitializedFdb) - { - if ( RC_BAD( rc = fdbInit( pDb, FLM_READ_TRANS, - FDB_TRANS_GOING_OK, 0, pbTransStarted))) - { - fdbExit( pDb); - goto Notify_Waiters; - } - bInitializedFdb = TRUE; - } - - // Read record from disk. - -#ifdef FLM_DBG_LOG - flmDbgLogWrite( pFile->uiFFileId, uiContainer, - uiDrn, pDb->LogHdr.uiCurrTransID, "RRD"); -#endif - - if (pStack) - { - rc = FSReadElement( pDb, &pDb->TempPool, pLFile, - uiDrn, pStack, TRUE, ppRecord, - &uiLowTransId, &bMostCurrent); - } - else if ((pLFile) || - (RC_OK( rc = fdictGetContainer( pDb->pDict, uiContainer, &pLFile)))) - { - rc = FSReadRecord( pDb, pLFile, uiDrn, ppRecord, - &uiLowTransId, &bMostCurrent); - } - -Notify_Waiters: - - // Relock mutex - - f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); - bRCacheMutexLocked = TRUE; - - // If read was successful, link the record to its place in - // the FFILE list and coalesce any versions that overlap - // this one. - - if (RC_OK( rc)) - { - flmRcaLinkToFFILE( pRCache, - pDb->pFile, pDb, uiLowTransId, bMostCurrent); - } - - RCA_UNSET_READING_IN( pRCache->uiFlags); - - // Notify any threads waiting for the read to complete. - - pNotify = pRCache->pNotifyList; - pRCache->pNotifyList = NULL; - if (pNotify) - { - flmRcaNotify( pNotify, - (RCACHE *)((RC_BAD( rc)) - ? NULL - : pRCache), rc); - } - RCA_DECR_USE_COUNT( pRCache->uiFlags); - - // If we did not succeed, free the RCACHE structure. - - if (RC_BAD( rc)) - { - flmRcaFreeCache( pRCache, FALSE); - goto Exit; - } - - // If this item was purged while we were reading it in, - // start over with the search. - - if (RCA_IS_PURGED( pRCache->uiFlags)) - { - if (!RCA_IS_IN_USE( pRCache->uiFlags)) - { - flmRcaFreePurged( pRCache); - } - - // Start over with the find - this one has - // been marked for purging. - - pNewRecord = NULL; - pOldRecord = NULL; - bGotFromDisk = FALSE; - goto Start_Find; - } - - // When reading from disk, no need to verify that we - // are getting the app implementation - because we - // always will. - - bGotFromDisk = TRUE; - pRecord = *ppRecord; - flmRcaSetRecord( pRCache, pRecord); - -Found_Record: - - if (!bGotFromDisk) - { - if( (pRecord = *ppRecord) != pRCache->pRecord) - { - if (*ppRecord) - { - FlmRecordExt::Release( *ppRecord, bRCacheMutexLocked); - } - - pRecord = *ppRecord = pRCache->pRecord; - FlmRecordExt::AddRef( pRecord, bRCacheMutexLocked); - } - } - - // Clean up cache, if necessary. - - if (gv_FlmSysData.RCacheMgr.pRCacheAlloc->getTotalBytesAllocated() > - gv_FlmSysData.RCacheMgr.Usage.uiMaxBytes) - { - flmRcaReduceCache( bRCacheMutexLocked); - } - -Exit: - - if (pNewRecord) - { - FlmRecordExt::Release( pNewRecord, bRCacheMutexLocked); - } - - if (bRCacheMutexLocked) - { - f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); - } - - if (bInitializedFdb) - { - fdbExit(pDb); - } - - return( rc); -} - -/**************************************************************************** -Desc: This routine inserts a record into the record cache. This is ONLY - called by FlmRecordModify or FlmRecordAdd. In the case of a modify, - this should replace any uncommitted version of the record that may - have been put there by a prior call to FlmRecordModify. -****************************************************************************/ -RCODE flmRcaInsertRec( - FDB * pDb, - LFILE * pLFile, // Container record is in. - FLMUINT uiDrn, // DRN of record - FlmRecord * pRecord) // Record to be inserted. -{ - RCODE rc = FERR_OK; - FFILE * pFile = pDb->pFile; - FLMUINT uiContainer = pLFile->uiLfNum; - FLMBOOL bMutexLocked = FALSE; - RCACHE * pRCache; - RCACHE * pNewerRCache; - RCACHE * pOlderRCache; - FLMBOOL bDontPoisonCache = pDb->uiFlags & FDB_DONT_POISON_CACHE - ? TRUE - : FALSE; - - if (pLFile->bMakeFieldIdTable && !pRecord->fieldIdTableEnabled()) - { - - // NOTE: createFieldIdTable will call sortFieldIdTable(). - - if (RC_BAD( rc = pRecord->createFieldIdTable( TRUE))) - { - goto Exit; - } - } - else - { - pRecord->sortFieldIdTable(); - if (getFieldIdTableItemCount( pRecord->getFieldIdTbl()) != - getFieldIdTableArraySize( pRecord->getFieldIdTbl())) - { - if (RC_BAD( rc = pRecord->truncateFieldIdTable())) - { - goto Exit; - } - } - } - flmAssert( uiDrn != 0); - - // Lock the mutex - - f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); - bMutexLocked = TRUE; - - if ((gv_FlmSysData.RCacheMgr.Usage.uiCount > - FLM_RCA_MAX_REC_CNT( gv_FlmSysData.RCacheMgr.uiNumBuckets) && - gv_FlmSysData.RCacheMgr.uiNumBuckets < MAX_RCACHE_BUCKETS) || - (gv_FlmSysData.RCacheMgr.Usage.uiCount < - FLM_RCA_MIN_REC_CNT( gv_FlmSysData.RCacheMgr.uiNumBuckets) && - gv_FlmSysData.RCacheMgr.uiNumBuckets > MIN_RCACHE_BUCKETS)) - { - if (RC_BAD( rc = flmRcaRehash())) - { - goto Exit; - } - } - - // See if we can find the record in cache - - flmRcaFindRec( uiContainer, uiDrn, pFile, - pDb->LogHdr.uiCurrTransID, bDontPoisonCache, NULL, &pRCache, - &pNewerRCache, &pOlderRCache); - - if (pRCache) - { - - // If we found the last committed version, instead of replacing it, - // we want to change its high transaction ID, and go create a new - // record to put in cache. - - if (pRCache->uiLowTransId < pDb->LogHdr.uiCurrTransID) - { - - // pOlderRCache and pRCache should be the same at this point if we - // found something. Furthermore, the high transaction ID on what - // we found better be -1 - most current version. - - flmAssert( pOlderRCache == pRCache); - flmAssert( pOlderRCache->uiHighTransId == 0xFFFFFFFF); - - flmRcaSetTransID( pOlderRCache, (pDb->LogHdr.uiCurrTransID - 1)); - - flmAssert( pOlderRCache->uiHighTransId >= pOlderRCache->uiLowTransId); - - RCA_SET_UNCOMMITTED( pOlderRCache->uiFlags); - RCA_SET_LATEST_VER( pOlderRCache->uiFlags); - flmRcaUnlinkFromFile( pOlderRCache); - flmRcaLinkToFileAtHead( pOlderRCache, pFile); - } - else - { - // Found latest UNCOMMITTED VERSION - replace it. - -#ifdef FLM_CHECK_RECORD - if (RC_BAD( rc = pRecord->checkRecord())) - { - goto Exit; - } -#endif - - if (RC_BAD( rc = pRecord->compressMemory())) - { - goto Exit; - } - -#ifdef FLM_CHECK_RECORD - if (RC_BAD( rc = pRecord->checkRecord())) - { - goto Exit; - } -#endif - - // Replace the old record data with the new record data. - - flmRcaSetRecord( pRCache, pRecord); - - // Make sure we set the "uncommitted" flag and move the record - // to the head of the FFILE's list. - - if (!RCA_IS_UNCOMMITTED( pRCache->uiFlags)) - { - RCA_SET_UNCOMMITTED( pRCache->uiFlags); - flmRcaUnlinkFromFile( pRCache); - flmRcaLinkToFileAtHead( pRCache, pFile); - } - - // Will not have already been put at MRU if bDonPoisonCache is TRUE. - - if (pRCache->pPrevInGlobal) - { - flmRcaUnlinkFromGlobal( pRCache); - flmRcaLinkToGlobalAsMRU( pRCache); - } - - goto Exit; - } - } - - // We are positioned to insert the new record. For an update, it - // must always be the newest version. - - flmAssert( pNewerRCache == NULL); - -#ifdef FLM_CHECK_RECORD - if (RC_BAD( rc = pRecord->checkRecord())) - { - goto Exit; - } -#endif - - if (RC_BAD( rc = pRecord->compressMemory())) - { - goto Exit; - } - -#ifdef FLM_CHECK_RECORD - if (RC_BAD( rc = pRecord->checkRecord())) - { - goto Exit; - } -#endif - - // Allocate a new RCACHE structure. - - if (RC_BAD( rc = flmRcaAllocCacheStruct( &pRCache))) - { - goto Exit; - } - - // Set the DRN and container for the structure. - - pRCache->uiDrn = uiDrn; - pRCache->uiContainer = uiContainer; - pRCache->pFile = pFile; - - // Link into the global list and hash bucket. - - flmRcaLinkIntoRCache( pNewerRCache, pOlderRCache, pRCache, TRUE); - - // Link to its FFILE and coalesce out duplicates. - // NOTE: This routine automatically puts an uncommitted version - // at the head of the FFILE's list and sets the uncommitted flag. - - flmRcaLinkToFFILE( pRCache, pFile, pDb, pDb->LogHdr.uiCurrTransID, TRUE); - - // Set the record data pointer for the RCACHE structure. This will also - // release the old data pointer and set the read only flag and the mutex - // for the record data. Also updates memory usage fields in RCacheMgr. - - flmRcaSetRecord( pRCache, pRecord); - - // Clean up cache, if necessary. - - flmRcaReduceCache( bMutexLocked); - -Exit: - - if (bMutexLocked) - { - f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); - } - - return( rc); -} - -/**************************************************************************** -Desc: This routine is called by FlmRecordDelete to remove a record from - cache. If there is an uncommitted version of the record, it should - remove that version from cache. If the last committed version is in - cache, it should set the high transaction ID on that version to be - one less than the transaction ID of the update transaction that is - doing the FlmRecordDelete call. -****************************************************************************/ -RCODE flmRcaRemoveRec( - FDB * pDb, - FLMUINT uiContainer, - FLMUINT uiDrn) -{ - RCODE rc = FERR_OK; - FLMBOOL bMutexLocked = FALSE; - RCACHE * pRCache; - RCACHE * pNewerRCache; - RCACHE * pOlderRCache; - FFILE * pFile = pDb->pFile; - - flmAssert( uiDrn != 0); - - // Lock the semaphore - - bMutexLocked = TRUE; - f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); - - if ((gv_FlmSysData.RCacheMgr.Usage.uiCount > - FLM_RCA_MAX_REC_CNT( gv_FlmSysData.RCacheMgr.uiNumBuckets) && - gv_FlmSysData.RCacheMgr.uiNumBuckets < MAX_RCACHE_BUCKETS) || - (gv_FlmSysData.RCacheMgr.Usage.uiCount < - FLM_RCA_MIN_REC_CNT( gv_FlmSysData.RCacheMgr.uiNumBuckets) && - gv_FlmSysData.RCacheMgr.uiNumBuckets > MIN_RCACHE_BUCKETS)) - { - if (RC_BAD( rc = flmRcaRehash())) - { - goto Exit; - } - } - - // See if we can find the record in cache - - flmRcaFindRec( uiContainer, uiDrn, pFile, - pDb->LogHdr.uiCurrTransID, FALSE, NULL, &pRCache, - &pNewerRCache, &pOlderRCache); - - if (pRCache) - { - // FlmRecordDelete is calling this routine, so we determine if we found - // the last committed version or a record that was added by this same - // transaction. If we found the last committed version, set its high - // transaction ID. Otherwise, remove the record from cache. - - if (pRCache->uiLowTransId < pDb->LogHdr.uiCurrTransID) - { - - // pOlderRCache and pRCache should be the same at this point if we - // found something. Furthermore, the high transaction ID on what - // we found better be -1 - most current version. - - flmAssert( pOlderRCache == pRCache); - flmAssert( pOlderRCache->uiHighTransId == 0xFFFFFFFF); - - flmRcaSetTransID( pOlderRCache, (pDb->LogHdr.uiCurrTransID - 1)); - flmAssert( pOlderRCache->uiHighTransId >= pOlderRCache->uiLowTransId); - RCA_SET_UNCOMMITTED( pOlderRCache->uiFlags); - RCA_SET_LATEST_VER( pOlderRCache->uiFlags); - flmRcaUnlinkFromFile( pOlderRCache); - flmRcaLinkToFileAtHead( pOlderRCache, pFile); - } - else - { - flmRcaFreeCache( pRCache, - (FLMBOOL)(RCA_IS_IN_USE( pRCache->uiFlags) - ? TRUE - : FALSE)); - } - } - - // Take the opportunity to clean up cache. - - flmRcaReduceCache( bMutexLocked); - -Exit: - - if (bMutexLocked) - { - f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); - } - - return( rc); -} - -/**************************************************************************** -Desc: This routine is called when an FFILE structure is going to be removed - from the shared memory area. At that point, we also need to get rid - of all records that have been cached for that FFILE. -****************************************************************************/ -void flmRcaFreeFileRecs( - FFILE * pFile) -{ - FLMUINT uiNumFreed = 0; - - f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); - while (pFile->pFirstRecord) - { - flmRcaFreeCache( pFile->pFirstRecord, - RCA_IS_IN_USE( pFile->pFirstRecord->uiFlags) - ? TRUE - : FALSE); - - // Release the CPU every 100 records freed. - - if (uiNumFreed < 100) - { - uiNumFreed++; - } - else - { - f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); - f_yieldCPU(); - f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); - uiNumFreed = 0; - } - } - f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); -} - -/**************************************************************************** -Desc: This routine is called when an update transaction aborts. At that - point, we need to get rid of any uncommitted versions of records in - the record cache. -****************************************************************************/ -void flmRcaAbortTrans( - FDB * pDb) -{ - FFILE * pFile = pDb->pFile; - RCACHE * pRCache; - RCACHE * pOlderVersion; - FLMUINT uiOlderTransId = - pDb->LogHdr.uiCurrTransID - 1; - - f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); - pRCache = pFile->pFirstRecord; - while (pRCache) - { - if (RCA_IS_UNCOMMITTED( pRCache->uiFlags)) - { - if (RCA_IS_LATEST_VER( pRCache->uiFlags)) - { - flmRcaSetTransID( pRCache, 0xFFFFFFFF); - RCA_UNSET_UNCOMMITTED( pRCache->uiFlags); - RCA_UNSET_LATEST_VER( pRCache->uiFlags); - flmRcaUnlinkFromFile( pRCache); - flmRcaLinkToFileAtEnd( pRCache, pFile); - } - else - { - // Save the older version - we may be changing its - // high transaction ID back to 0xFFFFFFFF - - pOlderVersion = pRCache->pOlderVersion; - - // Free the uncommitted version. - - flmRcaFreeCache( pRCache, - (FLMBOOL)((RCA_IS_IN_USE( pRCache->uiFlags) || - RCA_IS_READING_IN( pRCache->uiFlags)) - ? TRUE - : FALSE)); - - // If the older version has a high transaction ID that - // is exactly one less than our current transaction, - // it is the most current version. Hence, we need to - // change its high transaction ID back to 0xFFFFFFFF. - - if ((pOlderVersion) && - (pOlderVersion->uiHighTransId == uiOlderTransId)) - { - flmRcaSetTransID( pOlderVersion, 0xFFFFFFFF); - } - } - pRCache = pFile->pFirstRecord; - } - else - { - // We can stop when we hit a committed version, because - // uncommitted versions are always linked in together at - // the head of the list. - - break; - } - } - f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); -} - -/**************************************************************************** -Desc: This routine is called when an update transaction commits. At that - point, we need to unset the "uncommitted" flag on any records - currently in record cache for the FFILE. -****************************************************************************/ -void flmRcaCommitTrans( - FDB * pDb) -{ - RCACHE * pRCache; - - f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); - pRCache = pDb->pFile->pFirstRecord; - while (pRCache) - { - if (RCA_IS_UNCOMMITTED( pRCache->uiFlags)) - { - RCA_UNSET_UNCOMMITTED( pRCache->uiFlags); - RCA_UNSET_LATEST_VER( pRCache->uiFlags); - pRCache = pRCache->pNextInFile; - } - else - { - - // We can stop when we hit a committed version, because - // uncommitted versions are always linked in together at - // the head of the list. - - break; - } - } - f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); -} - -/**************************************************************************** -Desc: This routine is called when a container in the database is deleted. - All records in record cache that are in that container must be - removed from cache. -****************************************************************************/ -void flmRcaRemoveContainerRecs( - FDB * pDb, - FLMUINT uiContainer) -{ - FFILE * pFile = pDb->pFile; - RCACHE * pRCache; - RCACHE * pPrevRCache; - FLMUINT uiTransId = pDb->LogHdr.uiCurrTransID; - - f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); - pRCache = gv_FlmSysData.RCacheMgr.pLRURecord; - - // Stay in the loop until we have freed all old records, or - // we have run through the entire list. - - while (pRCache) - { - // Save the pointer to the previous entry in the list because - // we may end up unlinking pRCache below, in which case we would - // have lost the previous entry. - - pPrevRCache = pRCache->pPrevInGlobal; - - // Only look at records in this container and this database. - - if ((pRCache->uiContainer == uiContainer) && - (pRCache->pFile == pFile)) - { - - // Only look at the most current versions. - - if (pRCache->uiHighTransId == 0xFFFFFFFF) - { - - // Better not be a newer version. - - flmAssert( pRCache->pNewerVersion == NULL); - - if (pRCache->uiLowTransId < uiTransId) - { - - // This version was not added or modified by this - // transaction so it's high transaction ID should simply - // be set to one less than the current transaction ID. - - flmRcaSetTransID( pRCache, uiTransId - 1); - flmAssert( pRCache->uiHighTransId >= pRCache->uiLowTransId); - RCA_SET_UNCOMMITTED( pRCache->uiFlags); - RCA_SET_LATEST_VER( pRCache->uiFlags); - flmRcaUnlinkFromFile( pRCache); - flmRcaLinkToFileAtHead( pRCache, pFile); - } - else - { - - // The record was added or modified in this - // transaction. Simply remove it from cache. - - flmRcaFreeCache( pRCache, - (FLMBOOL)(RCA_IS_IN_USE( pRCache->uiFlags) - ? TRUE - : FALSE)); - } - } - else - { - - // If not most current version, the record's high transaction - // ID better already be less than transaction ID. - - flmAssert( pRCache->uiHighTransId < uiTransId); - } - } - pRCache = pPrevRCache; - - } - f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); -} - -/**************************************************************************** -Desc: -****************************************************************************/ -#ifdef FLM_DEBUG -FSTATIC RCODE flmRcaCheck( - FDB * pDb, - FLMUINT uiContainer, - FLMUINT uiDrn) -{ - LFILE * pLFile; - FlmRecord * pRecord = NULL; - FLMUINT uiLowTransId; - FLMBOOL bMostCurrent; - RCODE rc; - - if( RC_OK( rc = fdictGetContainer( pDb->pDict, uiContainer, &pLFile))) - { - rc = FSReadRecord( pDb, pLFile, uiDrn, &pRecord, - &uiLowTransId, &bMostCurrent); - } - - if( pRecord) - { - pRecord->Release(); - } - - return( rc); -} -#endif - -/**************************************************************************** -Desc: -****************************************************************************/ -FLMBOOL FlmRecordExt::canRelocateRec( - void * pvAlloc) -{ - FlmRecord * pRec = (FlmRecord *)pvAlloc; - - if( pRec->getRefCount() == 1 && pRec->isCached()) - { - return( TRUE); - } - - return( FALSE); -} - -/**************************************************************************** -Desc: -****************************************************************************/ -void FlmRecordExt::relocateRec( - void * pvOldAlloc, - void * pvNewAlloc) -{ - FlmRecord * pOldRec = (FlmRecord *)pvOldAlloc; - FlmRecord * pNewRec = (FlmRecord *)pvNewAlloc; - RCACHE * pRCache; - RCACHE * pVersion; - - flmAssert( pOldRec->getRefCount() == 1); - flmAssert( pOldRec->isCached()); - flmAssert( pvNewAlloc < pvOldAlloc); - - // Update the record pointer in the data buffer - - if( pNewRec->m_pucBuffer) - { - flmAssert( *((FlmRecord **)pOldRec->m_pucBuffer) == pOldRec); - *((FlmRecord **)pNewRec->m_pucBuffer) = pNewRec; - } - if( pNewRec->m_pucFieldIdTable) - { - flmAssert( *((FlmRecord **)pOldRec->m_pucFieldIdTable) == pOldRec); - *((FlmRecord **)pNewRec->m_pucFieldIdTable) = pNewRec; - } - - // Find the record - - pRCache = *(FLM_RCA_HASH( pNewRec->m_uiRecordID)); - - while( pRCache) - { - if( pRCache->uiDrn == pNewRec->m_uiRecordID) - { - pVersion = pRCache; - - while( pVersion) - { - if( pVersion->pRecord == pOldRec) - { - pVersion->pRecord = pNewRec; - goto Done; - } - - pVersion = pVersion->pOlderVersion; - } - } - - pRCache = pRCache->pNextInBucket; - } - -Done: - - flmAssert( pRCache); -} - -/**************************************************************************** -Desc: -****************************************************************************/ -FLMBOOL FlmRecordExt::canRelocateRecBuffer( - void * pvAlloc) -{ - FlmRecord * pRec = *((FlmRecord **)pvAlloc); - - flmAssert( pRec->m_pucBuffer == pvAlloc || pRec->m_pucFieldIdTable == pvAlloc); - - if( pRec->getRefCount() == 1 && pRec->isCached()) - { - return( TRUE); - } - - return( FALSE); -} - -/**************************************************************************** -Desc: -****************************************************************************/ -void FlmRecordExt::relocateRecBuffer( - void * pvOldAlloc, - void * pvNewAlloc) -{ - FlmRecord * pRec = *((FlmRecord **)pvOldAlloc); - - flmAssert( pRec->getRefCount() == 1); - flmAssert( pRec->isCached()); - flmAssert( pvNewAlloc < pvOldAlloc); - - // Update the buffer pointer in the record - - if (pRec->m_pucBuffer == pvOldAlloc) - { - pRec->m_pucBuffer = (FLMBYTE *)pvNewAlloc; - } - else if (pRec->m_pucFieldIdTable == pvOldAlloc) - { - pRec->m_pucFieldIdTable = (FLMBYTE *)pvNewAlloc; - } - else - { - flmAssert( 0); - } -} - -/**************************************************************************** -Desc: -Notes: This routine assumes the rcache mutex is locked -****************************************************************************/ -FSTATIC FLMBOOL rcaCanRelocate( - void * pvAlloc) -{ - RCACHE * pRCache = (RCACHE *)pvAlloc; - - if( RCA_IS_IN_USE( pRCache->uiFlags) || - RCA_IS_READING_IN( pRCache->uiFlags)) - { - return( FALSE); - } - - return( TRUE); -} - -/**************************************************************************** -Desc: Fixes up all pointers needed to allow an RCACHE struct to be - moved to a different location in memory -Notes: This routine assumes the rcache mutex is locked -****************************************************************************/ -FSTATIC void rcaRelocate( - void * pvOldAlloc, - void * pvNewAlloc) -{ - RCACHE * pOldRCache = (RCACHE *)pvOldAlloc; - RCACHE * pNewRCache = (RCACHE *)pvNewAlloc; - RCACHE ** ppBucket; - RCACHE_MGR * pRCacheMgr = &gv_FlmSysData.RCacheMgr; - FFILE * pFile = pOldRCache->pFile; - - flmAssert( !RCA_IS_IN_USE( pOldRCache->uiFlags)); - flmAssert( !RCA_IS_READING_IN( pOldRCache->uiFlags)); - flmAssert( !pOldRCache->pNotifyList); - - if( pNewRCache->pPrevInFile) - { - pNewRCache->pPrevInFile->pNextInFile = pNewRCache; - } - - if( pNewRCache->pNextInFile) - { - pNewRCache->pNextInFile->pPrevInFile = pNewRCache; - } - - if( pNewRCache->pPrevInGlobal) - { - pNewRCache->pPrevInGlobal->pNextInGlobal = pNewRCache; - } - - if( pNewRCache->pNextInGlobal) - { - pNewRCache->pNextInGlobal->pPrevInGlobal = pNewRCache; - } - - if( pNewRCache->pPrevInBucket) - { - pNewRCache->pPrevInBucket->pNextInBucket = pNewRCache; - } - - if( pNewRCache->pNextInBucket) - { - pNewRCache->pNextInBucket->pPrevInBucket = pNewRCache; - } - - if( pNewRCache->pOlderVersion) - { - pNewRCache->pOlderVersion->pNewerVersion = pNewRCache; - } - - if( pNewRCache->pNewerVersion) - { - pNewRCache->pNewerVersion->pOlderVersion = pNewRCache; - } - - if( pNewRCache->pPrevInHeapList) - { - pNewRCache->pPrevInHeapList->pNextInHeapList = pNewRCache; - } - - if( pNewRCache->pNextInHeapList) - { - pNewRCache->pNextInHeapList->pPrevInHeapList = pNewRCache; - } - - ppBucket = FLM_RCA_HASH( pOldRCache->uiDrn); - if( *ppBucket == pOldRCache) - { - *ppBucket = pNewRCache; - } - - if( pRCacheMgr->pHeapList == pOldRCache) - { - pRCacheMgr->pHeapList = pNewRCache; - } - - if( pRCacheMgr->pPurgeList == pOldRCache) - { - pRCacheMgr->pPurgeList = pNewRCache; - } - - if( pRCacheMgr->pMRURecord == pOldRCache) - { - pRCacheMgr->pMRURecord = pNewRCache; - } - - if( pRCacheMgr->pLRURecord == pOldRCache) - { - pRCacheMgr->pLRURecord = pNewRCache; - } - - if( pFile) - { - if( pFile->pFirstRecord == pOldRCache) - { - pFile->pFirstRecord = pNewRCache; - } - - if( pFile->pLastRecord == pOldRCache) - { - pFile->pLastRecord = pNewRCache; - } - } - -#ifdef FLM_DEBUG - f_memset( pOldRCache, 0, sizeof( RCACHE)); -#endif -} +//------------------------------------------------------------------------- +// Desc: Record caching +// Tabs: 3 +// +// Copyright (c) 1999-2006 Novell, Inc. All Rights Reserved. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of version 2 of the GNU General Public +// License as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, contact Novell, Inc. +// +// To contact Novell about this file by physical or electronic mail, +// you may find current contact information at www.novell.com +// +// $Id: rcache.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +#if defined( FLM_NLM) && !defined( __MWERKS__) + // Disable "Warning! W549: col(XX) 'sizeof' operand contains + // compiler generated information" + + #pragma warning 549 9 +#endif + +FSTATIC FLMBOOL rcaCanRelocate( + void * pvAlloc); + +FSTATIC void rcaRelocate( + void * pvOldAlloc, + void * pvNewAlloc); + +/**************************************************************************** +Desc: Extended record object for accessing private members of FlmRecord +****************************************************************************/ +struct FlmRecordExt +{ + static FINLINE void clearCached( + FlmRecord * pRec) + { + pRec->clearCached(); + } + + static FINLINE void setCached( + FlmRecord * pRec) + { + pRec->setCached(); + } + + static FINLINE void setReadOnly( + FlmRecord * pRec) + { + pRec->setReadOnly(); + } + + static FINLINE FLMINT AddRef( + FlmRecord * pRec, + FLMBOOL bMutexLocked) + { + return( pRec->AddRef( bMutexLocked)); + } + + static FINLINE FLMINT Release( + FlmRecord * pRec, + FLMBOOL bMutexLocked) + { + return( pRec->Release( bMutexLocked)); + } + + static FINLINE void setOldVersion( + FlmRecord * pRec) + { + pRec->setOldVersion(); + } + + static FINLINE void clearOldVersion( + FlmRecord * pRec) + { + pRec->clearOldVersion(); + } + + static FINLINE FLMUINT getFlags( + FlmRecord * pRec) + { + return( pRec->m_uiFlags); + } + + static FLMBOOL canRelocateRec( + void * pvAlloc); + + static void relocateRec( + void * pvOldAlloc, + void * pvNewAlloc); + + static FLMBOOL canRelocateRecBuffer( + void * pvAlloc); + + static void relocateRecBuffer( + void * pvOldAlloc, + void * pvNewAlloc); +}; + +// Functions for calculating minimum and maximum record counts for a +// given hash table size. + +#define FLM_RCA_MIN_REC_CNT(uiHashTblSz) \ + ((uiHashTblSz) / 4) + +#define FLM_RCA_MAX_REC_CNT(uiHashTblSz) \ + ((uiHashTblSz) * 4) + +// Hash function for hashing to records in record cache. + +#define FLM_RCA_HASH( uiDrn) \ + (RCACHE **)(&(gv_FlmSysData.RCacheMgr.ppHashBuckets[(uiDrn) & \ + (gv_FlmSysData.RCacheMgr.uiHashMask)])) + +// Local functions + +FSTATIC void flmRcaFreePurged( + RCACHE * pRCache); + +FSTATIC void flmRcaFreeCache( + RCACHE * pRCache, + FLMBOOL bPutInPurgeList); + +FSTATIC FLMUINT flmRcaGetBestHashTblSize( + FLMUINT uiCurrRecCount); + +FSTATIC RCODE flmRcaRehash( void); + +FSTATIC RCODE flmRcaSetMemLimit( + FLMUINT uiMaxCacheBytes); + +FSTATIC RCODE flmRcaWaitNotify( + FNOTIFY ** ppNotifyListRV); + +FSTATIC void flmRcaNotify( + FNOTIFY * pNotify, + RCACHE * pUseRCache, + RCODE NotifyRc); + +FSTATIC RCODE flmRcaAllocCacheStruct( + RCACHE ** ppRCache); + +FSTATIC void flmRcaFreeCacheStruct( + RCACHE ** ppRCache); + +FSTATIC void flmRcaSetRecord( + RCACHE * pRCache, + FlmRecord * pNewRecord); + +FSTATIC void flmRcaLinkIntoRCache( + RCACHE * pNewerRCache, + RCACHE * pOlderRCache, + RCACHE * pRCache, + FLMBOOL bLinkAsMRU); + +FSTATIC void flmRcaLinkToFFILE( + RCACHE * pRCache, + FFILE * pFile, + FDB * pDb, + FLMUINT uiLowTransId, + FLMBOOL bMostCurrent); + +#ifdef FLM_DEBUG +FSTATIC RCODE flmRcaCheck( + FDB * pDb, + FLMUINT uiContainer, + FLMUINT uiDrn); +#endif + +/**************************************************************************** +Desc: This inline assumes that the global mutex is locked, because + it potentially updates the cache usage statistics. +****************************************************************************/ +FINLINE void flmRcaSetTransID( + RCACHE * pRCache, + FLMUINT uiNewTransID) +{ + if (pRCache->uiHighTransId == 0xFFFFFFFF && + uiNewTransID != 0xFFFFFFFF) + { + FLMUINT uiSize = (FLMUINT)((pRCache->pRecord) + ? pRCache->pRecord->getTotalMemory() + : (FLMUINT)0) + sizeof( RCACHE); + gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes += uiSize; + gv_FlmSysData.RCacheMgr.Usage.uiOldVerCount++; + + if( pRCache->pRecord) + { + FlmRecordExt::setOldVersion( pRCache->pRecord); + } + } + else if (pRCache->uiHighTransId != 0xFFFFFFFF && + uiNewTransID == 0xFFFFFFFF) + { + FLMUINT uiSize = (FLMUINT)((pRCache->pRecord) + ? pRCache->pRecord->getTotalMemory() + : (FLMUINT)0) + sizeof( RCACHE); + flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes >= uiSize); + flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiOldVerCount); + gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes -= uiSize; + gv_FlmSysData.RCacheMgr.Usage.uiOldVerCount--; + if( pRCache->pRecord) + { + FlmRecordExt::clearOldVersion( pRCache->pRecord); + } + } + pRCache->uiHighTransId = uiNewTransID; +} + +/**************************************************************************** +Desc: This routine links a record into the global list as the MRU record. + This routine assumes that the record cache mutex has already + been locked. +****************************************************************************/ +FINLINE void flmRcaLinkToGlobalAsMRU( + RCACHE * pRCache) +{ + pRCache->pPrevInGlobal = NULL; + if ((pRCache->pNextInGlobal = gv_FlmSysData.RCacheMgr.pMRURecord) != NULL) + { + gv_FlmSysData.RCacheMgr.pMRURecord->pPrevInGlobal = pRCache; + } + else + { + gv_FlmSysData.RCacheMgr.pLRURecord = pRCache; + } + gv_FlmSysData.RCacheMgr.pMRURecord = pRCache; +} + +/**************************************************************************** +Desc: This routine links a record into the global list as the LRU record. + This routine assumes that the record cache mutex has already + been locked. +****************************************************************************/ +FINLINE void flmRcaLinkToGlobalAsLRU( + RCACHE * pRCache) +{ + pRCache->pNextInGlobal = NULL; + if ((pRCache->pPrevInGlobal = gv_FlmSysData.RCacheMgr.pLRURecord) != NULL) + { + gv_FlmSysData.RCacheMgr.pLRURecord->pNextInGlobal = pRCache; + } + else + { + gv_FlmSysData.RCacheMgr.pMRURecord = pRCache; + } + gv_FlmSysData.RCacheMgr.pLRURecord = pRCache; +} + +/**************************************************************************** +Desc: Moves a record one step closer to the MRU slot in the global list. + This routine assumes that the record cache mutex has already + been locked. +****************************************************************************/ +FINLINE void flmRcaStepUpInGlobalList( + RCACHE * pRCache) +{ + RCACHE * pPrevRCache; + + if( (pPrevRCache = pRCache->pPrevInGlobal) != NULL) + { + if( pPrevRCache->pPrevInGlobal) + { + pPrevRCache->pPrevInGlobal->pNextInGlobal = pRCache; + } + else + { + gv_FlmSysData.RCacheMgr.pMRURecord = pRCache; + } + + pRCache->pPrevInGlobal = pPrevRCache->pPrevInGlobal; + pPrevRCache->pPrevInGlobal = pRCache; + pPrevRCache->pNextInGlobal = pRCache->pNextInGlobal; + + if( pRCache->pNextInGlobal) + { + pRCache->pNextInGlobal->pPrevInGlobal = pPrevRCache; + } + else + { + gv_FlmSysData.RCacheMgr.pLRURecord = pPrevRCache; + } + pRCache->pNextInGlobal = pPrevRCache; + } +} + +/**************************************************************************** +Desc: This routine unlinks a record from the global list This routine + assumes that the record cache mutex has already been locked. +****************************************************************************/ +FINLINE void flmRcaUnlinkFromGlobal( + RCACHE * pRCache) +{ + if (pRCache->pNextInGlobal) + { + pRCache->pNextInGlobal->pPrevInGlobal = pRCache->pPrevInGlobal; + } + else + { + gv_FlmSysData.RCacheMgr.pLRURecord = pRCache->pPrevInGlobal; + } + if (pRCache->pPrevInGlobal) + { + pRCache->pPrevInGlobal->pNextInGlobal = pRCache->pNextInGlobal; + } + else + { + gv_FlmSysData.RCacheMgr.pMRURecord = pRCache->pNextInGlobal; + } + pRCache->pPrevInGlobal = pRCache->pNextInGlobal = NULL; +} + +/**************************************************************************** +Desc: This routine unlinks a record from the global purged list This routine + assumes that the record cache mutex has already been locked. +****************************************************************************/ +FINLINE void flmRcaUnlinkFromPurged( + RCACHE * pRCache) +{ + if (pRCache->pNextInGlobal) + { + pRCache->pNextInGlobal->pPrevInGlobal = pRCache->pPrevInGlobal; + } + + if (pRCache->pPrevInGlobal) + { + pRCache->pPrevInGlobal->pNextInGlobal = pRCache->pNextInGlobal; + } + else + { + gv_FlmSysData.RCacheMgr.pPurgeList = pRCache->pNextInGlobal; + } + + pRCache->pPrevInGlobal = NULL; + pRCache->pNextInGlobal = NULL; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FINLINE void flmRcaLinkToHeapList( + RCACHE * pRCache) +{ + flmAssert( !pRCache->pPrevInHeapList); + flmAssert( !pRCache->pNextInHeapList); + flmAssert( !RCA_IS_IN_HEAP_LIST( pRCache->uiFlags)); + flmAssert( FlmRecordExt::getFlags( pRCache->pRecord) & RCA_HEAP_BUFFER); + + if( (pRCache->pNextInHeapList = gv_FlmSysData.RCacheMgr.pHeapList) != NULL) + { + pRCache->pNextInHeapList->pPrevInHeapList = pRCache; + } + gv_FlmSysData.RCacheMgr.pHeapList = pRCache; + RCA_SET_IN_HEAP_LIST( pRCache->uiFlags); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FINLINE void flmRcaUnlinkFromHeapList( + RCACHE * pRCache) +{ + flmAssert( RCA_IS_IN_HEAP_LIST( pRCache->uiFlags)); + + if( pRCache->pNextInHeapList) + { + pRCache->pNextInHeapList->pPrevInHeapList = pRCache->pPrevInHeapList; + } + + if( pRCache->pPrevInHeapList) + { + pRCache->pPrevInHeapList->pNextInHeapList = pRCache->pNextInHeapList; + } + else + { + gv_FlmSysData.RCacheMgr.pHeapList = pRCache->pNextInHeapList; + } + + pRCache->pPrevInHeapList = NULL; + pRCache->pNextInHeapList = NULL; + + RCA_UNSET_IN_HEAP_LIST( pRCache->uiFlags); +} + +/**************************************************************************** +Desc: This routine links a record to an FFILE list at the head of the list. + This routine assumes that the record cache mutex has already been + locked. +****************************************************************************/ +FINLINE void flmRcaLinkToFileAtHead( + RCACHE * pRCache, + FFILE * pFile) +{ + pRCache->pPrevInFile = NULL; + if ((pRCache->pNextInFile = pFile->pFirstRecord) != NULL) + { + pFile->pFirstRecord->pPrevInFile = pRCache; + } + else + { + pFile->pLastRecord = pRCache; + } + + pFile->pFirstRecord = pRCache; + pRCache->pFile = pFile; + RCA_SET_LINKED_TO_FILE( pRCache->uiFlags); +} + +/**************************************************************************** +Desc: This routine links a record to an FFILE list at the end of the list. + This routine assumes that the record cache mutex has already been + locked. +****************************************************************************/ +FINLINE void flmRcaLinkToFileAtEnd( + RCACHE * pRCache, + FFILE * pFile) +{ + pRCache->pNextInFile = NULL; + if( (pRCache->pPrevInFile = pFile->pLastRecord) != NULL) + { + pFile->pLastRecord->pNextInFile = pRCache; + } + else + { + pFile->pFirstRecord = pRCache; + } + pFile->pLastRecord = pRCache; + pRCache->pFile = pFile; + RCA_SET_LINKED_TO_FILE( pRCache->uiFlags); +} + +/**************************************************************************** +Desc: This routine unlinks a record from its FFILE list. This routine + assumes that the record cache mutex has already been locked. +****************************************************************************/ +FINLINE void flmRcaUnlinkFromFile( + RCACHE * pRCache) +{ + if( RCA_IS_LINKED_TO_FILE( pRCache->uiFlags)) + { + if( pRCache->pNextInFile) + { + pRCache->pNextInFile->pPrevInFile = pRCache->pPrevInFile; + } + else + { + pRCache->pFile->pLastRecord = pRCache->pPrevInFile; + } + + if( pRCache->pPrevInFile) + { + pRCache->pPrevInFile->pNextInFile = pRCache->pNextInFile; + } + else + { + pRCache->pFile->pFirstRecord = pRCache->pNextInFile; + } + + pRCache->pPrevInFile = pRCache->pNextInFile = NULL; + RCA_UNSET_LINKED_TO_FILE( pRCache->uiFlags); + } +} + +/**************************************************************************** +Desc: This routine links a record into its hash bucket. This routine + assumes that the record cache mutex has already been locked. +****************************************************************************/ +FINLINE void flmRcaLinkToHashBucket( + RCACHE * pRCache) +{ + RCACHE ** ppHashBucket = FLM_RCA_HASH( pRCache->uiDrn); + + flmAssert( pRCache->pNewerVersion == NULL); + + pRCache->pPrevInBucket = NULL; + if( (pRCache->pNextInBucket = *ppHashBucket) != NULL) + { + pRCache->pNextInBucket->pPrevInBucket = pRCache; + } + *ppHashBucket = pRCache; +} + +/**************************************************************************** +Desc: This routine unlinks a record from its hash bucket. This routine + assumes that the record cache mutex has already been locked. +****************************************************************************/ +FINLINE void flmRcaUnlinkFromHashBucket( + RCACHE * pRCache) +{ + flmAssert( pRCache->pNewerVersion == NULL); + if (pRCache->pNextInBucket) + { + pRCache->pNextInBucket->pPrevInBucket = pRCache->pPrevInBucket; + } + + if (pRCache->pPrevInBucket) + { + pRCache->pPrevInBucket->pNextInBucket = pRCache->pNextInBucket; + } + else + { + RCACHE ** ppHashBucket = FLM_RCA_HASH( pRCache->uiDrn); + *ppHashBucket = pRCache->pNextInBucket; + } + pRCache->pPrevInBucket = pRCache->pNextInBucket = NULL; +} + +/**************************************************************************** +Desc: This routine unlinks a record from its version list. This routine + assumes that the record cache mutex has already been locked. +****************************************************************************/ +FINLINE void flmRcaLinkToVerList( + RCACHE * pRCache, + RCACHE * pNewerVer, + RCACHE * pOlderVer) +{ + if( (pRCache->pNewerVersion = pNewerVer) != NULL) + { + pNewerVer->pOlderVersion = pRCache; + } + + if ((pRCache->pOlderVersion = pOlderVer) != NULL) + { + pOlderVer->pNewerVersion = pRCache; + } +} + +/**************************************************************************** +Desc: This routine unlinks a record from its version list. This routine + assumes that the record cache mutex has already been locked. +****************************************************************************/ +FINLINE void flmRcaUnlinkFromVerList( + RCACHE * pRCache) +{ + if (pRCache->pNewerVersion) + { + pRCache->pNewerVersion->pOlderVersion = pRCache->pOlderVersion; + } + + if (pRCache->pOlderVersion) + { + pRCache->pOlderVersion->pNewerVersion = pRCache->pNewerVersion; + } + pRCache->pNewerVersion = pRCache->pOlderVersion = NULL; +} + +/**************************************************************************** +Desc: This routine frees a purged from record cache. This routine assumes + that the record cache mutex has already been locked. +****************************************************************************/ +FSTATIC void flmRcaFreePurged( + RCACHE * pRCache) +{ + FLMUINT uiTotalMemory; + FLMUINT uiFreeMemory; + + // Release the record data object we are pointing to. + + if (pRCache->pRecord) + { + if( RCA_IS_IN_HEAP_LIST( pRCache->uiFlags)) + { + flmRcaUnlinkFromHeapList( pRCache); + } + + uiTotalMemory = pRCache->pRecord->getTotalMemory(); + uiFreeMemory = pRCache->pRecord->getFreeMemory(); + flmAssert( uiTotalMemory >= uiFreeMemory); + FlmRecordExt::clearCached( pRCache->pRecord); + FlmRecordExt::Release( pRCache->pRecord, TRUE); + pRCache->pRecord = NULL; + } + else + { + uiTotalMemory = 0; + uiFreeMemory = 0; + } + + // If this is an old version, decrement the old version counters. + + if (pRCache->uiHighTransId != 0xFFFFFFFF) + { + FLMUINT uiTotalOldMemory = uiTotalMemory + sizeof( RCACHE); + + flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes >= + uiTotalOldMemory); + flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiOldVerCount); + gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes -= uiTotalOldMemory; + gv_FlmSysData.RCacheMgr.Usage.uiOldVerCount--; + } + + // Unlink the RCACHE from the purged list. + + flmRcaUnlinkFromPurged( pRCache); + + // Free the RCACHE structure. + + RCA_UNSET_PURGED( pRCache->uiFlags); + flmRcaFreeCacheStruct( &pRCache); +} + +/**************************************************************************** +Desc: This routine frees a record in the record cache. This routine assumes + that the record cache mutex has already been locked. +****************************************************************************/ +FSTATIC void flmRcaFreeCache( + RCACHE * pRCache, + FLMBOOL bPutInPurgeList) +{ + FLMUINT uiTotalMemory; + FLMUINT uiFreeMemory; + FLMBOOL bOldVersion; +#ifdef FLM_DBG_LOG + char szTmpBuf[ 80]; +#endif + + // Release the record data object we are pointing to. + + if (pRCache->pRecord && !bPutInPurgeList) + { + if( RCA_IS_IN_HEAP_LIST( pRCache->uiFlags)) + { + flmRcaUnlinkFromHeapList( pRCache); + } + + uiTotalMemory = pRCache->pRecord->getTotalMemory(); + uiFreeMemory = pRCache->pRecord->getFreeMemory(); + flmAssert( uiTotalMemory >= uiFreeMemory); + FlmRecordExt::clearCached( pRCache->pRecord); + FlmRecordExt::Release( pRCache->pRecord, TRUE); + pRCache->pRecord = NULL; + } + else + { + uiTotalMemory = 0; + uiFreeMemory = 0; + } + bOldVersion = (FLMBOOL)((pRCache->uiHighTransId != 0xFFFFFFFF) + ? TRUE + : FALSE); + +#ifdef FLM_DBG_LOG + f_sprintf( szTmpBuf, "RCD:H%X", + (unsigned)pRCache->uiHighTransId); + + flmDbgLogWrite( pRCache->pFile ? pRCache->pFile->uiFFileId : 0, pRCache->uiContainer, + pRCache->uiDrn, pRCache->uiLowTransId, szTmpBuf); +#endif + + // Unlink the RCACHE from its various lists. + + flmRcaUnlinkFromGlobal( pRCache); + flmRcaUnlinkFromFile( pRCache); + if (!pRCache->pNewerVersion) + { + RCACHE * pOlderVersion = pRCache->pOlderVersion; + + flmRcaUnlinkFromHashBucket( pRCache); + + // If there was an older version, it now needs to be + // put into the hash bucket. + + if (pOlderVersion) + { + flmRcaUnlinkFromVerList( pRCache); + flmRcaLinkToHashBucket( pOlderVersion); + } + } + else + { + flmRcaUnlinkFromVerList( pRCache); + } + + // Free the RCACHE structure if not putting in purge list. + + if (!bPutInPurgeList) + { + // If this was an older version, decrement the old version counters. + + if (bOldVersion) + { + FLMUINT uiTotalOldMemory = uiTotalMemory + sizeof( RCACHE); + + flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes >= + uiTotalOldMemory); + flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiOldVerCount); + gv_FlmSysData.RCacheMgr.Usage.uiOldVerCount--; + gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes -= + uiTotalOldMemory; + } + flmRcaFreeCacheStruct( &pRCache); + } + else + { + if ((pRCache->pNextInGlobal = gv_FlmSysData.RCacheMgr.pPurgeList) != NULL) + { + pRCache->pNextInGlobal->pPrevInGlobal = pRCache; + } + gv_FlmSysData.RCacheMgr.pPurgeList = pRCache; + RCA_SET_PURGED( pRCache->uiFlags); + } +} + +/**************************************************************************** +Desc: This routine initializes record cache manager. +****************************************************************************/ +RCODE flmRcaInit( + FLMUINT uiMaxRecordCacheBytes) +{ + RCODE rc = FERR_OK; + + f_memset( &gv_FlmSysData.RCacheMgr, 0, sizeof( RCACHE_MGR)); + gv_FlmSysData.RCacheMgr.Usage.uiMaxBytes = uiMaxRecordCacheBytes; + gv_FlmSysData.RCacheMgr.hMutex = F_MUTEX_NULL; + + // Allocate the hash buckets. + + if (RC_BAD( rc = f_calloc( + (FLMUINT)sizeof( RCACHE *) * + (FLMUINT)MIN_RCACHE_BUCKETS, + &gv_FlmSysData.RCacheMgr.ppHashBuckets))) + { + goto Exit; + } + gv_FlmSysData.RCacheMgr.uiNumBuckets = MIN_RCACHE_BUCKETS; + gv_FlmSysData.RCacheMgr.uiHashMask = + gv_FlmSysData.RCacheMgr.uiNumBuckets - 1; + gv_FlmSysData.RCacheMgr.Usage.uiTotalBytesAllocated += + (sizeof( RCACHE *) * gv_FlmSysData.RCacheMgr.uiNumBuckets); + + // Allocate the mutex for controlling access to the + // record cache. + + if (RC_BAD( rc = f_mutexCreate( &gv_FlmSysData.RCacheMgr.hMutex))) + { + goto Exit; + } + + // Set up the RCACHE struct allocator + + if( (gv_FlmSysData.RCacheMgr.pRCacheAlloc = f_new F_FixedAlloc) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = gv_FlmSysData.RCacheMgr.pRCacheAlloc->setup( + gv_FlmSysData.pSlabManager, TRUE, sizeof( RCACHE), + &gv_FlmSysData.RCacheMgr.Usage.uiTotalBytesAllocated))) + { + goto Exit; + } + + gv_FlmSysData.RCacheMgr.pRCacheAlloc->setRelocationFuncs( + rcaCanRelocate, rcaRelocate); + + // Set up the record object allocator + + if( (gv_FlmSysData.RCacheMgr.pRecAlloc = f_new F_FixedAlloc) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = gv_FlmSysData.RCacheMgr.pRecAlloc->setup( + gv_FlmSysData.pSlabManager, + gv_FlmSysData.RCacheMgr.pRCacheAlloc->getMutex(), + sizeof( FlmRecord), + &gv_FlmSysData.RCacheMgr.Usage.uiTotalBytesAllocated))) + { + goto Exit; + } + + gv_FlmSysData.RCacheMgr.pRecAlloc->setRelocationFuncs( + FlmRecordExt::canRelocateRec, FlmRecordExt::relocateRec); + + // Set up the record buffer allocator + + if( (gv_FlmSysData.RCacheMgr.pRecBufAlloc = f_new F_BufferAlloc) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if( RC_BAD( rc = gv_FlmSysData.RCacheMgr.pRecBufAlloc->setup( + gv_FlmSysData.pSlabManager, + gv_FlmSysData.RCacheMgr.pRCacheAlloc->getMutex(), + &gv_FlmSysData.RCacheMgr.Usage.uiTotalBytesAllocated))) + { + goto Exit; + } + + gv_FlmSysData.RCacheMgr.pRecBufAlloc->setRelocationFuncs( + FlmRecordExt::canRelocateRecBuffer, + FlmRecordExt::relocateRecBuffer); + +#ifdef FLM_DEBUG + gv_FlmSysData.RCacheMgr.bDebug = TRUE; +#endif + +Exit: + if (RC_BAD( rc)) + { + flmRcaExit(); + } + + return( rc); +} + +/**************************************************************************** +Desc: This routine determines what hash table size best fits the current + record count. It finds the hash bucket size whose midpoint between + the minimum and maximum range is closest to the record count. +****************************************************************************/ +FSTATIC FLMUINT flmRcaGetBestHashTblSize( + FLMUINT uiCurrRecCount) +{ + FLMUINT uiHashTblSize; + FLMUINT uiMaxRecsForHashTblSize; + FLMUINT uiMinRecsForHashTblSize; + FLMUINT uiClosestHashTblSize = 0; + FLMUINT uiDistanceFromMidpoint; + FLMUINT uiLowestDistanceFromMidpoint; + FLMUINT uiHashTblRecsMidpoint; + + uiLowestDistanceFromMidpoint = 0xFFFFFFFF; + for (uiHashTblSize = MIN_RCACHE_BUCKETS; + uiHashTblSize <= MAX_RCACHE_BUCKETS; + uiHashTblSize *= 2) + { + + // Maximum desirable record count for a specific hash table size + // we have arbitrarily chosen to be four times the number of buckets. + // Minimum desirable record count we have arbitrarily chosen to be + // the hash table size divided by four. + + uiMaxRecsForHashTblSize = FLM_RCA_MAX_REC_CNT( uiHashTblSize); + uiMinRecsForHashTblSize = FLM_RCA_MIN_REC_CNT( uiHashTblSize); + + // Ignore any hash bucket sizes where the current record count + // is not between the desired minimum and maximum. + + if (uiCurrRecCount >= uiMinRecsForHashTblSize && + uiCurrRecCount <= uiMaxRecsForHashTblSize) + { + + // Calculate the midpoint between the minimum and maximum + // for this particular hash table size. + + uiHashTblRecsMidpoint = (uiMaxRecsForHashTblSize - + uiMinRecsForHashTblSize) / 2; + + // See how far our current record count is from this midpoint. + + uiDistanceFromMidpoint = (FLMUINT)((uiHashTblRecsMidpoint > uiCurrRecCount) + ? (uiHashTblRecsMidpoint - uiCurrRecCount) + : (uiCurrRecCount - uiHashTblRecsMidpoint)); + + // If the distance from the midpoint is closer than our previous + // lowest distance, save it. + + if (uiDistanceFromMidpoint < uiLowestDistanceFromMidpoint) + { + uiClosestHashTblSize = uiHashTblSize; + uiLowestDistanceFromMidpoint = uiDistanceFromMidpoint; + } + } + } + + // Take the number of buckets whose middle was closest to the + // current record count; + + if (uiLowestDistanceFromMidpoint == 0xFFFFFFFF) + { + // If we did not fall between any of the minimums or maximums, + // we are either below the lowest minimum, or higher than the + // highest maximum. + + uiHashTblSize = + (FLMUINT)((uiCurrRecCount < FLM_RCA_MIN_REC_CNT( MIN_RCACHE_BUCKETS)) + ? (FLMUINT)MIN_RCACHE_BUCKETS + : (FLMUINT)MAX_RCACHE_BUCKETS); + + } + else + { + uiHashTblSize = uiClosestHashTblSize; + } + return( uiHashTblSize); +} + +/**************************************************************************** +Desc: This routine resizes the hash table for the record cache manager. + NOTE: This routine assumes that the record cache mutex has been locked. +****************************************************************************/ +FSTATIC RCODE flmRcaRehash( void) +{ + RCODE rc = FERR_OK; + FLMUINT uiNewHashTblSize; + RCACHE ** ppOldHashTbl; + FLMUINT uiOldHashTblSize; + RCACHE ** ppBucket; + FLMUINT uiLoop; + RCACHE * pTmpRCache; + RCACHE * pTmpNextRCache; + + uiNewHashTblSize = flmRcaGetBestHashTblSize( + gv_FlmSysData.RCacheMgr.Usage.uiCount); + + // At this point we better have a different hash table size + // or something is mucked up! + + flmAssert( uiNewHashTblSize != + gv_FlmSysData.RCacheMgr.uiNumBuckets); + + // Save the old hash table and its size. + + ppOldHashTbl = gv_FlmSysData.RCacheMgr.ppHashBuckets; + uiOldHashTblSize = gv_FlmSysData.RCacheMgr.uiNumBuckets; + + // Allocate a new hash table. + + if (RC_BAD( rc = f_calloc( + (FLMUINT)sizeof( RCACHE *) * + (FLMUINT)uiNewHashTblSize, + &gv_FlmSysData.RCacheMgr.ppHashBuckets))) + { + gv_FlmSysData.RCacheMgr.ppHashBuckets = ppOldHashTbl; + goto Exit; + } + + // Subtract off old size and add in new size. + + gv_FlmSysData.RCacheMgr.pRCacheAlloc->decrementTotalBytesAllocated( + (sizeof( RCACHE *) * uiOldHashTblSize)); + gv_FlmSysData.RCacheMgr.pRCacheAlloc->incrementTotalBytesAllocated( + (sizeof( RCACHE *) * uiNewHashTblSize)); + + gv_FlmSysData.RCacheMgr.uiNumBuckets = uiNewHashTblSize; + gv_FlmSysData.RCacheMgr.uiHashMask = uiNewHashTblSize - 1; + + // Relink all of the records into the new + // hash table. + + for (uiLoop = 0, ppBucket = ppOldHashTbl; + uiLoop < uiOldHashTblSize; + uiLoop++, ppBucket++) + { + pTmpRCache = *ppBucket; + while (pTmpRCache) + { + pTmpNextRCache = pTmpRCache->pNextInBucket; + flmRcaLinkToHashBucket( pTmpRCache); + pTmpRCache = pTmpNextRCache; + } + } + + // Throw away the old hash table. + + f_free( &ppOldHashTbl); + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: This routine changes the cache size for the record cache manager. + If necessary, it will resize the hash table. NOTE: This routine + assumes that the record cache mutex has been locked. +****************************************************************************/ +FSTATIC RCODE flmRcaSetMemLimit( + FLMUINT uiMaxCacheBytes) +{ + RCODE rc = FERR_OK; + + // If we are shrinking the maximum cache, clean up and + // defragment cache first + + gv_FlmSysData.RCacheMgr.Usage.uiMaxBytes = uiMaxCacheBytes; + if (gv_FlmSysData.RCacheMgr.pRCacheAlloc->getTotalBytesAllocated() > + uiMaxCacheBytes) + { + flmRcaCleanupCache( ~((FLMUINT)0), TRUE); + } + + // If the current record count is below the minimum records for the + // number of buckets or is greater than the maximum records for the + // number of buckets, we want to resize the hash table. + + if ((gv_FlmSysData.RCacheMgr.Usage.uiCount > + FLM_RCA_MAX_REC_CNT( gv_FlmSysData.RCacheMgr.uiNumBuckets) && + gv_FlmSysData.RCacheMgr.uiNumBuckets < MAX_RCACHE_BUCKETS) || + (gv_FlmSysData.RCacheMgr.Usage.uiCount < + FLM_RCA_MIN_REC_CNT( gv_FlmSysData.RCacheMgr.uiNumBuckets) && + gv_FlmSysData.RCacheMgr.uiNumBuckets > MIN_RCACHE_BUCKETS)) + { + if (RC_BAD( rc = flmRcaRehash())) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: This routine configures the record cache manager. NOTE: This routine + assumes that the record cache mutex has been locked. +****************************************************************************/ +RCODE flmRcaConfig( + FLMUINT uiType, + void * Value1, + void * Value2) +{ + RCODE rc = FERR_OK; + + F_UNREFERENCED_PARM( Value2); + + switch (uiType) + { + case FLM_CACHE_LIMIT: + rc = flmRcaSetMemLimit( (FLMUINT)Value1); + break; + case FLM_SCACHE_DEBUG: +#ifdef FLM_DEBUG + gv_FlmSysData.RCacheMgr.bDebug = (FLMBOOL)(Value1 ? + (FLMBOOL)TRUE : (FLMBOOL)FALSE); +#endif + break; + default: + rc = RC_SET( FERR_NOT_IMPLEMENTED); + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: This routine shuts down the record cache manager and frees all + resources allocated by it. +****************************************************************************/ +void flmRcaExit( void) +{ + if (gv_FlmSysData.RCacheMgr.hMutex != F_MUTEX_NULL) + { + f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); + } + + // Free all of the record cache objects. + + while (gv_FlmSysData.RCacheMgr.pMRURecord) + { + f_yieldCPU(); + flmRcaFreeCache( gv_FlmSysData.RCacheMgr.pMRURecord, FALSE); + } + + // Must free those in the purge list too. + + while (gv_FlmSysData.RCacheMgr.pPurgeList) + { + f_yieldCPU(); + flmRcaFreePurged( gv_FlmSysData.RCacheMgr.pPurgeList); + } + + // The math better be consistent! + + flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiCount == 0); + flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiOldVerCount == 0); + flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes == 0); + + // Free the hash bucket array + + if (gv_FlmSysData.RCacheMgr.ppHashBuckets) + { + f_free( &gv_FlmSysData.RCacheMgr.ppHashBuckets); + gv_FlmSysData.RCacheMgr.pRCacheAlloc->decrementTotalBytesAllocated( + (sizeof( RCACHE *) * gv_FlmSysData.RCacheMgr.uiNumBuckets)); + } + + // Free the mutex that controls access to record cache. + // NOTE: This should be done last so that the mutex is not + // unlocked until the very end. + + if (gv_FlmSysData.RCacheMgr.hMutex != F_MUTEX_NULL) + { + f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); + f_mutexDestroy( &gv_FlmSysData.RCacheMgr.hMutex); + } + + // Free the allocators + + if( gv_FlmSysData.RCacheMgr.pRecBufAlloc) + { + gv_FlmSysData.RCacheMgr.pRecBufAlloc->Release(); + gv_FlmSysData.RCacheMgr.pRecBufAlloc = NULL; + } + + if( gv_FlmSysData.RCacheMgr.pRecAlloc) + { + gv_FlmSysData.RCacheMgr.pRecAlloc->Release(); + gv_FlmSysData.RCacheMgr.pRecAlloc = NULL; + } + + if( gv_FlmSysData.RCacheMgr.pRCacheAlloc) + { + gv_FlmSysData.RCacheMgr.pRCacheAlloc->Release(); + gv_FlmSysData.RCacheMgr.pRCacheAlloc = NULL; + } + + // Zero the entire structure out, just for good measure. + + f_memset( &gv_FlmSysData.RCacheMgr, 0, sizeof( RCACHE_MGR)); +} + +/**************************************************************************** +Desc: This routine links a notify request into a notification list and + then waits to be notified that the event has occurred. + NOTE: This routine assumes that the record cache mutex is locked and that + it is supposed to unlock it. It will relock the mutex on its way out. +****************************************************************************/ +FSTATIC RCODE flmRcaWaitNotify( + FNOTIFY ** ppNotifyListRV) +{ + FNOTIFY * pNotify = NULL; + RCODE TempRc; + RCODE rc = FERR_OK; + F_SEM hSem; + + // First create a notify request and link it into the list. + + if (RC_OK( rc = f_calloc( (FLMUINT)(sizeof( FNOTIFY)), &pNotify))) + { + // Allocate a semaphore for the notify request. + + pNotify->uiThreadId = f_threadId(); + pNotify->hSem = F_SEM_NULL; + rc = f_semCreate( &pNotify->hSem); + } + + if (RC_BAD( rc)) + { + if (pNotify) + { + if (pNotify->hSem != F_SEM_NULL) + { + f_semDestroy( &pNotify->hSem); + } + + f_free( &pNotify); + } + + goto Exit; + } + + pNotify->pRc = &rc; + pNotify->UserData = NULL; + pNotify->pNext = *ppNotifyListRV; + *ppNotifyListRV = pNotify; + hSem = pNotify->hSem; + + // Unlock the mutex and wait on the semaphore. + + f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); + if (RC_BAD( TempRc = f_semWait( hSem, F_SEM_WAITFOREVER))) + { + rc = TempRc; + } + + // Free the semaphore and the notify structure. + + f_semDestroy( &hSem); + f_free( &pNotify); + + // Relock the record cache mutex + + f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: This routine notifies threads waiting for a pending read to complete. + NOTE: This routine assumes that the record cache mutex is already + locked. +****************************************************************************/ +FSTATIC void flmRcaNotify( + FNOTIFY * pNotify, + RCACHE * pUseRCache, + RCODE NotifyRc) +{ + while (pNotify) + { + F_SEM hSem; + + *(pNotify->pRc) = NotifyRc; + if (RC_OK( NotifyRc)) + { + RCA_INCR_USE_COUNT( pUseRCache->uiFlags); + } + hSem = pNotify->hSem; + pNotify = pNotify->pNext; + f_semSignal( hSem); + } +} + +/**************************************************************************** +Desc: Allocate a new record cache structure. This routine assumes that the + record cache mutex has already been locked. +****************************************************************************/ +FSTATIC RCODE flmRcaAllocCacheStruct( + RCACHE ** ppRCache) +{ + RCODE rc = FERR_OK; + + if( (*ppRCache = + (RCACHE *)gv_FlmSysData.RCacheMgr.pRCacheAlloc->allocCell()) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + f_memset( *ppRCache, 0, sizeof( RCACHE)); + + // Increment the total records cached + + gv_FlmSysData.RCacheMgr.Usage.uiCount++; + + // Set the high transaction ID to 0xFFFFFFFF so that this will NOT + // be treated as one that had memory assigned to the old records. + + (*ppRCache)->uiHighTransId = 0xFFFFFFFF; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Free a record cache structure. This routine assumes that the record + cache mutex has already been locked. +****************************************************************************/ +FSTATIC void flmRcaFreeCacheStruct( + RCACHE ** ppRCache) +{ + flmAssert( !RCA_IS_IN_HEAP_LIST( (*ppRCache)->uiFlags)); + + gv_FlmSysData.RCacheMgr.pRCacheAlloc->freeCell( *ppRCache); + *ppRCache = NULL; + + flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiCount > 0); + gv_FlmSysData.RCacheMgr.Usage.uiCount--; +} + +/**************************************************************************** +Desc: Cleanup old records in cache that are no longer needed by any + transaction. +****************************************************************************/ +void flmRcaCleanupCache( + FLMUINT uiMaxLockTime, + FLMBOOL bMutexesLocked) +{ + RCACHE * pTmpRCache; + RCACHE * pPrevRCache; + RCACHE * pNextRCache; + FLMUINT uiRecordsExamined = 0; + FLMUINT uiLastTimePaused = FLM_GET_TIMER(); + FLMUINT uiCurrTime; + FLMBOOL bUnlockMutexes = FALSE; + + if( !bMutexesLocked) + { + f_mutexLock( gv_FlmSysData.hShareMutex); + f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); + bUnlockMutexes = TRUE; + } + + // Try to free everything in the heap list + + pTmpRCache = gv_FlmSysData.RCacheMgr.pHeapList; + + while( pTmpRCache) + { + uiRecordsExamined++; + + // Save the pointer to the next entry in the list because + // we may end up unlinking pTmpRCache below + + pNextRCache = pTmpRCache->pNextInHeapList; + + // Determine if the item can be freed + + flmAssert( RCA_IS_IN_HEAP_LIST( pTmpRCache->uiFlags)); + + if( !RCA_IS_IN_USE( pTmpRCache->uiFlags) && + !RCA_IS_READING_IN( pTmpRCache->uiFlags)) + { + flmRcaFreeCache( pTmpRCache, FALSE); + } + + pTmpRCache = pNextRCache; + } + + // Now, free any old versions that are no longer needed + + flmRcaReduceCache( TRUE); + pTmpRCache = gv_FlmSysData.RCacheMgr.pLRURecord; + + // Stay in the loop until we have freed all old records, or + // we have run through the entire list. + + for( ;;) + { + if( !pTmpRCache || !gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes) + { + break; + } + + // After each 200 records examined, see if our maximum + // time has elapsed for examining without a pause. + + if( uiRecordsExamined >= 200) + { + uiRecordsExamined = 0; + uiCurrTime = FLM_GET_TIMER(); + + if( FLM_ELAPSED_TIME( uiCurrTime, uiLastTimePaused) >= uiMaxLockTime) + { + // IMPORTANT! Don't stop and pause on one that is + // being read in. + + while( pTmpRCache && RCA_IS_READING_IN( pTmpRCache->uiFlags)) + { + pTmpRCache = pTmpRCache->pPrevInGlobal; + } + + if( !pTmpRCache) + { + break; + } + + if( bUnlockMutexes) + { + // Increment the use count so that this item will not + // go away while we are paused. + + RCA_INCR_USE_COUNT( pTmpRCache->uiFlags); + f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); + f_mutexUnlock( gv_FlmSysData.hShareMutex); + + // Shortest possible pause - to allow other threads + // to do work. + + #ifdef FLM_NLM + f_yieldCPU(); + #else + f_sleep( 0); + #endif + + // Relock the mutexes + + uiLastTimePaused = FLM_GET_TIMER(); + f_mutexLock( gv_FlmSysData.hShareMutex); + f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); + + // Decrement use count that was added above. + + RCA_DECR_USE_COUNT( pTmpRCache->uiFlags); + } + + // If the item was purged while we were paused, + // finish the job and then start again at the + // top of the list. + + if( RCA_IS_PURGED( pTmpRCache->uiFlags)) + { + if( !RCA_IS_IN_USE( pTmpRCache->uiFlags)) + { + flmRcaFreePurged( pTmpRCache); + } + + pTmpRCache = gv_FlmSysData.RCacheMgr.pLRURecord; + continue; + } + } + } + + uiRecordsExamined++; + + // Save the pointer to the previous entry in the list because + // we may end up unlinking pTmpRCache below, in which case we would + // have lost the previous entry. + + pPrevRCache = pTmpRCache->pPrevInGlobal; + + // Block must not currently be in use, + // Must not be the most current version of a block, + // Cannot be dirty in any way, + // Cannot be in the process of being read in from disk, + // And must not be needed by a read transaction. + + if( !RCA_IS_IN_USE( pTmpRCache->uiFlags) && + !RCA_IS_READING_IN( pTmpRCache->uiFlags) && + (RCA_IS_IN_HEAP_LIST( pTmpRCache->uiFlags) || + (pTmpRCache->uiHighTransId != 0xFFFFFFFF && + !flmNeededByReadTrans( pTmpRCache->pFile, + pTmpRCache->uiLowTransId, + pTmpRCache->uiHighTransId)))) + { + flmRcaFreeCache( pTmpRCache, FALSE); + } + + pTmpRCache = pPrevRCache; + } + + // Defragment memory + + gv_FlmSysData.RCacheMgr.pRCacheAlloc->defragmentMemory(); + gv_FlmSysData.RCacheMgr.pRecAlloc->defragmentMemory(); + gv_FlmSysData.RCacheMgr.pRecBufAlloc->defragmentMemory(); + + // Unlock the mutexes + + if( bUnlockMutexes) + { + f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); + f_mutexUnlock( gv_FlmSysData.hShareMutex); + } +} + +/**************************************************************************** +Desc: This routine reduces record cache down to the limit expected. +****************************************************************************/ +void flmRcaReduceCache( + FLMBOOL bMutexAlreadyLocked) +{ + RCACHE * pRCache; + RCACHE * pPrevRCache; + + // Make sure the mutex is locked. + + if (!bMutexAlreadyLocked) + { + f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); + } + + pRCache = gv_FlmSysData.RCacheMgr.pLRURecord; + + // Free things until we get down below our memory limit. + + while( gv_FlmSysData.RCacheMgr.pRCacheAlloc->getTotalBytesAllocated() > + gv_FlmSysData.RCacheMgr.Usage.uiMaxBytes) + { + if( !pRCache) + { + break; + } + + // If the total of block and record cache is below the global + // cache maximum, there is no need to reduce record cache. + + if( (gv_FlmSysData.RCacheMgr.pRCacheAlloc->getTotalBytesAllocated() + + gv_FlmSysData.SCacheMgr.Usage.uiTotalBytesAllocated) <= + gv_FlmSysData.uiMaxCache) + { + break; + } + + pPrevRCache = pRCache->pPrevInGlobal; + if (!(RCA_IS_IN_USE( pRCache->uiFlags)) && + !(RCA_IS_READING_IN( pRCache->uiFlags))) + { + flmRcaFreeCache( pRCache, FALSE); + } + pRCache = pPrevRCache; + } + + if (!bMutexAlreadyLocked) + { + f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); + } +} + +/**************************************************************************** +Desc: This routine finds a record in the record cache. If it cannot + find the record, it will return the position where the record should + be inserted. NOTE: This routine assumes that the record cache mutex + has been locked. +****************************************************************************/ +void flmRcaFindRec( + FLMUINT uiContainer, + FLMUINT uiDrn, + FFILE * pFile, + FLMUINT uiVersionNeeded, + FLMBOOL bDontPoisonCache, + FLMUINT * puiNumLooks, + RCACHE ** ppRCache, + RCACHE ** ppNewerRCache, + RCACHE ** ppOlderRCache) +{ + RCACHE * pRCache; + FLMUINT uiNumLooks = 0; + FLMBOOL bFound; + RCACHE * pNewerRCache; + RCACHE * pOlderRCache; + + // Search down the hash bucket for the matching item. + +Start_Find: + + // NOTE: Need to always calculate hash bucket because + // the hash table may have been changed while we + // were waiting to be notified below - mutex can + // be unlocked, but it is guaranteed to be locked + // here. + + pRCache = *(FLM_RCA_HASH( uiDrn)); + bFound = FALSE; + uiNumLooks = 1; + while ((pRCache) && + ((pRCache->uiDrn != uiDrn) || + (pRCache->uiContainer != uiContainer) || + (pRCache->pFile != pFile))) + { + if ((pRCache = pRCache->pNextInBucket) != NULL) + { + uiNumLooks++; + } + } + + // If we found the record, see if we have the right version. + + if (!pRCache) + { + pNewerRCache = pOlderRCache = NULL; + } + else + { + pNewerRCache = NULL; + pOlderRCache = pRCache; + for (;;) + { + + // If this one is being read in, we need to wait on it. + + if (RCA_IS_READING_IN( pRCache->uiFlags)) + { + // We need to wait for this record to be read in + // in case it coalesces with other versions, resulting + // in a version that satisfies our request. + + gv_FlmSysData.RCacheMgr.uiIoWaits++; + if (RC_BAD( flmRcaWaitNotify( &pRCache->pNotifyList))) + { + + // Don't care what error the other thread had reading + // the thing in from disk - we'll bail out and start + // our find again. + + goto Start_Find; + } + + // The thread doing the notify "uses" the record cache + // on behalf of this thread to prevent the record + // from being replaced after it unlocks the mutex. + // At this point, since we have locked the mutex, + // we need to release the record - because we + // will put a "use" on it below. + + RCA_DECR_USE_COUNT( pRCache->uiFlags); + + if (RCA_IS_PURGED( pRCache->uiFlags)) + { + if (!RCA_IS_IN_USE( pRCache->uiFlags)) + { + flmRcaFreePurged( pRCache); + } + } + + // Start over with the find because the list + // structure has changed. + + goto Start_Find; + } + + // See if this record version is the one we need. + + if (uiVersionNeeded < pRCache->uiLowTransId) + { + pNewerRCache = pRCache; + if ((pOlderRCache = pRCache = pRCache->pOlderVersion) == NULL) + { + break; + } + uiNumLooks++; + } + else if (uiVersionNeeded <= pRCache->uiHighTransId) + { + // Make this the MRU record. + + if (puiNumLooks) + { + if (bDontPoisonCache) + { + flmRcaStepUpInGlobalList( pRCache); + } + else if (pRCache->pPrevInGlobal) + { + flmRcaUnlinkFromGlobal( pRCache); + flmRcaLinkToGlobalAsMRU( pRCache); + } + + gv_FlmSysData.RCacheMgr.Usage.uiCacheHits++; + gv_FlmSysData.RCacheMgr.Usage.uiCacheHitLooks += uiNumLooks; + } + + bFound = TRUE; + break; + } + else + { + pOlderRCache = pRCache; + pNewerRCache = pRCache->pNewerVersion; + + // Set pRCache to NULL as an indicator that we did not + // find the version we needed. + + pRCache = NULL; + break; + } + } + } + + *ppRCache = pRCache; + *ppOlderRCache = pOlderRCache; + *ppNewerRCache = pNewerRCache; + + if (puiNumLooks) + { + *puiNumLooks = uiNumLooks; + } +} + +/**************************************************************************** +Desc: This routine replaces the FlmRecord that a record is pointing to + with a new one. NOTE: This routine assumes that the record cache + mutex is already locked. +****************************************************************************/ +FSTATIC void flmRcaSetRecord( + RCACHE * pRCache, + FlmRecord * pNewRecord) +{ + FLMUINT uiTotalMemory = 0; + FLMUINT uiFreeMemory; + FlmRecord * pOldRecord; + + // Release the cache's pointer to the old record data + + if ((pOldRecord = pRCache->pRecord) != NULL) + { + if( RCA_IS_IN_HEAP_LIST( pRCache->uiFlags)) + { + flmRcaUnlinkFromHeapList( pRCache); + } + + uiTotalMemory = pOldRecord->getTotalMemory(); + uiFreeMemory = pOldRecord->getFreeMemory(); + flmAssert( uiTotalMemory >= uiFreeMemory); + FlmRecordExt::clearCached( pOldRecord); + FlmRecordExt::Release( pOldRecord, TRUE); + pRCache->pRecord = NULL; + } + + if (pRCache->uiHighTransId != 0xFFFFFFFF) + { + FLMUINT uiTotalOldMemory = uiTotalMemory + sizeof( RCACHE); + + flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes >= + uiTotalOldMemory); + flmAssert( gv_FlmSysData.RCacheMgr.Usage.uiOldVerCount); + gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes -= + uiTotalOldMemory; + gv_FlmSysData.RCacheMgr.Usage.uiOldVerCount--; + } + + // Point to the new record data. + + flmAssert( pNewRecord->getID() == pRCache->uiDrn); + flmAssert( pNewRecord->getContainerID() == pRCache->uiContainer); + pRCache->pRecord = pNewRecord; + flmAssert( !pNewRecord->isCached()); + FlmRecordExt::setCached( pNewRecord); + FlmRecordExt::AddRef( pNewRecord, TRUE); + FlmRecordExt::setReadOnly( pNewRecord); + + if( FlmRecordExt::getFlags( pNewRecord) & RCA_HEAP_BUFFER) + { + flmRcaLinkToHeapList( pRCache); + } + + uiTotalMemory = pNewRecord->getTotalMemory(); + + if (pRCache->uiHighTransId != 0xFFFFFFFF) + { + gv_FlmSysData.RCacheMgr.Usage.uiOldVerBytes += + (uiTotalMemory + sizeof( RCACHE)); + gv_FlmSysData.RCacheMgr.Usage.uiOldVerCount++; + } + uiFreeMemory = pNewRecord->getFreeMemory(); + flmAssert( uiTotalMemory >= uiFreeMemory); +} + +/**************************************************************************** +Desc: This routine links a new RCACHE structure into the global list and + into the correct place in its hash bucket. This routine assumes that + the record cache mutex is already locked. +****************************************************************************/ +FSTATIC void flmRcaLinkIntoRCache( + RCACHE * pNewerRCache, + RCACHE * pOlderRCache, + RCACHE * pRCache, + FLMBOOL bLinkAsMRU) +{ + if( bLinkAsMRU) + { + flmRcaLinkToGlobalAsMRU( pRCache); + } + else + { + flmRcaLinkToGlobalAsLRU( pRCache); + } + + if (pNewerRCache) + { + flmRcaLinkToVerList( pRCache, pNewerRCache, pOlderRCache); + } + else + { + RCACHE * pNull = NULL; + + if (pOlderRCache) + { + flmRcaUnlinkFromHashBucket( pOlderRCache); + } + flmRcaLinkToHashBucket( pRCache); + flmRcaLinkToVerList( pRCache, pNull, pOlderRCache); + } +} + +/**************************************************************************** +Desc: This routine links a new record to its FFILE according to whether + or not it is an update transaction or a read transaction. + It coalesces out any unnecessary versions. This routine assumes + that the record cache mutex is already locked. +****************************************************************************/ +FSTATIC void flmRcaLinkToFFILE( + RCACHE * pRCache, + FFILE * pFile, + FDB * pDb, + FLMUINT uiLowTransId, + FLMBOOL bMostCurrent) +{ + RCACHE * pTmpRCache; +#ifdef FLM_DBG_LOG + char szTmpBuf[ 80]; +#endif + + + pRCache->uiLowTransId = uiLowTransId; + + // Before coalescing, link to FFILE. + // The following test determines if the record is an + // uncommitted version generated by the update transaction. + // If so, we mark it as such, and link it at the head of the + // FFILE list - so we can get rid of it quickly if we abort + // the transaction. + + if (flmGetDbTransType( pDb) == FLM_UPDATE_TRANS) + { + + // If we are in an update transaction, there better not + // be any newer versions in the list and the high + // transaction ID returned better be 0xFFFFFFFF. + + flmAssert( pRCache->pNewerVersion == NULL); + flmRcaSetTransID( pRCache, 0xFFFFFFFF); + + // If the low transaction ID is the same as the transaction, + // we may have modified this record during the transaction. + // Unfortunately, there is no sure way to tell, so we are + // forced to assume it may have been modified. If the + // transaction aborts, we will get rid if this version out + // of cache. + + if (uiLowTransId == pDb->LogHdr.uiCurrTransID) + { + RCA_SET_UNCOMMITTED( pRCache->uiFlags); + flmRcaLinkToFileAtHead( pRCache, pFile); + } + else + { + RCA_UNSET_UNCOMMITTED( pRCache->uiFlags); + flmRcaLinkToFileAtEnd( pRCache, pFile); + } +#ifdef FLM_DBG_LOG + f_sprintf( szTmpBuf, "RCI:L%X,H%X", + (unsigned)pRCache->uiLowTransId, + (unsigned)pRCache->uiHighTransId); + + flmDbgLogWrite( pFile->uiFFileId, pRCache->uiContainer, pRCache->uiDrn, + pDb->LogHdr.uiCurrTransID, szTmpBuf); +#endif + } + else + { + // Adjust the high transaction ID to be the same as + // the transaction ID - we may have gotten a 0xFFFFFFF + // back, but that is possible even if the record is + // not the most current version. Besides that, it is + // possible that in the mean time one or more update + // transactions have come along and created one or + // more newer versions of the record. + + FLMUINT uiHighTransId = (FLMUINT)((bMostCurrent) + ? (FLMUINT)0xFFFFFFFF + : pDb->LogHdr.uiCurrTransID); + + flmRcaSetTransID( pRCache, uiHighTransId); + + // For a read transaction, if there is a newer version, + // it better have a higher "low transaction ID" + +#ifdef FLM_DBG_LOG + f_sprintf( szTmpBuf, "RCA:L%X,H%X", + (unsigned)pRCache->uiLowTransId, + (unsigned)pRCache->uiHighTransId); + + flmDbgLogWrite( pFile->uiFFileId, pRCache->uiContainer, pRCache->uiDrn, + pDb->LogHdr.uiCurrTransID, szTmpBuf); +#endif + +#ifdef FLM_DEBUG + if (pRCache->pNewerVersion && + !RCA_IS_READING_IN( pRCache->pNewerVersion->uiFlags)) + { + flmAssert( pRCache->uiHighTransId < + pRCache->pNewerVersion->uiLowTransId); + if( pRCache->uiHighTransId >= + pRCache->pNewerVersion->uiLowTransId) + { + flmRcaCheck( pDb, pRCache->uiContainer, pRCache->uiDrn); + } + } +#endif + RCA_UNSET_UNCOMMITTED( pRCache->uiFlags); + flmRcaLinkToFileAtEnd( pRCache, pFile); + } + + // Coalesce any versions that overlap - can only + // coalesce older versions. For an updater, there + // should not be any newer versions. For a reader, it + // is impossible to know how high up it can coalesce. + // The read operation that read the record may have + // gotten back a 0xFFFFFFFF for its high transaction + // ID - but after that point in time, it is possible + // that one or more update transactions may have come + // along and created one or more newer versions that + // it would be incorrect to coalesce with. + // In reality, a read transaction has to ignore the + // 0xFFFFFFFF in the high transaction ID anyway + // because there is no way to know if it is correct. + + // Coalesce older versions. + + for (;;) + { + if ((pTmpRCache = pRCache->pOlderVersion) == NULL) + { + break; + } + + // Stop if we encounter one that is being read in. + + if (RCA_IS_READING_IN( pTmpRCache->uiFlags)) + { + break; + } + + // If there is no overlap between these two, there is + // nothing more to coalesce. + + if (pRCache->uiLowTransId > pTmpRCache->uiHighTransId) + { + break; + } + + if (pRCache->uiHighTransId <= pTmpRCache->uiHighTransId) + { + // This assert represents the following case, + // which should not be possible to hit: + + // pOlder->uiHighTransId > pRCache->uiHighTransId. + // This cannot be, because if pOlder has a higher + // transaction ID, we would have found it up above and + // not tried to have read it in. + + flmAssert( 0); +#ifdef FLM_DEBUG + flmRcaCheck( pDb, pRCache->uiContainer, pRCache->uiDrn); +#endif + } + else if (pRCache->uiLowTransId >= pTmpRCache->uiLowTransId) + { + pRCache->uiLowTransId = pTmpRCache->uiLowTransId; + flmRcaFreeCache( pTmpRCache, + (FLMBOOL)((RCA_IS_IN_USE( pTmpRCache->uiFlags) || + RCA_IS_READING_IN( pTmpRCache->uiFlags)) + ? TRUE + : FALSE)); + } + else + { + // This assert represents the following case, + // which should not be possible to hit: + + // pRCache->uiLowTransId < pOlder->uiLowTransId. + // This cannot be, because pOlder has to have been read + // in to memory by a transaction whose transaction ID is + // less than or equal to our own. That being the case, + // it would be impossible for our transaction to have + // found a version of the record that is older than pOlder. + + flmAssert( 0); +#ifdef FLM_DEBUG + flmRcaCheck( pDb, pRCache->uiContainer, pRCache->uiDrn); +#endif + } + } +} + +/**************************************************************************** +Desc: This routine retrieves a record from the record cache. +****************************************************************************/ +RCODE flmRcaRetrieveRec( + FDB * pDb, + FLMBOOL * pbTransStarted, + FLMUINT uiContainer, // Container record is in. + FLMUINT uiDrn, // DRN of record. + FLMBOOL bOkToGetFromDisk, // If not in cache, OK to get from disk? + BTSK * pStack, // Use stack to retrieve, if NON-NULL. + LFILE * pLFile, // LFILE to use, if retrieving with stack + FlmRecord ** ppRecord) +{ + RCODE rc = FERR_OK; + FLMBOOL bRCacheMutexLocked = FALSE; + FFILE * pFile = pDb->pFile; + RCACHE * pRCache; + RCACHE * pNewerRCache; + RCACHE * pOlderRCache; + FlmRecord * pRecord = NULL; + FLMBOOL bGotFromDisk = FALSE; + FlmRecord * pNewRecord = NULL; + FlmRecord * pOldRecord = NULL; + FLMUINT uiLowTransId; + FLMBOOL bMostCurrent; + FLMUINT uiCurrTransId; + FNOTIFY * pNotify; + FLMUINT uiNumLooks; + FLMBOOL bInitializedFdb = FALSE; + FLMBOOL bDontPoisonCache = pDb->uiFlags & FDB_DONT_POISON_CACHE + ? TRUE + : FALSE; + + if( RC_BAD( rc = flmCheckDatabaseState( pDb))) + { + goto Exit; + } + + // Get the current transaction ID + + if( pDb->uiTransType != FLM_NO_TRANS) + { + uiCurrTransId = pDb->LogHdr.uiCurrTransID; + } + else + { + flmAssert( pbTransStarted != NULL); + + f_mutexLock( gv_FlmSysData.hShareMutex); + + // Get the last committed transaction ID. + + uiCurrTransId = (FLMUINT)FB2UD( + &pFile->ucLastCommittedLogHdr[ LOG_CURR_TRANS_ID]); + + f_mutexUnlock( gv_FlmSysData.hShareMutex); + } + + flmAssert( uiDrn != 0); + + // Lock the mutex + + f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); + bRCacheMutexLocked = TRUE; + + // Reset the FDB's inactive time + + pDb->uiInactiveTime = 0; + + // See if we should resize the hash table. + + if ((gv_FlmSysData.RCacheMgr.Usage.uiCount > + FLM_RCA_MAX_REC_CNT( gv_FlmSysData.RCacheMgr.uiNumBuckets) && + gv_FlmSysData.RCacheMgr.uiNumBuckets < MAX_RCACHE_BUCKETS) || + (gv_FlmSysData.RCacheMgr.Usage.uiCount < + FLM_RCA_MIN_REC_CNT( gv_FlmSysData.RCacheMgr.uiNumBuckets) && + gv_FlmSysData.RCacheMgr.uiNumBuckets > MIN_RCACHE_BUCKETS)) + { + if (RC_BAD( rc = flmRcaRehash())) + { + goto Exit; + } + } + +Start_Find: + + flmRcaFindRec( uiContainer, uiDrn, pFile, + uiCurrTransId, bDontPoisonCache, + &uiNumLooks, &pRCache, + &pNewerRCache, &pOlderRCache); + + if (pRCache) + { + if( ppRecord) + { + goto Found_Record; + } + goto Exit; + } + + // Did not find the record, fetch from disk, if OK to do so. + + if (!bOkToGetFromDisk || !ppRecord) + { + rc = RC_SET( FERR_NOT_FOUND); + goto Exit; + } + + // Code to handle case where we are not in a transaction. + // If we are already in a transaction, we will do the + // call to fdbInit below - AFTER allocating the RCACHE, etc. + + if( pbTransStarted && pDb->uiTransType == FLM_NO_TRANS) + { + f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); + bRCacheMutexLocked = FALSE; + + if ( RC_BAD( rc = fdbInit( pDb, FLM_READ_TRANS, + FDB_TRANS_GOING_OK, 0, pbTransStarted))) + { + fdbExit( pDb); + goto Exit; + } + bInitializedFdb = TRUE; + + f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); + bRCacheMutexLocked = TRUE; + + uiCurrTransId = pDb->LogHdr.uiCurrTransID; + goto Start_Find; + } + + // Increment the number of faults only if we retrieve the record from disk. + + gv_FlmSysData.RCacheMgr.Usage.uiCacheFaults++; + gv_FlmSysData.RCacheMgr.Usage.uiCacheFaultLooks += uiNumLooks; + + // Create a place holder for the object. + + if (RC_BAD( rc = flmRcaAllocCacheStruct( &pRCache))) + { + goto Exit; + } + pRCache->uiDrn = uiDrn; + pRCache->uiContainer = uiContainer; + + // Set the FFILE so that other threads looking for this record in + // cache will find it and wait until the read has completed. If + // the FFILE is not set, other threads will attempt their own read, + // because they won't match a NULL FFILE. The result of not setting + // the FFILE is that multiple copies of the same version of a particular + // record could end up in cache. + + pRCache->pFile = pFile; + + flmRcaLinkIntoRCache( pNewerRCache, pOlderRCache, + pRCache, !bDontPoisonCache); + + RCA_SET_READING_IN( pRCache->uiFlags); + RCA_INCR_USE_COUNT( pRCache->uiFlags); + pRCache->pNotifyList = NULL; + + // Unlock mutex before reading in from disk. + + f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); + bRCacheMutexLocked = FALSE; + + if( pbTransStarted && !bInitializedFdb) + { + if ( RC_BAD( rc = fdbInit( pDb, FLM_READ_TRANS, + FDB_TRANS_GOING_OK, 0, pbTransStarted))) + { + fdbExit( pDb); + goto Notify_Waiters; + } + bInitializedFdb = TRUE; + } + + // Read record from disk. + +#ifdef FLM_DBG_LOG + flmDbgLogWrite( pFile->uiFFileId, uiContainer, + uiDrn, pDb->LogHdr.uiCurrTransID, "RRD"); +#endif + + if (pStack) + { + rc = FSReadElement( pDb, &pDb->TempPool, pLFile, + uiDrn, pStack, TRUE, ppRecord, + &uiLowTransId, &bMostCurrent); + } + else if ((pLFile) || + (RC_OK( rc = fdictGetContainer( pDb->pDict, uiContainer, &pLFile)))) + { + rc = FSReadRecord( pDb, pLFile, uiDrn, ppRecord, + &uiLowTransId, &bMostCurrent); + } + +Notify_Waiters: + + // Relock mutex + + f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); + bRCacheMutexLocked = TRUE; + + // If read was successful, link the record to its place in + // the FFILE list and coalesce any versions that overlap + // this one. + + if (RC_OK( rc)) + { + flmRcaLinkToFFILE( pRCache, + pDb->pFile, pDb, uiLowTransId, bMostCurrent); + } + + RCA_UNSET_READING_IN( pRCache->uiFlags); + + // Notify any threads waiting for the read to complete. + + pNotify = pRCache->pNotifyList; + pRCache->pNotifyList = NULL; + if (pNotify) + { + flmRcaNotify( pNotify, + (RCACHE *)((RC_BAD( rc)) + ? NULL + : pRCache), rc); + } + RCA_DECR_USE_COUNT( pRCache->uiFlags); + + // If we did not succeed, free the RCACHE structure. + + if (RC_BAD( rc)) + { + flmRcaFreeCache( pRCache, FALSE); + goto Exit; + } + + // If this item was purged while we were reading it in, + // start over with the search. + + if (RCA_IS_PURGED( pRCache->uiFlags)) + { + if (!RCA_IS_IN_USE( pRCache->uiFlags)) + { + flmRcaFreePurged( pRCache); + } + + // Start over with the find - this one has + // been marked for purging. + + pNewRecord = NULL; + pOldRecord = NULL; + bGotFromDisk = FALSE; + goto Start_Find; + } + + // When reading from disk, no need to verify that we + // are getting the app implementation - because we + // always will. + + bGotFromDisk = TRUE; + pRecord = *ppRecord; + flmRcaSetRecord( pRCache, pRecord); + +Found_Record: + + if (!bGotFromDisk) + { + if( (pRecord = *ppRecord) != pRCache->pRecord) + { + if (*ppRecord) + { + FlmRecordExt::Release( *ppRecord, bRCacheMutexLocked); + } + + pRecord = *ppRecord = pRCache->pRecord; + FlmRecordExt::AddRef( pRecord, bRCacheMutexLocked); + } + } + + // Clean up cache, if necessary. + + if (gv_FlmSysData.RCacheMgr.pRCacheAlloc->getTotalBytesAllocated() > + gv_FlmSysData.RCacheMgr.Usage.uiMaxBytes) + { + flmRcaReduceCache( bRCacheMutexLocked); + } + +Exit: + + if (pNewRecord) + { + FlmRecordExt::Release( pNewRecord, bRCacheMutexLocked); + } + + if (bRCacheMutexLocked) + { + f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); + } + + if (bInitializedFdb) + { + fdbExit(pDb); + } + + return( rc); +} + +/**************************************************************************** +Desc: This routine inserts a record into the record cache. This is ONLY + called by FlmRecordModify or FlmRecordAdd. In the case of a modify, + this should replace any uncommitted version of the record that may + have been put there by a prior call to FlmRecordModify. +****************************************************************************/ +RCODE flmRcaInsertRec( + FDB * pDb, + LFILE * pLFile, // Container record is in. + FLMUINT uiDrn, // DRN of record + FlmRecord * pRecord) // Record to be inserted. +{ + RCODE rc = FERR_OK; + FFILE * pFile = pDb->pFile; + FLMUINT uiContainer = pLFile->uiLfNum; + FLMBOOL bMutexLocked = FALSE; + RCACHE * pRCache; + RCACHE * pNewerRCache; + RCACHE * pOlderRCache; + FLMBOOL bDontPoisonCache = pDb->uiFlags & FDB_DONT_POISON_CACHE + ? TRUE + : FALSE; + + if (pLFile->bMakeFieldIdTable && !pRecord->fieldIdTableEnabled()) + { + + // NOTE: createFieldIdTable will call sortFieldIdTable(). + + if (RC_BAD( rc = pRecord->createFieldIdTable( TRUE))) + { + goto Exit; + } + } + else + { + pRecord->sortFieldIdTable(); + if (getFieldIdTableItemCount( pRecord->getFieldIdTbl()) != + getFieldIdTableArraySize( pRecord->getFieldIdTbl())) + { + if (RC_BAD( rc = pRecord->truncateFieldIdTable())) + { + goto Exit; + } + } + } + flmAssert( uiDrn != 0); + + // Lock the mutex + + f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); + bMutexLocked = TRUE; + + if ((gv_FlmSysData.RCacheMgr.Usage.uiCount > + FLM_RCA_MAX_REC_CNT( gv_FlmSysData.RCacheMgr.uiNumBuckets) && + gv_FlmSysData.RCacheMgr.uiNumBuckets < MAX_RCACHE_BUCKETS) || + (gv_FlmSysData.RCacheMgr.Usage.uiCount < + FLM_RCA_MIN_REC_CNT( gv_FlmSysData.RCacheMgr.uiNumBuckets) && + gv_FlmSysData.RCacheMgr.uiNumBuckets > MIN_RCACHE_BUCKETS)) + { + if (RC_BAD( rc = flmRcaRehash())) + { + goto Exit; + } + } + + // See if we can find the record in cache + + flmRcaFindRec( uiContainer, uiDrn, pFile, + pDb->LogHdr.uiCurrTransID, bDontPoisonCache, NULL, &pRCache, + &pNewerRCache, &pOlderRCache); + + if (pRCache) + { + + // If we found the last committed version, instead of replacing it, + // we want to change its high transaction ID, and go create a new + // record to put in cache. + + if (pRCache->uiLowTransId < pDb->LogHdr.uiCurrTransID) + { + + // pOlderRCache and pRCache should be the same at this point if we + // found something. Furthermore, the high transaction ID on what + // we found better be -1 - most current version. + + flmAssert( pOlderRCache == pRCache); + flmAssert( pOlderRCache->uiHighTransId == 0xFFFFFFFF); + + flmRcaSetTransID( pOlderRCache, (pDb->LogHdr.uiCurrTransID - 1)); + + flmAssert( pOlderRCache->uiHighTransId >= pOlderRCache->uiLowTransId); + + RCA_SET_UNCOMMITTED( pOlderRCache->uiFlags); + RCA_SET_LATEST_VER( pOlderRCache->uiFlags); + flmRcaUnlinkFromFile( pOlderRCache); + flmRcaLinkToFileAtHead( pOlderRCache, pFile); + } + else + { + // Found latest UNCOMMITTED VERSION - replace it. + +#ifdef FLM_CHECK_RECORD + if (RC_BAD( rc = pRecord->checkRecord())) + { + goto Exit; + } +#endif + + if (RC_BAD( rc = pRecord->compressMemory())) + { + goto Exit; + } + +#ifdef FLM_CHECK_RECORD + if (RC_BAD( rc = pRecord->checkRecord())) + { + goto Exit; + } +#endif + + // Replace the old record data with the new record data. + + flmRcaSetRecord( pRCache, pRecord); + + // Make sure we set the "uncommitted" flag and move the record + // to the head of the FFILE's list. + + if (!RCA_IS_UNCOMMITTED( pRCache->uiFlags)) + { + RCA_SET_UNCOMMITTED( pRCache->uiFlags); + flmRcaUnlinkFromFile( pRCache); + flmRcaLinkToFileAtHead( pRCache, pFile); + } + + // Will not have already been put at MRU if bDonPoisonCache is TRUE. + + if (pRCache->pPrevInGlobal) + { + flmRcaUnlinkFromGlobal( pRCache); + flmRcaLinkToGlobalAsMRU( pRCache); + } + + goto Exit; + } + } + + // We are positioned to insert the new record. For an update, it + // must always be the newest version. + + flmAssert( pNewerRCache == NULL); + +#ifdef FLM_CHECK_RECORD + if (RC_BAD( rc = pRecord->checkRecord())) + { + goto Exit; + } +#endif + + if (RC_BAD( rc = pRecord->compressMemory())) + { + goto Exit; + } + +#ifdef FLM_CHECK_RECORD + if (RC_BAD( rc = pRecord->checkRecord())) + { + goto Exit; + } +#endif + + // Allocate a new RCACHE structure. + + if (RC_BAD( rc = flmRcaAllocCacheStruct( &pRCache))) + { + goto Exit; + } + + // Set the DRN and container for the structure. + + pRCache->uiDrn = uiDrn; + pRCache->uiContainer = uiContainer; + pRCache->pFile = pFile; + + // Link into the global list and hash bucket. + + flmRcaLinkIntoRCache( pNewerRCache, pOlderRCache, pRCache, TRUE); + + // Link to its FFILE and coalesce out duplicates. + // NOTE: This routine automatically puts an uncommitted version + // at the head of the FFILE's list and sets the uncommitted flag. + + flmRcaLinkToFFILE( pRCache, pFile, pDb, pDb->LogHdr.uiCurrTransID, TRUE); + + // Set the record data pointer for the RCACHE structure. This will also + // release the old data pointer and set the read only flag and the mutex + // for the record data. Also updates memory usage fields in RCacheMgr. + + flmRcaSetRecord( pRCache, pRecord); + + // Clean up cache, if necessary. + + flmRcaReduceCache( bMutexLocked); + +Exit: + + if (bMutexLocked) + { + f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); + } + + return( rc); +} + +/**************************************************************************** +Desc: This routine is called by FlmRecordDelete to remove a record from + cache. If there is an uncommitted version of the record, it should + remove that version from cache. If the last committed version is in + cache, it should set the high transaction ID on that version to be + one less than the transaction ID of the update transaction that is + doing the FlmRecordDelete call. +****************************************************************************/ +RCODE flmRcaRemoveRec( + FDB * pDb, + FLMUINT uiContainer, + FLMUINT uiDrn) +{ + RCODE rc = FERR_OK; + FLMBOOL bMutexLocked = FALSE; + RCACHE * pRCache; + RCACHE * pNewerRCache; + RCACHE * pOlderRCache; + FFILE * pFile = pDb->pFile; + + flmAssert( uiDrn != 0); + + // Lock the semaphore + + bMutexLocked = TRUE; + f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); + + if ((gv_FlmSysData.RCacheMgr.Usage.uiCount > + FLM_RCA_MAX_REC_CNT( gv_FlmSysData.RCacheMgr.uiNumBuckets) && + gv_FlmSysData.RCacheMgr.uiNumBuckets < MAX_RCACHE_BUCKETS) || + (gv_FlmSysData.RCacheMgr.Usage.uiCount < + FLM_RCA_MIN_REC_CNT( gv_FlmSysData.RCacheMgr.uiNumBuckets) && + gv_FlmSysData.RCacheMgr.uiNumBuckets > MIN_RCACHE_BUCKETS)) + { + if (RC_BAD( rc = flmRcaRehash())) + { + goto Exit; + } + } + + // See if we can find the record in cache + + flmRcaFindRec( uiContainer, uiDrn, pFile, + pDb->LogHdr.uiCurrTransID, FALSE, NULL, &pRCache, + &pNewerRCache, &pOlderRCache); + + if (pRCache) + { + // FlmRecordDelete is calling this routine, so we determine if we found + // the last committed version or a record that was added by this same + // transaction. If we found the last committed version, set its high + // transaction ID. Otherwise, remove the record from cache. + + if (pRCache->uiLowTransId < pDb->LogHdr.uiCurrTransID) + { + + // pOlderRCache and pRCache should be the same at this point if we + // found something. Furthermore, the high transaction ID on what + // we found better be -1 - most current version. + + flmAssert( pOlderRCache == pRCache); + flmAssert( pOlderRCache->uiHighTransId == 0xFFFFFFFF); + + flmRcaSetTransID( pOlderRCache, (pDb->LogHdr.uiCurrTransID - 1)); + flmAssert( pOlderRCache->uiHighTransId >= pOlderRCache->uiLowTransId); + RCA_SET_UNCOMMITTED( pOlderRCache->uiFlags); + RCA_SET_LATEST_VER( pOlderRCache->uiFlags); + flmRcaUnlinkFromFile( pOlderRCache); + flmRcaLinkToFileAtHead( pOlderRCache, pFile); + } + else + { + flmRcaFreeCache( pRCache, + (FLMBOOL)(RCA_IS_IN_USE( pRCache->uiFlags) + ? TRUE + : FALSE)); + } + } + + // Take the opportunity to clean up cache. + + flmRcaReduceCache( bMutexLocked); + +Exit: + + if (bMutexLocked) + { + f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); + } + + return( rc); +} + +/**************************************************************************** +Desc: This routine is called when an FFILE structure is going to be removed + from the shared memory area. At that point, we also need to get rid + of all records that have been cached for that FFILE. +****************************************************************************/ +void flmRcaFreeFileRecs( + FFILE * pFile) +{ + FLMUINT uiNumFreed = 0; + + f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); + while (pFile->pFirstRecord) + { + flmRcaFreeCache( pFile->pFirstRecord, + RCA_IS_IN_USE( pFile->pFirstRecord->uiFlags) + ? TRUE + : FALSE); + + // Release the CPU every 100 records freed. + + if (uiNumFreed < 100) + { + uiNumFreed++; + } + else + { + f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); + f_yieldCPU(); + f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); + uiNumFreed = 0; + } + } + f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); +} + +/**************************************************************************** +Desc: This routine is called when an update transaction aborts. At that + point, we need to get rid of any uncommitted versions of records in + the record cache. +****************************************************************************/ +void flmRcaAbortTrans( + FDB * pDb) +{ + FFILE * pFile = pDb->pFile; + RCACHE * pRCache; + RCACHE * pOlderVersion; + FLMUINT uiOlderTransId = + pDb->LogHdr.uiCurrTransID - 1; + + f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); + pRCache = pFile->pFirstRecord; + while (pRCache) + { + if (RCA_IS_UNCOMMITTED( pRCache->uiFlags)) + { + if (RCA_IS_LATEST_VER( pRCache->uiFlags)) + { + flmRcaSetTransID( pRCache, 0xFFFFFFFF); + RCA_UNSET_UNCOMMITTED( pRCache->uiFlags); + RCA_UNSET_LATEST_VER( pRCache->uiFlags); + flmRcaUnlinkFromFile( pRCache); + flmRcaLinkToFileAtEnd( pRCache, pFile); + } + else + { + // Save the older version - we may be changing its + // high transaction ID back to 0xFFFFFFFF + + pOlderVersion = pRCache->pOlderVersion; + + // Free the uncommitted version. + + flmRcaFreeCache( pRCache, + (FLMBOOL)((RCA_IS_IN_USE( pRCache->uiFlags) || + RCA_IS_READING_IN( pRCache->uiFlags)) + ? TRUE + : FALSE)); + + // If the older version has a high transaction ID that + // is exactly one less than our current transaction, + // it is the most current version. Hence, we need to + // change its high transaction ID back to 0xFFFFFFFF. + + if ((pOlderVersion) && + (pOlderVersion->uiHighTransId == uiOlderTransId)) + { + flmRcaSetTransID( pOlderVersion, 0xFFFFFFFF); + } + } + pRCache = pFile->pFirstRecord; + } + else + { + // We can stop when we hit a committed version, because + // uncommitted versions are always linked in together at + // the head of the list. + + break; + } + } + f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); +} + +/**************************************************************************** +Desc: This routine is called when an update transaction commits. At that + point, we need to unset the "uncommitted" flag on any records + currently in record cache for the FFILE. +****************************************************************************/ +void flmRcaCommitTrans( + FDB * pDb) +{ + RCACHE * pRCache; + + f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); + pRCache = pDb->pFile->pFirstRecord; + while (pRCache) + { + if (RCA_IS_UNCOMMITTED( pRCache->uiFlags)) + { + RCA_UNSET_UNCOMMITTED( pRCache->uiFlags); + RCA_UNSET_LATEST_VER( pRCache->uiFlags); + pRCache = pRCache->pNextInFile; + } + else + { + + // We can stop when we hit a committed version, because + // uncommitted versions are always linked in together at + // the head of the list. + + break; + } + } + f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); +} + +/**************************************************************************** +Desc: This routine is called when a container in the database is deleted. + All records in record cache that are in that container must be + removed from cache. +****************************************************************************/ +void flmRcaRemoveContainerRecs( + FDB * pDb, + FLMUINT uiContainer) +{ + FFILE * pFile = pDb->pFile; + RCACHE * pRCache; + RCACHE * pPrevRCache; + FLMUINT uiTransId = pDb->LogHdr.uiCurrTransID; + + f_mutexLock( gv_FlmSysData.RCacheMgr.hMutex); + pRCache = gv_FlmSysData.RCacheMgr.pLRURecord; + + // Stay in the loop until we have freed all old records, or + // we have run through the entire list. + + while (pRCache) + { + // Save the pointer to the previous entry in the list because + // we may end up unlinking pRCache below, in which case we would + // have lost the previous entry. + + pPrevRCache = pRCache->pPrevInGlobal; + + // Only look at records in this container and this database. + + if ((pRCache->uiContainer == uiContainer) && + (pRCache->pFile == pFile)) + { + + // Only look at the most current versions. + + if (pRCache->uiHighTransId == 0xFFFFFFFF) + { + + // Better not be a newer version. + + flmAssert( pRCache->pNewerVersion == NULL); + + if (pRCache->uiLowTransId < uiTransId) + { + + // This version was not added or modified by this + // transaction so it's high transaction ID should simply + // be set to one less than the current transaction ID. + + flmRcaSetTransID( pRCache, uiTransId - 1); + flmAssert( pRCache->uiHighTransId >= pRCache->uiLowTransId); + RCA_SET_UNCOMMITTED( pRCache->uiFlags); + RCA_SET_LATEST_VER( pRCache->uiFlags); + flmRcaUnlinkFromFile( pRCache); + flmRcaLinkToFileAtHead( pRCache, pFile); + } + else + { + + // The record was added or modified in this + // transaction. Simply remove it from cache. + + flmRcaFreeCache( pRCache, + (FLMBOOL)(RCA_IS_IN_USE( pRCache->uiFlags) + ? TRUE + : FALSE)); + } + } + else + { + + // If not most current version, the record's high transaction + // ID better already be less than transaction ID. + + flmAssert( pRCache->uiHighTransId < uiTransId); + } + } + pRCache = pPrevRCache; + + } + f_mutexUnlock( gv_FlmSysData.RCacheMgr.hMutex); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +#ifdef FLM_DEBUG +FSTATIC RCODE flmRcaCheck( + FDB * pDb, + FLMUINT uiContainer, + FLMUINT uiDrn) +{ + LFILE * pLFile; + FlmRecord * pRecord = NULL; + FLMUINT uiLowTransId; + FLMBOOL bMostCurrent; + RCODE rc; + + if( RC_OK( rc = fdictGetContainer( pDb->pDict, uiContainer, &pLFile))) + { + rc = FSReadRecord( pDb, pLFile, uiDrn, &pRecord, + &uiLowTransId, &bMostCurrent); + } + + if( pRecord) + { + pRecord->Release(); + } + + return( rc); +} +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMBOOL FlmRecordExt::canRelocateRec( + void * pvAlloc) +{ + FlmRecord * pRec = (FlmRecord *)pvAlloc; + + if( pRec->getRefCount() == 1 && pRec->isCached()) + { + return( TRUE); + } + + return( FALSE); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void FlmRecordExt::relocateRec( + void * pvOldAlloc, + void * pvNewAlloc) +{ + FlmRecord * pOldRec = (FlmRecord *)pvOldAlloc; + FlmRecord * pNewRec = (FlmRecord *)pvNewAlloc; + RCACHE * pRCache; + RCACHE * pVersion; + + flmAssert( pOldRec->getRefCount() == 1); + flmAssert( pOldRec->isCached()); + flmAssert( pvNewAlloc < pvOldAlloc); + + // Update the record pointer in the data buffer + + if( pNewRec->m_pucBuffer) + { + flmAssert( *((FlmRecord **)pOldRec->m_pucBuffer) == pOldRec); + *((FlmRecord **)pNewRec->m_pucBuffer) = pNewRec; + } + if( pNewRec->m_pucFieldIdTable) + { + flmAssert( *((FlmRecord **)pOldRec->m_pucFieldIdTable) == pOldRec); + *((FlmRecord **)pNewRec->m_pucFieldIdTable) = pNewRec; + } + + // Find the record + + pRCache = *(FLM_RCA_HASH( pNewRec->m_uiRecordID)); + + while( pRCache) + { + if( pRCache->uiDrn == pNewRec->m_uiRecordID) + { + pVersion = pRCache; + + while( pVersion) + { + if( pVersion->pRecord == pOldRec) + { + pVersion->pRecord = pNewRec; + goto Done; + } + + pVersion = pVersion->pOlderVersion; + } + } + + pRCache = pRCache->pNextInBucket; + } + +Done: + + flmAssert( pRCache); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMBOOL FlmRecordExt::canRelocateRecBuffer( + void * pvAlloc) +{ + FlmRecord * pRec = *((FlmRecord **)pvAlloc); + + flmAssert( pRec->m_pucBuffer == pvAlloc || pRec->m_pucFieldIdTable == pvAlloc); + + if( pRec->getRefCount() == 1 && pRec->isCached()) + { + return( TRUE); + } + + return( FALSE); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void FlmRecordExt::relocateRecBuffer( + void * pvOldAlloc, + void * pvNewAlloc) +{ + FlmRecord * pRec = *((FlmRecord **)pvOldAlloc); + + flmAssert( pRec->getRefCount() == 1); + flmAssert( pRec->isCached()); + flmAssert( pvNewAlloc < pvOldAlloc); + + // Update the buffer pointer in the record + + if (pRec->m_pucBuffer == pvOldAlloc) + { + pRec->m_pucBuffer = (FLMBYTE *)pvNewAlloc; + } + else if (pRec->m_pucFieldIdTable == pvOldAlloc) + { + pRec->m_pucFieldIdTable = (FLMBYTE *)pvNewAlloc; + } + else + { + flmAssert( 0); + } +} + +/**************************************************************************** +Desc: +Notes: This routine assumes the rcache mutex is locked +****************************************************************************/ +FSTATIC FLMBOOL rcaCanRelocate( + void * pvAlloc) +{ + RCACHE * pRCache = (RCACHE *)pvAlloc; + + if( RCA_IS_IN_USE( pRCache->uiFlags) || + RCA_IS_READING_IN( pRCache->uiFlags)) + { + return( FALSE); + } + + return( TRUE); +} + +/**************************************************************************** +Desc: Fixes up all pointers needed to allow an RCACHE struct to be + moved to a different location in memory +Notes: This routine assumes the rcache mutex is locked +****************************************************************************/ +FSTATIC void rcaRelocate( + void * pvOldAlloc, + void * pvNewAlloc) +{ + RCACHE * pOldRCache = (RCACHE *)pvOldAlloc; + RCACHE * pNewRCache = (RCACHE *)pvNewAlloc; + RCACHE ** ppBucket; + RCACHE_MGR * pRCacheMgr = &gv_FlmSysData.RCacheMgr; + FFILE * pFile = pOldRCache->pFile; + + flmAssert( !RCA_IS_IN_USE( pOldRCache->uiFlags)); + flmAssert( !RCA_IS_READING_IN( pOldRCache->uiFlags)); + flmAssert( !pOldRCache->pNotifyList); + + if( pNewRCache->pPrevInFile) + { + pNewRCache->pPrevInFile->pNextInFile = pNewRCache; + } + + if( pNewRCache->pNextInFile) + { + pNewRCache->pNextInFile->pPrevInFile = pNewRCache; + } + + if( pNewRCache->pPrevInGlobal) + { + pNewRCache->pPrevInGlobal->pNextInGlobal = pNewRCache; + } + + if( pNewRCache->pNextInGlobal) + { + pNewRCache->pNextInGlobal->pPrevInGlobal = pNewRCache; + } + + if( pNewRCache->pPrevInBucket) + { + pNewRCache->pPrevInBucket->pNextInBucket = pNewRCache; + } + + if( pNewRCache->pNextInBucket) + { + pNewRCache->pNextInBucket->pPrevInBucket = pNewRCache; + } + + if( pNewRCache->pOlderVersion) + { + pNewRCache->pOlderVersion->pNewerVersion = pNewRCache; + } + + if( pNewRCache->pNewerVersion) + { + pNewRCache->pNewerVersion->pOlderVersion = pNewRCache; + } + + if( pNewRCache->pPrevInHeapList) + { + pNewRCache->pPrevInHeapList->pNextInHeapList = pNewRCache; + } + + if( pNewRCache->pNextInHeapList) + { + pNewRCache->pNextInHeapList->pPrevInHeapList = pNewRCache; + } + + ppBucket = FLM_RCA_HASH( pOldRCache->uiDrn); + if( *ppBucket == pOldRCache) + { + *ppBucket = pNewRCache; + } + + if( pRCacheMgr->pHeapList == pOldRCache) + { + pRCacheMgr->pHeapList = pNewRCache; + } + + if( pRCacheMgr->pPurgeList == pOldRCache) + { + pRCacheMgr->pPurgeList = pNewRCache; + } + + if( pRCacheMgr->pMRURecord == pOldRCache) + { + pRCacheMgr->pMRURecord = pNewRCache; + } + + if( pRCacheMgr->pLRURecord == pOldRCache) + { + pRCacheMgr->pLRURecord = pNewRCache; + } + + if( pFile) + { + if( pFile->pFirstRecord == pOldRCache) + { + pFile->pFirstRecord = pNewRCache; + } + + if( pFile->pLastRecord == pOldRCache) + { + pFile->pLastRecord = pNewRCache; + } + } + +#ifdef FLM_DEBUG + f_memset( pOldRCache, 0, sizeof( RCACHE)); +#endif +} diff --git a/flaim/src/rfl.cpp b/flaim/src/rfl.cpp index 8bc5e93..886daec 100644 --- a/flaim/src/rfl.cpp +++ b/flaim/src/rfl.cpp @@ -1,8432 +1,8432 @@ -//------------------------------------------------------------------------- -// Desc: Routines for roll-forward logging. -// Tabs: 3 -// -// Copyright (c) 1998-2006 Novell, Inc. All Rights Reserved. -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of version 2 of the GNU General Public -// License as published by the Free Software Foundation. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, contact Novell, Inc. -// -// To contact Novell about this file by physical or electronic mail, -// you may find current contact information at www.novell.com -// -// $Id: rfl.cpp 12334 2006-01-23 12:45:35 -0700 (Mon, 23 Jan 2006) dsanders $ -//------------------------------------------------------------------------- - -#include "flaimsys.h" - -#define MOD_512(uiNum) \ - (FLMUINT) ((uiNum) & 511) - -#define ON_512_BYTE_BOUNDARY(uiNum) \ - (!MOD_512( uiNum)) - -#define ROUND_DOWN_TO_NEAREST_512(uiNum) \ - (FLMUINT) ((uiNum) & (~((FLMUINT) 511))) - -FSTATIC RCODE RflCheckMaxLogged( - FLMUINT * puiMaxBytesNeededRV, - FLMUINT uiPacketsLogged, - FLMUINT * puiCurrTotalLoggedRV, - FLMUINT uiBytesToLog); - -FSTATIC void RflChangeCallback( - GRD_DifferenceData & DiffData, - void * CallbackData); - -/******************************************************************** -Desc: -*********************************************************************/ -F_Rfl::F_Rfl() -{ - m_pFile = NULL; - m_hBufMutex = F_MUTEX_NULL; - m_pCommitBuf = NULL; - m_pCurrentBuf = NULL; - m_uiRflWriteBufs = DEFAULT_RFL_WRITE_BUFFERS; - m_uiBufferSize = DEFAULT_RFL_BUFFER_SIZE; - f_memset( &m_Buf1, 0, sizeof(m_Buf1)); - f_memset( &m_Buf2, 0, sizeof(m_Buf2)); - m_bKeepRflFiles = FALSE; - m_uiRflMinFileSize = DEFAULT_MIN_RFL_FILE_SIZE; - m_uiRflMaxFileSize = DEFAULT_MAX_RFL_FILE_SIZE; - m_pFileHdl = NULL; - m_uiLastRecoverFileNum = 0; - f_memset( m_ucCurrSerialNum, 0, sizeof(m_ucCurrSerialNum)); - m_bLoggingOff = FALSE; - m_bLoggingUnknown = FALSE; - m_uiUnknownPacketLen = 0; - m_bReadingUnknown = FALSE; - m_uiUnknownPacketBodyLen = 0; - m_pucUnknownPacketBody = NULL; - m_uiUnknownBodyLenProcessed = 0; - m_uiUnknownPacketRc = FERR_OK; - m_uiTransStartFile = 0; - m_uiTransStartAddr = 0; - m_uiCurrTransID = 0; - m_uiLastTransID = 0; - m_uiLastLoggedCommitTransID = 0; - m_uiOperCount = 0; - m_uiRflReadOffset = 0; - m_uiFileEOF = 0; - m_pRestore = NULL; - f_memset( m_szDbPrefix, 0, sizeof(m_szDbPrefix)); - f_memset( m_szRflDir, 0, sizeof(m_szRflDir)); - m_bRflDirSameAsDb = FALSE; - m_bCreateRflDir = FALSE; - f_memset( m_ucNextSerialNum, 0, sizeof(m_ucNextSerialNum)); - m_bRflVolumeOk = TRUE; - m_bRflVolumeFull = FALSE; -} - -/******************************************************************** -Desc: -*********************************************************************/ -F_Rfl::~F_Rfl() -{ - - // Better not be in the middle of logging unknown packets for the - // application. - - flmAssert( !m_bLoggingUnknown); - - if (m_Buf1.pIOBuffer) - { - m_Buf1.pIOBuffer->Release(); - m_Buf1.pIOBuffer = NULL; - } - - if (m_Buf2.pIOBuffer) - { - m_Buf2.pIOBuffer->Release(); - m_Buf2.pIOBuffer = NULL; - } - - if (m_Buf1.pBufferMgr) - { - flmAssert( !m_Buf1.pBufferMgr->havePendingIO() && - !m_Buf1.pBufferMgr->haveUsed()); - - m_Buf1.pBufferMgr->Release(); - m_Buf1.pBufferMgr = NULL; - } - - if (m_Buf2.pBufferMgr) - { - flmAssert( !m_Buf2.pBufferMgr->havePendingIO() && - !m_Buf2.pBufferMgr->haveUsed()); - - m_Buf2.pBufferMgr->Release(); - m_Buf2.pBufferMgr = NULL; - } - - if (m_hBufMutex != F_MUTEX_NULL) - { - f_mutexDestroy( &m_hBufMutex); - } - - if (m_pFileHdl) - { - m_pFileHdl->Close(); - m_pFileHdl->Release(); - m_pFileHdl = NULL; - m_pFile = NULL; - } -} - -/******************************************************************** -Desc: Returns a boolean indicating whether or not we are at - the end of the RFL log - will only be TRUE when we are - doing recovery. -*********************************************************************/ -FLMBOOL F_Rfl::atEndOfLog(void) -{ - return( (!m_pRestore && m_uiFileEOF && - m_pCurrentBuf->uiRflFileOffset + - m_pCurrentBuf->uiRflBufBytes >= m_uiFileEOF && - m_uiRflReadOffset == m_pCurrentBuf->uiRflBufBytes && - m_pCurrentBuf->uiCurrFileNum == m_uiLastRecoverFileNum) - ? TRUE - : FALSE); -} - -/******************************************************************** -Desc: Gets the base RFL file name - does not have directory part. - This needs to be separate from the F_Rfl object so it can - be called without having to instantiate and set up an F_Rfl - object. -*********************************************************************/ -void rflGetBaseFileName( - FLMUINT uiDbVersion, - const char * pszDbPrefix, - FLMUINT uiFileNum, - char * pszBaseNameOut) -{ - FLMINT iCnt = 0; - FLMUINT uiDigit; - char * pszTmp = pszBaseNameOut; - - if (uiDbVersion < FLM_FILE_FORMAT_VER_4_3) - { - - // Output the database name prefix (up to three characters). - - f_strcpy( pszTmp, pszDbPrefix); - while (*pszTmp) - { - pszTmp++; - } - - // Output as five digit base 36 number. - - pszTmp += 4; - while (iCnt < 5) - { - uiDigit = (FLMUINT) (uiFileNum % 36); - uiFileNum /= 36; - if (uiDigit <= 9) - { - uiDigit += NATIVE_ZERO; - } - else - { - uiDigit += (NATIVE_LOWER_A - 10); - } - - *pszTmp = (FLMBYTE) uiDigit; - pszTmp--; - iCnt++; - } - - // Skip to end of digits and append ".log" to name - - pszTmp += 6; - f_strcpy( pszTmp, ".log"); - } - else - { - - // Output as eight digit hex number. - - pszTmp += 7; - while (iCnt < 8) - { - uiDigit = (FLMUINT) (uiFileNum & 0xF); - uiFileNum >>= 4; - if (uiDigit <= 9) - { - uiDigit += NATIVE_ZERO; - } - else - { - uiDigit += (NATIVE_LOWER_A - 10); - } - - *pszTmp = (FLMBYTE) uiDigit; - pszTmp--; - iCnt++; - } - - // Skip to end of digits and append ".log" to name - - pszTmp += 9; - f_strcpy( pszTmp, ".log"); - } -} - -/******************************************************************** -Desc: Gets the base RFL file name - does not have directory part. -*********************************************************************/ -void F_Rfl::getBaseRflFileName( - FLMUINT uiFileNum, - char * pszBaseName) -{ - rflGetBaseFileName( m_pFile->FileHdr.uiVersionNum, - m_szDbPrefix, uiFileNum, pszBaseName); -} - -/******************************************************************** -Desc: Generates the full roll forward log file name. Name is based - on the sequence number and the first three characters of the - database if the DB is less than version 4.3. - Otherwise, it is a hex number. -*********************************************************************/ -RCODE F_Rfl::getFullRflFileName( - FLMUINT uiFileNum, - char * pszRflFileName) -{ - RCODE rc = FERR_OK; - char szBaseName[F_FILENAME_SIZE]; - - // Get the directory name. - - f_strcpy( pszRflFileName, m_szRflDir); - - // Get the base RFL file name. - - getBaseRflFileName( uiFileNum, szBaseName); - - // Append the two together. - - if (RC_BAD( rc = f_pathAppend( pszRflFileName, szBaseName))) - { - goto Exit; - } - -Exit: - - return (rc); -} - -/******************************************************************** -Desc: Positions to the offset specified in the RFL file. -*********************************************************************/ -RCODE F_Rfl::positionTo( - FLMUINT uiFileOffset) -{ - RCODE rc = FERR_OK; - FLMUINT uiBytesToRead; - FLMUINT uiBytesRead; - - // Should never be attempting to position to something less than 512 - - // the header is stored in the first 512 bytes. - - flmAssert( uiFileOffset >= 512); - - // If the position is within our current buffer, see if we can adjust - // things without having to go back and re-read the buffer from disk. - - if (m_pCurrentBuf->uiRflBufBytes && - uiFileOffset >= m_pCurrentBuf->uiRflFileOffset && - uiFileOffset <= m_pCurrentBuf->uiRflFileOffset + - m_pCurrentBuf->uiRflBufBytes) - { - - // Whatever is in the buffer beyond uiFileOffset is irrelevant and - // can be discarded. - - m_pCurrentBuf->uiRflBufBytes = uiFileOffset - - m_pCurrentBuf->uiRflFileOffset; - } - else - { - - // Populate the buffer from the 512 byte boundary that is just - // before the offset we are trying to position to. - - uiBytesToRead = MOD_512( uiFileOffset); - m_pCurrentBuf->uiRflFileOffset = ROUND_DOWN_TO_NEAREST_512( uiFileOffset); - m_pCurrentBuf->uiRflBufBytes = MOD_512( uiFileOffset); - if (m_pCurrentBuf->uiRflBufBytes) - { - if (RC_BAD( rc = m_pFileHdl->SectorRead( - m_pCurrentBuf->uiRflFileOffset, - m_pCurrentBuf->uiRflBufBytes, - m_pCurrentBuf->pIOBuffer->m_pucBuffer, &uiBytesRead))) - { - if (rc == FERR_IO_END_OF_FILE) - { - rc = RC_SET( FERR_NOT_RFL); - } - else - { - m_bRflVolumeOk = FALSE; - } - - goto Exit; - } - else if (uiBytesRead < m_pCurrentBuf->uiRflBufBytes) - { - rc = RC_SET( FERR_NOT_RFL); - goto Exit; - } - } - } - -Exit: - - return (rc); -} - -/******************************************************************** -Desc: Get the ACTUAL RFL directory, using as input parameters the - database version, the name of the database, and the - user specified RFL directory. Also return the database - prefix. -*********************************************************************/ -RCODE rflGetDirAndPrefix( - FLMUINT uiDbVersionNum, - const char * pszDbFileName, - const char * pszRflDirIn, - char * pszRflDirOut, - char * pszDbPrefixOut) -{ - RCODE rc = FERR_OK; - char szDbPath[F_PATH_MAX_SIZE]; - char szBaseName[F_FILENAME_SIZE]; - - // Parse the database name into directory and base name - - if (RC_BAD( rc = f_pathReduce( pszDbFileName, szDbPath, szBaseName))) - { - goto Exit; - } - - // Get the base path - - flmGetDbBasePath( pszDbPrefixOut, szBaseName, NULL); - - if (uiDbVersionNum >= FLM_FILE_FORMAT_VER_4_3) - { - - // Determine the RFL directory. If one was specified, it is - // whatever was specified. Otherwise, it is relative to the database - // directory. - - if (pszRflDirIn && *pszRflDirIn) - { - f_strcpy( pszRflDirOut, pszRflDirIn); - } - else - { - f_strcpy( pszRflDirOut, szDbPath); - } - - // For 4.3 and above, the RFL files go in a subdirectory underneath - // the directory where the database is located or the specified - // directory. - - f_strcpy( szBaseName, pszDbPrefixOut); - f_strcat( szBaseName, ".rfl"); - f_pathAppend( pszRflDirOut, szBaseName); - } - else - { - f_strcpy( pszRflDirOut, szDbPath); - } - -Exit: - - return (rc); -} - -/******************************************************************** -Desc: Set the RFL directory. If pszRflDir is NULL or empty string, - the RFL directory is set to the same directory as the - database. -*********************************************************************/ -RCODE F_Rfl::setRflDir( - const char * pszRflDir) -{ - - // Better have set up the FFILE pointer. - - flmAssert( m_pFile != NULL); - - m_bRflDirSameAsDb = (!pszRflDir || !(*pszRflDir)) ? TRUE : FALSE; - - flmAssert( m_pFile->FileHdr.uiVersionNum); - - if (m_pFile->FileHdr.uiVersionNum < FLM_FILE_FORMAT_VER_4_3) - { - - // Don't allow RFL directory to be specified for versions less than - // 4.3 - - pszRflDir = NULL; - m_bRflDirSameAsDb = TRUE; - } - - m_bCreateRflDir = - (m_pFile->FileHdr.uiVersionNum >= FLM_FILE_FORMAT_VER_4_3) - ? TRUE - : FALSE; - return( rflGetDirAndPrefix( - m_pFile->FileHdr.uiVersionNum, m_pFile->pszDbPath, - pszRflDir, m_szRflDir, m_szDbPrefix)); -} - -/******************************************************************** -Desc: Gets an RFL file name - based on DB name and RFL directory. -*********************************************************************/ -RCODE rflGetFileName( - FLMUINT uiDbVersion, - const char * pszDbName, - const char * pszRflDir, - FLMUINT uiFileNum, - char * pszRflFileName) -{ - RCODE rc = FERR_OK; - char szDbPrefix[ F_FILENAME_SIZE]; - char szBaseName[ F_FILENAME_SIZE]; - - // Get the full RFL file name. - - if (RC_BAD( rc = rflGetDirAndPrefix( uiDbVersion, pszDbName, pszRflDir, - pszRflFileName, szDbPrefix))) - { - goto Exit; - } - - rflGetBaseFileName( uiDbVersion, szDbPrefix, uiFileNum, szBaseName); - if (RC_BAD( rc = f_pathAppend( pszRflFileName, szBaseName))) - { - goto Exit; - } - -Exit: - - return (rc); -} - -/******************************************************************** -Desc: Gets an RFL file number from the RFL file name. -*********************************************************************/ -FLMBOOL rflGetFileNum( - FLMUINT uiDbVersion, - const char * pszDbPrefix, - const char * pszRflFileName, - FLMUINT * puiFileNum) -{ - FLMBOOL bGotNum = FALSE; - char szDir[F_PATH_MAX_SIZE]; - char szBaseName[F_FILENAME_SIZE]; - char * pszTmp; - FLMUINT uiCharCnt; - - if (RC_BAD( f_pathReduce( pszRflFileName, szDir, szBaseName))) - { - goto Exit; - } - - // See if it has a .log extension. - - pszTmp = &szBaseName[0]; - while (*pszTmp && *pszTmp != '.') - { - pszTmp++; - } - - // If we do not have a .log extension, it is not a legitimate RFL file. - - if (f_stricmp( pszTmp, ".log") != 0) - { - goto Exit; - } - - // Parse out the name according to the rules for this DB version. - - *pszTmp = 0; // Set period to zero - pszTmp = &szBaseName[0]; - *puiFileNum = 0; - uiCharCnt = 0; - if (uiDbVersion >= FLM_FILE_FORMAT_VER_4_3) - { - - // Name up to the period should be a hex number - - while (*pszTmp) - { - (*puiFileNum) <<= 4; - if (*pszTmp >= NATIVE_ZERO && *pszTmp <= NATIVE_NINE) - { - *puiFileNum += (FLMUINT) (*pszTmp - NATIVE_ZERO); - } - else if (*pszTmp >= NATIVE_LOWER_A && *pszTmp <= NATIVE_LOWER_F) - { - *puiFileNum += ((FLMUINT) (*pszTmp - NATIVE_LOWER_A) + 10); - } - else if (*pszTmp >= NATIVE_UPPER_A && *pszTmp <= NATIVE_UPPER_F) - { - *puiFileNum += ((FLMUINT) (*pszTmp - NATIVE_UPPER_A) + 10); - } - else - { - goto Exit; // Not a hex number - } - - uiCharCnt++; - pszTmp++; - } - - // Better have been exactly 8 hex digits. - - bGotNum = (FLMBOOL) ((uiCharCnt == 8) ? TRUE : FALSE); - } - else - { - FLMUINT uiLen = f_strlen( pszTmp); - FLMUINT uiPrefixLen = f_strlen( pszDbPrefix); - - // Length of base name without the .log extension better be exactly - // 5 more characters than the length of the prefix. - - if (uiLen != uiPrefixLen + 5) - { - flmAssert( 0); - goto Exit; - } - - // Prefix better match. - - while (uiPrefixLen) - { - if (f_toupper( *pszTmp) != f_toupper( *pszDbPrefix)) - { - goto Exit; - } - - uiPrefixLen--; - pszTmp++; - pszDbPrefix++; - } - - // Rest of the name is the five digits that are a base 36 number. - - while (*pszTmp) - { - (*puiFileNum) *= 36; - if (*pszTmp >= NATIVE_ZERO && *pszTmp <= NATIVE_NINE) - { - *puiFileNum += (FLMUINT) (*pszTmp - NATIVE_ZERO); - } - else if (*pszTmp >= NATIVE_LOWER_A && *pszTmp <= NATIVE_LOWER_Z) - { - *puiFileNum += ((FLMUINT) (*pszTmp - NATIVE_LOWER_A) + 10); - } - else if (*pszTmp >= NATIVE_UPPER_A && *pszTmp <= NATIVE_UPPER_Z) - { - *puiFileNum += ((FLMUINT) (*pszTmp - NATIVE_UPPER_A) + 10); - } - else - { - goto Exit; // Not a base 36 number - } - - pszTmp++; - } - - bGotNum = TRUE; - } - -Exit: - - return (bGotNum); -} - -/******************************************************************** -Desc: Sets up the RFL object - associating with a file, etc. -*********************************************************************/ -RCODE F_Rfl::setup( - FFILE * pFile, - const char * pszRflDir) -{ - RCODE rc = FERR_OK; - - // Better not already be associated with an FFILE - - flmAssert( m_pFile == NULL); - m_pFile = pFile; - - // Allocate memory for the RFL buffers - - if (!gv_FlmSysData.bOkToDoAsyncWrites) - { - m_uiRflWriteBufs = 1; - m_uiBufferSize = DEFAULT_RFL_WRITE_BUFFERS * DEFAULT_RFL_BUFFER_SIZE; - } - - if (RC_BAD( rc = f_mutexCreate( &m_hBufMutex))) - { - goto Exit; - } - - if ((m_Buf1.pBufferMgr = f_new F_IOBufferMgr) == NULL) - { - rc = RC_SET( FERR_MEM); - goto Exit; - } - - if ((m_Buf2.pBufferMgr = f_new F_IOBufferMgr) == NULL) - { - rc = RC_SET( FERR_MEM); - goto Exit; - } - - m_Buf1.pBufferMgr->enableKeepBuffer(); - m_Buf1.pBufferMgr->setMaxBuffers( m_uiRflWriteBufs); - m_Buf1.pBufferMgr->setMaxBytes( m_uiRflWriteBufs * m_uiBufferSize); - - if (RC_BAD( rc = m_Buf1.pBufferMgr->getBuffer( &m_Buf1.pIOBuffer, - m_uiBufferSize, m_uiBufferSize))) - { - goto Exit; - } - - m_Buf2.pBufferMgr->enableKeepBuffer(); - m_Buf2.pBufferMgr->setMaxBuffers( m_uiRflWriteBufs); - m_Buf2.pBufferMgr->setMaxBytes( m_uiRflWriteBufs * m_uiBufferSize); - - if (RC_BAD( rc = m_Buf2.pBufferMgr->getBuffer( &m_Buf2.pIOBuffer, - m_uiBufferSize, m_uiBufferSize))) - { - goto Exit; - } - - m_bLoggingOff = FALSE; - m_pCurrentBuf = &m_Buf1; - m_pCurrentBuf->uiRflBufBytes = 0; - - // Set the RFL directory and prefix if necessary. - - if (RC_BAD( rc = setRflDir( pszRflDir))) - { - goto Exit; - } - -Exit: - - return (rc); -} - -/******************************************************************** -Desc: Wait for the writes of a buffer to finish. This routine assumes - that the m_hBufMutex is locked when coming in. It will ALWAYS - unlock the mutex before exiting. -*********************************************************************/ -RCODE F_Rfl::waitForWrites( - RFL_BUFFER * pBuffer, - FLMBOOL bIsWriter) -{ - RCODE rc = FERR_OK; - RCODE TempRc; - RFL_WAITER Waiter; - FLMBOOL bMutexLocked = TRUE; - - // Put self on the wait queue for the buffer. - - Waiter.uiThreadId = f_threadId(); - Waiter.bIsWriter = bIsWriter; - Waiter.hESem = F_SEM_NULL; - if (RC_BAD( rc = f_semCreate( &Waiter.hESem))) - { - goto Exit; - } - - // Note: rc better be changed to success or write error by the process - // that signals us. - - rc = RC_SET( FERR_FAILURE); - Waiter.pRc = &rc; - Waiter.pNext = NULL; - if (pBuffer->pLastWaiter) - { - pBuffer->pLastWaiter->pNext = &Waiter; - } - else - { - pBuffer->pFirstWaiter = &Waiter; - } - - pBuffer->pLastWaiter = &Waiter; - f_mutexUnlock( m_hBufMutex); - bMutexLocked = FALSE; - - // Now just wait to be signaled. - - if (RC_BAD( TempRc = f_semWait( Waiter.hESem, F_SEM_WAITFOREVER))) - { -#ifdef FLM_NLM - EnterDebugger(); -#else - flmAssert( 0); -#endif - rc = TempRc; - } - else - { - - // Process that signaled us better set the rc to something besides - // FERR_FAILURE. - - if (rc == FERR_FAILURE) - { -#ifdef FLM_NLM - EnterDebugger(); -#else - flmAssert( 0); -#endif - } - } - -Exit: - - if (Waiter.hESem != F_SEM_NULL) - { - f_semDestroy( &Waiter.hESem); - } - - if (bMutexLocked) - { - f_mutexUnlock( m_hBufMutex); - } - - return (rc); -} - -/******************************************************************** -Desc: If a commit is in progress, wait for it to finish. -*********************************************************************/ -RCODE F_Rfl::waitForCommit(void) -{ - RCODE rc = FERR_OK; - FLMBOOL bMutexLocked = FALSE; - - // NOTE: If m_pCommitBuf is NULL it cannot be set to something - // non-NULL except by this thread when this thread ends the - // transaction. So, there is no need to lock the mutex and re-check if - // it is NULL. - - if (m_pCommitBuf) - { - f_mutexLock( m_hBufMutex); - bMutexLocked = TRUE; - - // Check m_pCommitBuf again after locking mutex - may have finished. - - if (m_pCommitBuf) - { - - // waitForWrites will unlock the mutex. - - bMutexLocked = FALSE; - rc = waitForWrites( m_pCommitBuf, FALSE); - } - } - - if (bMutexLocked) - { - f_mutexUnlock( m_hBufMutex); - } - - return (rc); -} - -/******************************************************************** -Desc: Write out the header information for an RFL file. -*********************************************************************/ -RCODE F_Rfl::writeHeader( - FLMUINT uiFileNum, - FLMUINT uiEof, - FLMBYTE * pucSerialNum, - FLMBYTE * pucNextSerialNum, - FLMBOOL bKeepSignature) -{ - RCODE rc = FERR_OK; - FLMBYTE ucBuf [512]; - FLMUINT uiBytesWritten; - - flmAssert( m_pFile); - flmAssert( m_pFileHdl); - - f_memset( ucBuf, 0, sizeof(ucBuf)); - f_memcpy( &ucBuf[RFL_NAME_POS], RFL_NAME, RFL_NAME_LEN); - f_memcpy( &ucBuf[RFL_VERSION_POS], RFL_VERSION, RFL_VERSION_LEN); - UD2FBA( (FLMUINT32) uiFileNum, &ucBuf[RFL_FILE_NUMBER_POS]); - UD2FBA( (FLMUINT32) uiEof, &ucBuf[RFL_EOF_POS]); - - if (m_pFile->FileHdr.uiVersionNum >= FLM_FILE_FORMAT_VER_4_3) - { - f_memcpy( &ucBuf[RFL_DB_SERIAL_NUM_POS], - &m_pFile->ucLastCommittedLogHdr[LOG_DB_SERIAL_NUM], - F_SERIAL_NUM_SIZE); - f_memcpy( &ucBuf[RFL_SERIAL_NUM_POS], pucSerialNum, F_SERIAL_NUM_SIZE); - f_memcpy( &ucBuf[RFL_NEXT_FILE_SERIAL_NUM_POS], pucNextSerialNum, - F_SERIAL_NUM_SIZE); - f_strcpy( (char *) &ucBuf[RFL_KEEP_SIGNATURE_POS], - ((bKeepSignature) ? RFL_KEEP_SIGNATURE : RFL_NOKEEP_SIGNATURE)); - } - - // Write out the header - - if (RC_BAD( rc = m_pFileHdl->SectorWrite( 0L, 512, ucBuf, sizeof(ucBuf), - NULL, &uiBytesWritten))) - { - - // Remap disk full error - - if (rc == FERR_IO_DISK_FULL) - { - rc = RC_SET( FERR_RFL_DEVICE_FULL); - m_bRflVolumeFull = TRUE; - } - - m_bRflVolumeOk = FALSE; - goto Exit; - } - - // Flush the file handle to ensure it is forced to disk. - - if (RC_BAD( rc = m_pFileHdl->Flush())) - { - - // Remap disk full error - - if (rc == FERR_IO_DISK_FULL) - { - rc = RC_SET( FERR_RFL_DEVICE_FULL); - m_bRflVolumeFull = TRUE; - } - - m_bRflVolumeOk = FALSE; - goto Exit; - } - -Exit: - - return (rc); -} - -/******************************************************************** -Desc: Verifies the header of an RFL file. -*********************************************************************/ -RCODE F_Rfl::verifyHeader( - FLMBYTE * pucHeader, - FLMUINT uiFileNum, - FLMBYTE * pucSerialNum) -{ - RCODE rc = FERR_OK; - - flmAssert( m_pFile); - - // Check the RFL name and version number - - if (f_memcmp( &pucHeader[RFL_NAME_POS], RFL_NAME, RFL_NAME_LEN) != 0) - { - rc = RC_SET( FERR_NOT_RFL); - goto Exit; - } - - if (f_memcmp( &pucHeader[RFL_VERSION_POS], - RFL_VERSION, RFL_VERSION_LEN) != 0) - { - rc = RC_SET( FERR_NOT_RFL); - goto Exit; - } - - if (m_pFile->FileHdr.uiVersionNum >= FLM_FILE_FORMAT_VER_4_3) - { - - // Verify the database serial number - - if (f_memcmp( &pucHeader[RFL_DB_SERIAL_NUM_POS], - &m_pFile->ucLastCommittedLogHdr[LOG_DB_SERIAL_NUM], - F_SERIAL_NUM_SIZE) != 0) - { - rc = RC_SET( FERR_BAD_RFL_DB_SERIAL_NUM); - goto Exit; - } - - // Verify the serial number that is expected to be on the RFL file. - // If pucSerialNum is NULL, we will not verify it. This is generally - // only done during recovery or restore when we are reading through - // multiple RFL files and we need to verify their serial numbers. - - if (pucSerialNum && - f_memcmp( &pucHeader[RFL_SERIAL_NUM_POS], pucSerialNum, - F_SERIAL_NUM_SIZE) != 0) - { - rc = RC_SET( FERR_BAD_RFL_SERIAL_NUM); - goto Exit; - } - - // Verify the file number. - - if (uiFileNum != (FLMUINT) FB2UD( &pucHeader[RFL_FILE_NUMBER_POS])) - { - rc = RC_SET( FERR_BAD_RFL_FILE_NUMBER); - goto Exit; - } - - // Save serial numbers from the header. - - f_memcpy( m_ucCurrSerialNum, &pucHeader[RFL_SERIAL_NUM_POS], - F_SERIAL_NUM_SIZE); - f_memcpy( m_ucNextSerialNum, &pucHeader[RFL_NEXT_FILE_SERIAL_NUM_POS], - F_SERIAL_NUM_SIZE); - } - - // Save some things from the header. - - m_uiFileEOF = (FLMUINT) FB2UD( &pucHeader[RFL_EOF_POS]); - -Exit: - - return (rc); -} - -/******************************************************************** -Desc: Opens an RFL file. Verifies the serial number for 4.3 dbs. -*********************************************************************/ -RCODE F_Rfl::openFile( - FLMUINT uiFileNum, - FLMBYTE * pucSerialNum) -{ - RCODE rc = FERR_OK; - char szRflFileName [F_PATH_MAX_SIZE]; - FLMBYTE ucBuf [512]; - FLMUINT uiBytesRead; - - flmAssert( m_pFile); - - // If we have a file open and it is not the file number passed in, - // close it. - - if (m_pFileHdl) - { - if (m_pCurrentBuf->uiCurrFileNum != uiFileNum) - { - if (RC_BAD( rc = waitForCommit())) - { - goto Exit; - } - - closeFile(); - } - else - { - goto Exit; // Will return FERR_OK - } - } - else - { - - // Should not be able to be in the middle of a commit if we don't - // have a file open! - - flmAssert( !m_pCommitBuf); - } - - // Generate the log file name. - - if (RC_BAD( rc = getFullRflFileName( uiFileNum, szRflFileName))) - { - goto Exit; - } - - // Open the file. - - if (RC_BAD( rc = gv_FlmSysData.pFileSystem->OpenBlockFile( szRflFileName, - F_IO_RDWR | F_IO_SH_DENYNONE | F_IO_DIRECT, 512, &m_pFileHdl))) - { - goto Exit; - } - - m_pFileHdl->setMaxAutoExtendSize( m_uiRflMaxFileSize); - m_pFileHdl->setExtendSize( m_pFile->uiFileExtendSize); - - // Read the header. - - if (RC_BAD( rc = m_pFileHdl->SectorRead( 0, 512, ucBuf, &uiBytesRead))) - { - if (rc == FERR_IO_END_OF_FILE) - { - rc = RC_SET( FERR_NOT_RFL); - } - else - { - m_bRflVolumeOk = FALSE; - } - - goto Exit; - } - - // If there is not enough data in the buffer, it is not an RFL file. - - if (uiBytesRead < 512) - { - rc = RC_SET( FERR_NOT_RFL); - goto Exit; - } - - // Verify the header information - - if (RC_BAD( rc = verifyHeader( ucBuf, uiFileNum, pucSerialNum))) - { - goto Exit; - } - - m_pCurrentBuf->uiRflBufBytes = 0; - m_pCurrentBuf->uiRflFileOffset = 0; - m_pCurrentBuf->uiCurrFileNum = uiFileNum; - -Exit: - - if (RC_BAD( rc)) - { - waitForCommit(); - closeFile(); - } - - return (rc); -} - -/******************************************************************** -Desc: Creates a new roll forward log file. -*********************************************************************/ -RCODE F_Rfl::createFile( - FLMUINT uiFileNum, - FLMBYTE * pucSerialNum, - FLMBYTE * pucNextSerialNum, - FLMBOOL bKeepSignature) -{ - RCODE rc = FERR_OK; - char szRflFileName [F_PATH_MAX_SIZE]; - - flmAssert( m_pFile); - - // Better not be trying to create the current file - - flmAssert( uiFileNum != m_pCurrentBuf->uiCurrFileNum); - - // If we have a file open close it. - - if (RC_BAD( rc = waitForCommit())) - { - goto Exit; - } - - closeFile(); - - // Generate the log file name. - - if (RC_BAD( rc = getFullRflFileName( uiFileNum, szRflFileName))) - { - goto Exit; - } - - // Delete the file if it already exists - don't care about return code - // here - - (void) gv_FlmSysData.pFileSystem->Delete( szRflFileName); - - // If DB is 4.3 or greater and we are in the same directory as our - // database files, see if we need to create the subdirectory. - // Otherwise, the RFL directory should already have been created. If - // the directory already exists, it is OK - we only try this the first - // time after setRflDir is called - to either verify that the directory - // exists, or if it doesn't, to create it. - - if (m_bCreateRflDir) - { - - // If it already exists, don't attempt to create it. - - if (RC_BAD( rc = gv_FlmSysData.pFileSystem->Exists( m_szRflDir))) - { - if (rc != FERR_IO_PATH_NOT_FOUND && rc != FERR_IO_INVALID_PATH) - { - goto Exit; - } - else - { - if (RC_BAD( rc = gv_FlmSysData.pFileSystem->CreateDir( m_szRflDir))) - { - goto Exit; - } - } - } - - m_bCreateRflDir = FALSE; - } - - // Create the file - - if (RC_BAD( rc = gv_FlmSysData.pFileSystem->CreateBlockFile( szRflFileName, - F_IO_RDWR | F_IO_EXCL | F_IO_SH_DENYNONE | F_IO_DIRECT, 512, - &m_pFileHdl))) - { - goto Exit; - } - - m_pFileHdl->setMaxAutoExtendSize( m_uiRflMaxFileSize); - m_pFileHdl->setExtendSize( m_pFile->uiFileExtendSize); - - // 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; - - // Update the size of the RFL - - if( m_bKeepRflFiles) - { - FLMUINT64 ui64RflDiskUsage; - - if( RC_BAD( rc = flmRflCalcDiskUsage( m_szRflDir, m_szDbPrefix, - m_pFile->FileHdr.uiVersionNum, &ui64RflDiskUsage))) - { - goto Exit; - } - - f_mutexLock( gv_FlmSysData.hShareMutex); - m_pFile->ui64RflDiskUsage = ui64RflDiskUsage; - f_mutexUnlock( gv_FlmSysData.hShareMutex); - } - - -Exit: - - // Close the RFL log file AND delete it if we were not successful. - - if (RC_BAD( rc)) - { - closeFile(); - (void) gv_FlmSysData.pFileSystem->Delete( szRflFileName); - } - - return (rc); -} - -/******************************************************************** -Desc: Copy last partial sector of last buffer written (or to be - written) into a new buffer. -*********************************************************************/ -void F_Rfl::copyLastSector( - RFL_BUFFER * pBuffer, - FLMBYTE * pucOldBuffer, - FLMBYTE * pucNewBuffer, - FLMUINT uiCurrPacketLen, - FLMBOOL bStartingNewFile) -{ - FLMUINT uiOldBufBytes = pBuffer->uiRflBufBytes; - - // If we will be starting a new file, no need to keep any of - // what is in the buffer. Only the current packet needs to - // be copied - at the beginning of the buffer. - - // OTHERWISE: - - // If there are fewer than 512 bytes in the buffer, we simply - // keep them and keep appending to the buffer the next time - // we output stuff. The beginning of the buffer must ALWAYS be - // a 512 byte boundary in the file, because we want to always - // do our writing on 512 byte boundaries - because of direct IO. - - // If the number of bytes in the buffer is over 512 and it is - // evenly divisible by 512, we can clear the buffer. Otherwise, - // we want to move the extra bytes over the last 512 byte boundary - // down to the beginning of the buffer and adjust the buffer bytes - // to reflect just these left-over bytes. - - if (bStartingNewFile) - { - pBuffer->uiRflBufBytes = 0; - pBuffer->uiRflFileOffset = 512; - } - else if (pBuffer->uiRflBufBytes >= 512) - { - - // See if the number of bytes in the buffer is an exact multiple of - // 512. - - if (pBuffer->uiRflBufBytes & 511) // Not exact multiple - { - - // Round m_uiRflBufBytes down to next 512 byte boundary - - FLMUINT ui512Offset = ROUND_DOWN_TO_NEAREST_512( - pBuffer->uiRflBufBytes); - - // Move all bytes above the nearest 512 byte boundary down to - // the beginning of the buffer and adjust pBuffer->uiRflBufBytes - // and pBuffer->uiRflFileOffset accordingly. - - f_memcpy( pucNewBuffer, &pucOldBuffer[ui512Offset], - pBuffer->uiRflBufBytes - ui512Offset); - pBuffer->uiRflBufBytes -= ui512Offset; - pBuffer->uiRflFileOffset += ui512Offset; - } - else - { - pBuffer->uiRflFileOffset += pBuffer->uiRflBufBytes; - pBuffer->uiRflBufBytes = 0; - } - } - else if (pucNewBuffer != pucOldBuffer) - { - f_memcpy( pucNewBuffer, pucOldBuffer, pBuffer->uiRflBufBytes); - } - - if (uiCurrPacketLen) - { - flmAssert( uiOldBufBytes + uiCurrPacketLen <= m_uiBufferSize); - f_memmove( &pucNewBuffer[pBuffer->uiRflBufBytes], - &pucOldBuffer[uiOldBufBytes], uiCurrPacketLen); - } -} - -/******************************************************************** -Desc: Flush the RFL data from the buffer to disk. -*********************************************************************/ -RCODE F_Rfl::flush( - RFL_BUFFER * pBuffer, - FLMBOOL bFinalWrite, - FLMUINT uiCurrPacketLen, - FLMBOOL bStartingNewFile) -{ - RCODE rc = FERR_OK; - FLMUINT uiBytesWritten; - F_IOBuffer * pNewBuffer; - F_IOBuffer * pAsyncBuf = NULL; - FLMBYTE * pucOldBuffer; - FLMUINT uiFileOffset; - FLMUINT uiBufBytes; - - if (m_pFileHdl && pBuffer->uiRflBufBytes) - { - - // Must wait for stuff in committing buffer, if any, before going - // ahead here. - - if (pBuffer != m_pCommitBuf) - { - if (RC_BAD( rc = waitForCommit())) - { - goto Exit; - } - } - - if (m_uiRflWriteBufs > 1 && m_pFileHdl->CanDoAsync()) - { - pAsyncBuf = pBuffer->pIOBuffer; - } - - if ((FLMUINT) (-1) - pBuffer->uiRflFileOffset <= pBuffer->uiRflBufBytes) - { - rc = RC_SET( FERR_DB_FULL); - goto Exit; - } - - pucOldBuffer = pBuffer->pIOBuffer->m_pucBuffer; - uiFileOffset = pBuffer->uiRflFileOffset; - uiBufBytes = pBuffer->uiRflBufBytes; - if (m_uiRflWriteBufs > 1) - { - if (RC_BAD( rc = pBuffer->pBufferMgr->getBuffer( &pNewBuffer, - m_uiBufferSize, m_uiBufferSize))) - { - goto Exit; - } - - // No need to copy data if it is the final write, because it - // won't be reused anyway - the data for the next transaction has - // already been copied to another buffer. - - if (!bFinalWrite) - { - copyLastSector( pBuffer, pucOldBuffer, pNewBuffer->m_pucBuffer, - uiCurrPacketLen, bStartingNewFile); - } - } - - if( RC_OK( rc = m_pFileHdl->SectorWrite( uiFileOffset, uiBufBytes, - pucOldBuffer, - m_uiBufferSize, pAsyncBuf, &uiBytesWritten, - FALSE))) - { - if( m_bKeepRflFiles) - { - f_mutexLock( gv_FlmSysData.hShareMutex); - - if( m_pFile->uiFileExtendSize) - { - FLMUINT uiTmpSize; - - uiTmpSize = (uiFileOffset % m_pFile->uiFileExtendSize) + - (FLMUINT)flmRoundUp( uiBufBytes, - m_pFileHdl->GetSectorSize()); - - if( uiTmpSize > m_pFile->uiFileExtendSize) - { - m_pFile->ui64RflDiskUsage += m_pFile->uiFileExtendSize; - } - } - else - { - m_pFile->ui64RflDiskUsage += uiBytesWritten; - } - - f_mutexUnlock( gv_FlmSysData.hShareMutex); - } - } - - if (m_uiRflWriteBufs == 1) - { - - // We are counting on the fact that the write completed. When we - // only have one buffer, we cannot do async writes. - - flmAssert( !pAsyncBuf); - if (RC_OK( rc) && !bFinalWrite) - { - copyLastSector( pBuffer, pucOldBuffer, pucOldBuffer, - uiCurrPacketLen, bStartingNewFile); - } - - // DO NOT call notifyComplete - that would put - // pBuffer->pIOBuffer into the avail list, and we don't want - // that. We simply want to keep reusing it. - - } - else - { - - // No need to call copyLastSector, because it was called above - // before calling SectorWrite. The part of the old buffer that - // needs to be transferred to the new buffer has already been - // transferred. - - if (!pAsyncBuf) - { - pBuffer->pIOBuffer->notifyComplete( rc); - } - - pBuffer->pIOBuffer = pNewBuffer; - } - - if (RC_BAD( rc)) - { - - // Remap disk full error - - if (rc == FERR_IO_DISK_FULL) - { - rc = RC_SET( FERR_RFL_DEVICE_FULL); - m_bRflVolumeFull = TRUE; - } - - m_bRflVolumeOk = FALSE; - goto Exit; - } - } - -Exit: - - return (rc); -} - -/******************************************************************** -Desc: Switch buffers. This routine assumes the m_hBufMutex is locked. -*********************************************************************/ -void F_Rfl::switchBuffers(void) -{ - RFL_BUFFER * pOldBuffer = m_pCurrentBuf; - - if (m_pCurrentBuf == &m_Buf1) - { - m_pCurrentBuf = &m_Buf2; - } - else - { - m_pCurrentBuf = &m_Buf1; - } - - m_pCurrentBuf->bTransInProgress = pOldBuffer->bTransInProgress; - m_pCurrentBuf->uiCurrFileNum = pOldBuffer->uiCurrFileNum; - m_pCurrentBuf->uiRflBufBytes = pOldBuffer->uiRflBufBytes; - m_pCurrentBuf->uiRflFileOffset = pOldBuffer->uiRflFileOffset; - if (pOldBuffer->uiRflBufBytes) - { - copyLastSector( m_pCurrentBuf, pOldBuffer->pIOBuffer->m_pucBuffer, - m_pCurrentBuf->pIOBuffer->m_pucBuffer, 0, FALSE); - } -} - -/******************************************************************** -Desc: Wait for all RFL transaction writes to be finished. The caller - has the write lock on the database, which will prevent further - writes to the RFL. -*********************************************************************/ -FLMBOOL F_Rfl::seeIfRflWritesDone( - FLMBOOL bForceWait) -{ - FLMBOOL bWritesDone; - - f_mutexLock( m_hBufMutex); - - if (!bForceWait) - { - bWritesDone = (FLMBOOL) ((m_pCurrentBuf->pFirstWaiter || m_pCommitBuf) - ? FALSE - : TRUE); - f_mutexUnlock( m_hBufMutex); - } - else - { - - // If the current buffer has a waiter, add self to that list to - // wait, because it will be notified after the commit buffer has - // been notified. Otherwise, if there is a commit in progress, add - // self to that list to wait. - - if (m_pCurrentBuf->pFirstWaiter) - { - - // If bTransInProgress is TRUE and m_pCommitBuf is NULL then - // this thread is the current transaction, and nobody is going to - // wake up the first waiter until we are done! Hence, we must - // wake him up. - - if (!m_pCommitBuf) - { - - // If m_pCommitBuf is NULL, this could only be possible if - // there is a transaction in progress. Otherwise, there would - // not have been a pFirstWaiter, because when the commit - // buffer finishes writing, if there is a waiter, it will set - // commitbuf=currentbuf if there is no transaction active. - - flmAssert( m_pCurrentBuf->bTransInProgress); - - m_pCommitBuf = m_pCurrentBuf; - switchBuffers(); - wakeUpWaiter( FERR_OK, TRUE); - (void) waitForWrites( m_pCommitBuf, FALSE); - } - else - { - FLMBOOL bSaveTransInProgress = m_pCurrentBuf->bTransInProgress; - - // Must set bTransInProgress to FALSE so that when the writer - // of m_pCommitBuf finishes, it will signal the first waiter - // on m_pCurrentBuf. If we don't do this, m_pCommitBuf will - // simply be set to NULL, and the first waiter will never be - // woke up. - - m_pCurrentBuf->bTransInProgress = FALSE; - (void) waitForWrites( m_pCurrentBuf, FALSE); - - // It is OK to restore the trans in progress flag to what it - // was before, because whoever called this routine has a lock - // on the database, and it is his trans-in-progress state that - // should be preserved. No other thread will have been able to - // change that state because the database is locked. - - f_mutexLock( m_hBufMutex); - m_pCurrentBuf->bTransInProgress = bSaveTransInProgress; - f_mutexUnlock( m_hBufMutex); - } - } - else if (m_pCommitBuf) - { - (void) waitForWrites( m_pCommitBuf, FALSE); - } - else - { - f_mutexUnlock( m_hBufMutex); - } - - bWritesDone = TRUE; - } - - return (bWritesDone); -} - -/******************************************************************** -Desc: Wake up the first thread that is waiting on the commit buffer. -*********************************************************************/ -void F_Rfl::wakeUpWaiter( - RCODE rc, - FLMBOOL bIsWriter // Only used for debug - ) -{ - F_SEM hESem; - -#ifndef FLM_DEBUG - F_UNREFERENCED_PARM( bIsWriter); -#else - if (bIsWriter) - { - flmAssert( m_pCommitBuf->pFirstWaiter->bIsWriter); - } - else - { - flmAssert( !m_pCommitBuf->pFirstWaiter->bIsWriter); - } -#endif - - *(m_pCommitBuf->pFirstWaiter->pRc) = rc; - hESem = m_pCommitBuf->pFirstWaiter->hESem; - if ((m_pCommitBuf->pFirstWaiter = m_pCommitBuf->pFirstWaiter->pNext) == NULL) - { - m_pCommitBuf->pLastWaiter = NULL; - } - - f_semSignal( hESem); -} - -/******************************************************************** -Desc: Wait for the transaction writes to be finished. -*********************************************************************/ -RCODE F_Rfl::completeTransWrites( - FDB * pDb, - FLMBOOL bCommitting, - FLMBOOL bOkToUnlock - ) -{ - RCODE rc = FERR_OK; - RCODE tmpRc; - FLMBOOL bMutexLocked = FALSE; - FLMBOOL bNotifyWaiters = FALSE; - FLMBOOL bDbUnlocked = FALSE; - DB_STATS * pDbStats = NULL; - F_TMSTAMP StartTime; - - f_mutexLock( m_hBufMutex); - bMutexLocked = TRUE; - m_pCurrentBuf->bTransInProgress = FALSE; - - flmAssert( pDb->uiFlags & FDB_HAS_WRITE_LOCK); - - // If we are not logging, we are probably recovering or restoring the - // database. All we need to do in this case is write out the log - // header. - - if (pDb->uiFlags & FDB_REPLAYING_RFL) - { - if (pDb->bHadUpdOper && m_pCurrentBuf->bOkToWriteHdrs) - { - f_mutexUnlock( m_hBufMutex); - bMutexLocked = FALSE; - if (RC_BAD( rc = flmWriteLogHdr( pDb->pDbStats, pDb->pSFileHdl, - pDb->pFile, m_pCurrentBuf->ucLogHdr, m_pCurrentBuf->ucCPHdr, - FALSE))) - { - flmSetMustCloseFlags( pDb->pFile, rc, FALSE); - } - } - - goto Exit; - } - - // Handle empty transactions differently. These transactions should - // not do any writing and do not need to wait for all writes to - // complete, unless the bOkToUnlock flag is set to FALSE. In that case - // they must wait for all writes to complete before unlocking. - - if (!pDb->bHadUpdOper) - { - - // If the current buffer has a waiter, add self to that list to - // wait, because it will be notified after the commit buffer has - // been notified. Otherwise, if there is a commit in progress, add - // self to that list to wait. - - if (m_pCurrentBuf->pFirstWaiter) - { - - // If m_pCommitBuf is NULL then nobody is going to wake up the - // first waiter - we must do it. - - if (!m_pCommitBuf) - { - if (bOkToUnlock) - { - flmUnlinkDbFromTrans( pDb, bCommitting); - bDbUnlocked = TRUE; - } - - m_pCommitBuf = m_pCurrentBuf; - switchBuffers(); - wakeUpWaiter( FERR_OK, TRUE); - - if (!bOkToUnlock) - { - bMutexLocked = FALSE; - (void) waitForWrites( m_pCommitBuf, FALSE); - } - } - else if (!bOkToUnlock) - { - bMutexLocked = FALSE; - (void) waitForWrites( m_pCurrentBuf, FALSE); - } - } - else if (m_pCommitBuf) - { - if (!bOkToUnlock) - { - bMutexLocked = FALSE; - rc = waitForWrites( m_pCommitBuf, FALSE); - } - } - - goto Exit; - } - - // If there is a transaction committing, put self into the wait list - // on the current buffer. When the committer finishes, he will wake up - // the first thread in the list and that thread will commit the buffer. - - if (m_pCommitBuf) - { - FLMBOOL bIsWriter; - - // Another thread has to be doing the writes to m_pCommitBuf, which - // means that m_pCurrentBuf better not be equal to m_pCommitBuf. - - flmAssert( m_pCommitBuf != m_pCurrentBuf); - - // If there are no waiters, we are the first one, so when we get - // signaled, we should proceed and do the write. - - bIsWriter = m_pCurrentBuf->pFirstWaiter ? FALSE : TRUE; - if (bOkToUnlock) - { - flmUnlinkDbFromTrans( pDb, bCommitting); - bDbUnlocked = TRUE; - } - - bMutexLocked = FALSE; - rc = waitForWrites( m_pCurrentBuf, bIsWriter); - - // If we were the first one in the queue, we must now do the write. - - if (!bIsWriter) - { - goto Exit; - } - - // First one in the queue, fall through to do the write. The thread - // that woke me up better have set m_pCommitBuf See below. - - flmAssert( m_pCommitBuf); - } - else if (m_pCurrentBuf->pFirstWaiter) - { - - // Another thread is ready to commit the next set of buffers, but - // just needs to be woke up. - - if (bOkToUnlock) - { - flmUnlinkDbFromTrans( pDb, bCommitting); - bDbUnlocked = TRUE; - } - - // Need to set things up for that first waiter and get him going. - - m_pCommitBuf = m_pCurrentBuf; - switchBuffers(); - wakeUpWaiter( rc, TRUE); - - // Wait for the write to be completed. - - bMutexLocked = FALSE; - rc = waitForWrites( m_pCommitBuf, FALSE); - goto Exit; - } - else - { - m_pCommitBuf = m_pCurrentBuf; - switchBuffers(); - if (bOkToUnlock) - { - flmUnlinkDbFromTrans( pDb, bCommitting); - bDbUnlocked = TRUE; - } - - f_mutexUnlock( m_hBufMutex); - bMutexLocked = FALSE; - } - - // NOTE: From this point on we use tmpRc because we don't want to lose - // the rc that may have been set above in the call to waitForWrites; - // At this point the mutex better not be locked. - flmAssert( !bMutexLocked); - bNotifyWaiters = TRUE; - - if ((pDbStats = pDb->pDbStats) != NULL) - { - f_timeGetTimeStamp( &StartTime); - } - - // Must write out whatever we have in the commit buffer before - // unlocking the database. - - if (RC_BAD( tmpRc = flush( m_pCommitBuf, TRUE))) - { - if (RC_OK( rc)) - { - rc = tmpRc; - } - - goto Exit; - } - - // Wait for any pending IO off of the log buffer - - if (RC_BAD( tmpRc = m_pCommitBuf->pBufferMgr->waitForAllPendingIO())) - { - if (RC_OK( rc)) - { - rc = tmpRc; - } - - goto Exit; - } - - // Force the RFL writes to disk if necessary. NOTE: It is possible for - // m_pFileHdl to be NULL at this point if there were no operations - // actually logged. This happens in FlmDbUpgrade (see flconvrt.cpp). - // Even though nothing was logged the transaction is not an empty - // transaction, because it still needs to write out the log header. - - if (m_pFileHdl) - { - if (RC_BAD( tmpRc = m_pFileHdl->Flush())) - { - - // Remap disk full error - - if (tmpRc == FERR_IO_DISK_FULL) - { - rc = RC_SET( FERR_RFL_DEVICE_FULL); - m_bRflVolumeFull = TRUE; - } - else if (RC_OK( rc)) - { - rc = tmpRc; - } - - m_bRflVolumeOk = FALSE; - goto Exit; - } - } - - // Write the log header - - if (m_pCommitBuf->bOkToWriteHdrs) - { - if (RC_BAD( tmpRc = flmWriteLogHdr( pDb->pDbStats, pDb->pSFileHdl, - pDb->pFile, m_pCommitBuf->ucLogHdr, m_pCommitBuf->ucCPHdr, - FALSE))) - { - if (RC_OK( rc)) - { - rc = tmpRc; - } - - flmSetMustCloseFlags( pDb->pFile, tmpRc, FALSE); - goto Exit; - } - } - -Exit: - - if (!bDbUnlocked && bOkToUnlock) - { - flmUnlinkDbFromTrans( pDb, bCommitting); - } - - if (bNotifyWaiters) - { - FLMUINT uiNumFinished = 1; // For self - - flmAssert( !bMutexLocked); - f_mutexLock( m_hBufMutex); - bMutexLocked = TRUE; - - // Wake up any waiters - - while (m_pCommitBuf->pFirstWaiter) - { - uiNumFinished++; - wakeUpWaiter( rc, FALSE); - } - - // If there are waiters on the current buffer, the first one should - // be woke up so it can start the next set of writes. - - if (m_pCurrentBuf->pFirstWaiter && !m_pCurrentBuf->bTransInProgress) - { - flmAssert( m_pCurrentBuf != m_pCommitBuf); - m_pCommitBuf = m_pCurrentBuf; - switchBuffers(); - wakeUpWaiter( rc, TRUE); - } - else - { - m_pCommitBuf = NULL; - } - - if (pDbStats) - { - flmAddElapTime( &StartTime, - &pDbStats->UpdateTransStats.GroupCompletes.ui64ElapMilli); - pDbStats->UpdateTransStats.GroupCompletes.ui64Count++; - pDbStats->bHaveStats = TRUE; - pDbStats->UpdateTransStats.ui64GroupFinished += uiNumFinished; - } - } - - if (bMutexLocked) - { - f_mutexUnlock( m_hBufMutex); - } - - return (rc); -} - -/******************************************************************** -Desc: Calculate the checksum for a packet. -*********************************************************************/ -FLMBYTE RflCalcChecksum( - const FLMBYTE * pucPacket, - FLMUINT uiPacketBodyLen) -{ - FLMUINT uiBytesToChecksum; - FLMUINT uiChecksum = 0; - FLMBYTE ucTmp; - const FLMBYTE * pucStart; - const FLMBYTE * pucEnd; - const FLMBYTE * pucSectionEnd; - const FLMBYTE * pucCur; - - // Checksum is calculated for every byte in the packet that comes - // after the checksum byte. - - pucStart = &pucPacket[RFL_PACKET_CHECKSUM_OFFSET + 1]; - uiBytesToChecksum = (FLMUINT)(uiPacketBodyLen + - RFL_PACKET_OVERHEAD - - (RFL_PACKET_CHECKSUM_OFFSET + 1)); - - pucCur = pucStart; - pucEnd = pucStart + uiBytesToChecksum; - -#ifdef FLM_64BIT - pucSectionEnd = pucStart + (sizeof( FLMUINT) - ((FLMUINT)pucStart & 0x7)); -#else - pucSectionEnd = pucStart + (sizeof( FLMUINT) - ((FLMUINT)pucStart & 0x3)); -#endif - - if (pucSectionEnd > pucEnd) - { - pucSectionEnd = pucEnd; - } - - while (pucCur < pucSectionEnd) - { - uiChecksum = (uiChecksum << 8) +*pucCur++; - } - -#ifdef FLM_64BIT - pucSectionEnd = (FLMBYTE *)((FLMUINT)pucEnd & 0xFFFFFFFFFFFFFFF8); -#else - pucSectionEnd = (FLMBYTE *)((FLMUINT)pucEnd & 0xFFFFFFFC); -#endif - - while (pucCur < pucSectionEnd) - { - uiChecksum ^= *((FLMUINT *) pucCur); - pucCur += sizeof(FLMUINT); - } - - while (pucCur < pucEnd) - { - uiChecksum ^= *pucCur++; - } - - ucTmp = (FLMBYTE) uiChecksum; - - uiChecksum >>= 8; - ucTmp ^= (FLMBYTE) uiChecksum; - - uiChecksum >>= 8; - ucTmp ^= (FLMBYTE) uiChecksum; - -#ifdef FLM_64BIT - uiChecksum >>= 8; - ucTmp ^= (FLMBYTE)uiChecksum; - - uiChecksum >>= 8; - ucTmp ^= (FLMBYTE)uiChecksum; - - uiChecksum >>= 8; - ucTmp ^= (FLMBYTE)uiChecksum; - - uiChecksum >>= 8; - ucTmp ^= (FLMBYTE)uiChecksum; -#endif - ucTmp ^= (FLMBYTE) (uiChecksum >> 8); - uiChecksum = ucTmp; - - if ((uiChecksum = ucTmp) == 0) - { - uiChecksum = 1; - } - - return ((FLMBYTE) uiChecksum); -} - -/******************************************************************** -Desc: Flush all completed packets out of the RFL buffer, and shift - the new partial packet down. This guarantees that there is - now room in the buffer for the maximum packet size. -*********************************************************************/ -RCODE F_Rfl::shiftPacketsDown( - FLMUINT uiCurrPacketLen, - FLMBOOL bStartingNewFile) -{ - RCODE rc = FERR_OK; - - // The call to flush will move whatever needs to be moved from the - // current buffer into a new buffer if multiple buffers are being used. - // If only one buffer is being used, it will move the part of the - // packet that needs to be moved down to the beginning of the buffer - - // AFTER writing out the buffer. - - if (RC_BAD( rc = flush( m_pCurrentBuf, FALSE, uiCurrPacketLen, - bStartingNewFile))) - { - goto Exit; - } - - // NOTE: If multiple buffers are being used, whatever was moved to the - // new buffer has not yet been written out - - if (bStartingNewFile) - { - if (RC_BAD( rc = waitPendingWrites())) - { - goto Exit; - } - } - -Exit: - - return (rc); -} - -/******************************************************************** -Desc: Determine if we should start a new file. If we are over the - low limit, and the bDoNewIfOverLowLimit flag is set, we - will start a new log file. Or, if this packet size would - put us over the upper limit, we will start a new log file. -*********************************************************************/ -RCODE F_Rfl::seeIfNeedNewFile( - FLMUINT uiPacketLen, - FLMBOOL bDoNewIfOverLowLimit) -{ - RCODE rc = FERR_OK; - FLMBYTE ucNextSerialNum[F_SERIAL_NUM_SIZE]; - - flmAssert( m_pFile); - - // If the keep files flag is FALSE, we won't start a new file. NOTE: - // This should ALWAYS be false for pre 4.3 databases. - - if (!m_bKeepRflFiles) - { - goto Exit; // Should return FERR_OK; - } - - // VERY IMPORTANT NOTE: It is preferrable that we keep transactions - // entirely contained in the same RFL file if at all possible. Note - // that it is NOT a hard and fast requirement. The system will work - // just fine if we don't. However, it would be nice if RFL files always - // ended with a commit or abort packet. This preferences is due to what - // happens after a restore operation. After a restore operation, we - // always need to start a new RFL file, but if possible, we would like - // that new RFL file to be the next one in the sequence after the last - // RFL file that was restored. We can only do this if we were able to - // restore EVERY transaction that was in the last restored RFL file - - // which we can only do if the last restored RFL file ended with a - // commit or abort packet. To accomplish this end, we try to roll to - // new files on the first transaction begin packet that occurs after we - // have exceeded our low threshold - which is why bDoNewIfOverLowLimit - // is only set to TRUE on transaction begin packets. It is set to FALSE - // on other packets so that we will continue logging the transaction in - // the same file that we started the transaction in - if possible. The - // only thing that will cause a non-transaction-begin packet to roll to - // a new file is if we would exceed the high limit. - - if ((bDoNewIfOverLowLimit && - m_pCurrentBuf->uiRflFileOffset + m_pCurrentBuf->uiRflBufBytes >= - m_uiRflMinFileSize) || - (m_pCurrentBuf->uiRflFileOffset + m_pCurrentBuf->uiRflBufBytes + - uiPacketLen >= m_uiRflMaxFileSize)) - { - FLMUINT uiCurrFileEOF = m_pCurrentBuf->uiRflFileOffset + - m_pCurrentBuf->uiRflBufBytes; - - // Shift the current packet to the beginning of the buffer. Any - // packets in the buffer before that one will be written out to the - // current file. - - if (RC_BAD( rc = shiftPacketsDown( uiPacketLen, TRUE))) - { - goto Exit; - } - - // Update the header of the current file and close it. - - if (RC_BAD( rc = writeHeader( m_pCurrentBuf->uiCurrFileNum, uiCurrFileEOF, - m_ucCurrSerialNum, m_ucNextSerialNum, TRUE))) - { - goto Exit; - } - - // Truncate the file. - - if (!ON_512_BYTE_BOUNDARY( uiCurrFileEOF)) - { - uiCurrFileEOF = ROUND_DOWN_TO_NEAREST_512( uiCurrFileEOF) + 512; - } - - if (RC_BAD( rc = m_pFileHdl->Truncate( uiCurrFileEOF))) - { - goto Exit; - } - - // Close the file handle. - - m_pFileHdl->Close(); - m_pFileHdl->Release(); - m_pFileHdl = NULL; - - // Get the next serial number that will be used for the RFL file - // after this one. - - if (RC_BAD( rc = f_createSerialNumber( ucNextSerialNum))) - { - goto Exit; - } - - // Create next file in the sequence. Use the next serial number - // stored in the FDB's log header for the serial number on this RFL - // file. Use the serial number we just generated as the next RFL - // serial number. - - if (RC_BAD( rc = createFile( m_pCurrentBuf->uiCurrFileNum + 1, - m_ucNextSerialNum, ucNextSerialNum, TRUE))) - { - goto Exit; - } - - // Move the next serial number to the current serial number and the - // serial number we generated above into the next serial number. - - f_memcpy( m_ucCurrSerialNum, m_ucNextSerialNum, F_SERIAL_NUM_SIZE); - f_memcpy( m_ucNextSerialNum, ucNextSerialNum, F_SERIAL_NUM_SIZE); - } - -Exit: - - return (rc); -} - -/******************************************************************** -Desc: Finish the current RFL file - set up so that next transaction - will begin a new RFL file. -********************************************************************/ -RCODE F_Rfl::finishCurrFile( - FDB * pDb, - FLMBOOL bNewKeepState) -{ - RCODE rc = FERR_OK; - FLMBOOL bDbLocked = FALSE; - FLMUINT uiTransFileNum; - FLMUINT uiTransOffset; - FLMUINT uiTruncateSize; - FLMBYTE * pucUncommittedLogHdr; - FLMBYTE ucCheckpointLogHdr[ LOG_HEADER_SIZE]; - - // Make sure we don't have a transaction going - - if (pDb->uiTransType != FLM_NO_TRANS) - { - rc = RC_SET( FERR_TRANS_ACTIVE); - goto Exit; - } - - // Make sure there is no active backup running - - f_mutexLock( gv_FlmSysData.hShareMutex); - if (m_pFile->bBackupActive) - { - f_mutexUnlock( gv_FlmSysData.hShareMutex); - rc = RC_SET( FERR_BACKUP_ACTIVE); - goto Exit; - } - - f_mutexUnlock( gv_FlmSysData.hShareMutex); - - // Lock the database - need to prevent update transactions and - // checkpoint thread from running. - - if (RC_BAD( rc = dbLock( pDb, FLM_NO_TIMEOUT))) - { - goto Exit; - } - - bDbLocked = TRUE; - - // Must wait for all RFL writes before switching files. - - (void) seeIfRflWritesDone( TRUE); - - // Better not be in the middle of logging unknown stuff for the - // application - - flmAssert( !m_bLoggingUnknown); - - // Better not be in the middle of a transaction. - - flmAssert( !m_uiCurrTransID); - - // If DB version is less than 4.3 we cannot do this. - - if (m_pFile->FileHdr.uiVersionNum < FLM_FILE_FORMAT_VER_4_3) - { - goto Exit; // Will return FERR_OK - } - - pucUncommittedLogHdr = &m_pFile->ucUncommittedLogHdr[0]; - - // Don't want to copy last committed log header into uncommitted log - // header if bNewKeepState is TRUE because the caller has already done - // it, and has made modifications to the uncommitted log header that we - // don't want to lose. - - if (!bNewKeepState) - { - f_memcpy( pucUncommittedLogHdr, m_pFile->ucLastCommittedLogHdr, - LOG_HEADER_SIZE); - - // If we are in a no-keep state, but we were not told that we have - // a new keep state, we cannot roll to the next RFL file, because a - // checkpoint has not been done. This is not an error - it is just - // the case where FlmDbConfig was asked to roll to the next RFL file - // when the keep flag was still FALSE. - - if (!pucUncommittedLogHdr[LOG_KEEP_RFL_FILES]) - { - goto Exit; // Will return FERR_OK - } - } - - // Get the last committed serial numbers from the file's log header - // buffer. - - f_memcpy( m_ucCurrSerialNum, - &pucUncommittedLogHdr[LOG_LAST_TRANS_RFL_SERIAL_NUM], - F_SERIAL_NUM_SIZE); - - f_memcpy( m_ucNextSerialNum, &pucUncommittedLogHdr[LOG_RFL_NEXT_SERIAL_NUM], - F_SERIAL_NUM_SIZE); - - uiTransFileNum = (FLMUINT) FB2UD( &pucUncommittedLogHdr[LOG_RFL_FILE_NUM]); - uiTransOffset = (FLMUINT) FB2UD( &pucUncommittedLogHdr[LOG_RFL_LAST_TRANS_OFFSET]); - - // If the LOG_RFL_LAST_TRANS_OFFSET is zero, there is no need to go - // set up to go to the next file, because we are already poised to do - // so at the beginning of the next transaction. Just return if this is - // the case. Same for if the file does not exist. - - if (!uiTransOffset) - { - if (!bNewKeepState) - { - goto Exit; // Will return FERR_OK - } - } - else if (RC_BAD( rc = openFile( uiTransFileNum, m_ucCurrSerialNum))) - { - if (rc == FERR_IO_PATH_NOT_FOUND || rc == FERR_IO_INVALID_PATH) - { - rc = FERR_OK; - if (!bNewKeepState) - { - goto Exit; - } - } - else - { - goto Exit; - } - } - else - { - - // At this point, we know the file exists, so we will update its - // header and then update the log header. Note that we use the keep - // RFL state from the last committed log header, not the uncommitted - // log header - because it will contain the correct keep-state for - // the current RFL file. - - if (RC_BAD( rc = writeHeader( m_pCurrentBuf->uiCurrFileNum, uiTransOffset, - m_ucCurrSerialNum, m_ucNextSerialNum, - m_pFile->ucLastCommittedLogHdr [LOG_KEEP_RFL_FILES] - ? TRUE - : FALSE))) - { - goto Exit; - } - - // Truncate the file down to its EOF size - the nearest 512 byte - // boundary. - - uiTruncateSize = uiTransOffset; - if (!ON_512_BYTE_BOUNDARY( uiTruncateSize)) - { - uiTruncateSize = ROUND_DOWN_TO_NEAREST_512( uiTruncateSize) + 512; - } - - if (RC_BAD( rc = m_pFileHdl->Truncate( uiTruncateSize))) - { - goto Exit; - } - - // Close the file handle. - - m_pFileHdl->Close(); - m_pFileHdl->Release(); - m_pFileHdl = NULL; - - // Set things up in the log header to go to the next file when we - // begin the next transaction. NOTE: NO need to lock the mutex, - // because nobody but an update transaction looks at the uncommitted - // log header. - - uiTransFileNum++; - UD2FBA( (FLMUINT32) uiTransFileNum, - &pucUncommittedLogHdr[LOG_RFL_FILE_NUM]); - } - - // Generate a new current serial number if bNewKeepState is TRUE. - // Otherwise, move the next serial number into the current serial - // number. - - if (bNewKeepState) - { - if (RC_BAD( rc = f_createSerialNumber( m_ucCurrSerialNum))) - { - goto Exit; - } - } - else - { - f_memcpy( m_ucCurrSerialNum, m_ucNextSerialNum, F_SERIAL_NUM_SIZE); - } - - // Always generate a new next serial number. - - if (RC_BAD( rc = f_createSerialNumber( m_ucNextSerialNum))) - { - goto Exit; - } - - // Set transaction offset to zero. This will force the next RFL file - // to be created on the next transaction begin. It will be created even - // if it is already there. - - UD2FBA( (FLMUINT32) 0, &pucUncommittedLogHdr[LOG_RFL_LAST_TRANS_OFFSET]); - - f_memcpy( &pucUncommittedLogHdr[LOG_LAST_TRANS_RFL_SERIAL_NUM], - m_ucCurrSerialNum, F_SERIAL_NUM_SIZE); - - f_memcpy( &pucUncommittedLogHdr[LOG_RFL_NEXT_SERIAL_NUM], m_ucNextSerialNum, - F_SERIAL_NUM_SIZE); - - // Set the CP file number and CP offset to point into the new file. - // The outer code (FlmDbConfig) has done a checkpoint and the database - // is still locked. We need to set these values here, otherwise if we - // crash before the next checkpoint, recovery will start in the old RFL - // file, causing an FERR_BAD_RFL_SERIAL_NUM to be returned when - // traversing from the old RFL file to the new RFL file. NOTE: These - // changes must be made to the uncommitted log header AND the CP log - // header (so that they will be written out even though we are not - // forcing a checkpoint). - - if (bNewKeepState) - { -#ifdef FLM_DEBUG - // Do a quick check to see if it looks like we are in a - // checkpointed state - - if (!m_pFile->ucLastCommittedLogHdr[LOG_KEEP_RFL_FILES] && - (FLMUINT) FB2UD( &m_pFile->ucLastCommittedLogHdr[ - LOG_RFL_LAST_TRANS_OFFSET]) > 512) - { - flmAssert( 0); - } -#endif - - f_memcpy( ucCheckpointLogHdr, m_pFile->ucCheckpointLogHdr, - LOG_HEADER_SIZE); - - UD2FBA( (FLMUINT32) uiTransFileNum, - &ucCheckpointLogHdr[LOG_RFL_LAST_CP_FILE_NUM]); - - UD2FBA( (FLMUINT32) uiTransFileNum, - &pucUncommittedLogHdr[LOG_RFL_LAST_CP_FILE_NUM]); - - UD2FBA( (FLMUINT32) 512, &ucCheckpointLogHdr[LOG_RFL_LAST_CP_OFFSET]); - - UD2FBA( (FLMUINT32) 512, &pucUncommittedLogHdr[LOG_RFL_LAST_CP_OFFSET]); - } - - // Write out the log header to disk. - - if (RC_BAD( rc = flmWriteLogHdr( pDb->pDbStats, pDb->pSFileHdl, - m_pFile, pucUncommittedLogHdr, - bNewKeepState - ? ucCheckpointLogHdr - : m_pFile->ucCheckpointLogHdr, FALSE))) - { - goto Exit; - } - - // Copy the uncommitted log header back to the committed log header - // and copy the CP log header (if changed above). - - f_mutexLock( gv_FlmSysData.hShareMutex); - f_memcpy( m_pFile->ucLastCommittedLogHdr, pucUncommittedLogHdr, - LOG_HEADER_SIZE); - - if (bNewKeepState) - { - f_memcpy( m_pFile->ucCheckpointLogHdr, ucCheckpointLogHdr, - LOG_HEADER_SIZE); - } - - f_mutexUnlock( gv_FlmSysData.hShareMutex); - -Exit: - - if (bDbLocked) - { - (void) dbUnlock( pDb); - } - - return (rc); -} - -/******************************************************************** -Desc: Finish packet by outputting header information for it. -*********************************************************************/ -RCODE F_Rfl::finishPacket( - FLMUINT uiPacketType, - FLMUINT uiPacketBodyLen, - FLMBOOL bDoNewIfOverLowLimit) -{ - RCODE rc = FERR_OK; - FLMUINT uiEncryptPacketBodyLen; - FLMUINT uiPacketLen; - FLMBYTE * pucPacket; - - // Encrypt the packet body, if requested. - - uiEncryptPacketBodyLen = getEncryptPacketBodyLen( uiPacketType, - uiPacketBodyLen); - uiPacketLen = uiEncryptPacketBodyLen + RFL_PACKET_OVERHEAD; - - // See if this packet will cause us to overflow the limits on the - // current file. If so, create a new file. - - if (RC_BAD( rc = seeIfNeedNewFile( uiPacketLen, bDoNewIfOverLowLimit))) - { - goto Exit; - } - - // Get a pointer to packet header. - - pucPacket = &(m_pCurrentBuf->pIOBuffer->m_pucBuffer[ - m_pCurrentBuf->uiRflBufBytes]); - - // Set the packet address in the packet header. - - m_uiPacketAddress = m_pCurrentBuf->uiRflFileOffset + - m_pCurrentBuf->uiRflBufBytes; - - UD2FBA( (FLMUINT32) m_uiPacketAddress, &pucPacket[ - RFL_PACKET_ADDRESS_OFFSET]); - - // Set the packet type and packet body length. - - pucPacket[RFL_PACKET_TYPE_OFFSET] = (FLMBYTE) uiPacketType; - - UW2FBA( (FLMUINT16) uiPacketBodyLen, - &pucPacket[RFL_PACKET_BODY_LENGTH_OFFSET]); - - // Set the checksum for the packet. - - pucPacket[RFL_PACKET_CHECKSUM_OFFSET] = - RflCalcChecksum( pucPacket, uiEncryptPacketBodyLen); - - // Increment bytes in the buffer to reflect the fact that this packet - // is now complete. - - m_pCurrentBuf->uiRflBufBytes += uiPacketLen; - -Exit: - - return (rc); -} - -/******************************************************************** -Desc: Truncate roll-forward log file to a certain size - only do if - not keeping RFL files. -*********************************************************************/ -RCODE F_Rfl::truncate( - FLMUINT uiTruncateSize) -{ - RCODE rc = FERR_OK; - FLMUINT uiFileNum; - - flmAssert( uiTruncateSize >= 512); - - // Keeping of log files better not be enabled. - - flmAssert( !m_pFile->ucLastCommittedLogHdr[LOG_KEEP_RFL_FILES]); - - // Better not be in the middle of logging unknown stuff for the - // application - - flmAssert( !m_bLoggingUnknown); - - // Better not be in the middle of a transaction. - - flmAssert( !m_uiCurrTransID); - - // Open the current RFL file. If it does not exist, it is OK - there - // is nothing to truncate. - - uiFileNum = (FLMUINT) FB2UD( &m_pFile->ucLastCommittedLogHdr[ - LOG_RFL_FILE_NUM]); - - if (RC_BAD( rc = openFile( uiFileNum, &m_pFile->ucLastCommittedLogHdr[ - LOG_LAST_TRANS_RFL_SERIAL_NUM]))) - { - if (rc == FERR_IO_PATH_NOT_FOUND || rc == FERR_IO_INVALID_PATH) - { - rc = FERR_OK; - } - - goto Exit; - } - - if (RC_BAD( rc = m_pFileHdl->Truncate( uiTruncateSize))) - { - m_bRflVolumeOk = FALSE; - goto Exit; - } - -Exit: - - return (rc); -} - -/******************************************************************** -Desc: Setup to begin a transaction -*********************************************************************/ -RCODE F_Rfl::setupTransaction(void) -{ - RCODE rc = FERR_OK; - FLMUINT uiFileNum; - FLMUINT uiLastTransOffset; - FLMBOOL bCreateFile; - - f_mutexLock( m_hBufMutex); - m_pCurrentBuf->bTransInProgress = TRUE; - f_mutexUnlock( m_hBufMutex); - - // Get the last committed serial numbers from the file's log header - // buffer. - - f_memcpy( m_ucCurrSerialNum, - &m_pFile->ucLastCommittedLogHdr[LOG_LAST_TRANS_RFL_SERIAL_NUM], - F_SERIAL_NUM_SIZE); - - f_memcpy( m_ucNextSerialNum, - &m_pFile->ucLastCommittedLogHdr[LOG_RFL_NEXT_SERIAL_NUM], - F_SERIAL_NUM_SIZE); - - uiFileNum = (FLMUINT) FB2UD( &m_pFile->ucLastCommittedLogHdr[ - LOG_RFL_FILE_NUM]); - - uiLastTransOffset = (FLMUINT) FB2UD( &m_pFile->ucLastCommittedLogHdr[ - LOG_RFL_LAST_TRANS_OFFSET]); - - // If the LOG_RFL_LAST_TRANS_OFFSET is zero, we need to create the - // next RFL file number no matter what. There are two cases where this - // happens: 1) when the database is first created, and 2) after a - // restore operation. - - if (!uiLastTransOffset) - { - bCreateFile = TRUE; - - // Close the current file, just in case we had opened it before. At - // this point, it doesn't matter because we are going to overwrite - // it. - - if (RC_BAD( rc = waitForCommit())) - { - goto Exit; - } - - closeFile(); - } - else if (RC_BAD( rc = openFile( uiFileNum, m_ucCurrSerialNum))) - { - if (rc != FERR_IO_PATH_NOT_FOUND && rc != FERR_IO_INVALID_PATH) - { - goto Exit; - } - - bCreateFile = TRUE; - } - else - { - bCreateFile = FALSE; - } - - if (bCreateFile) - { - - // If the log header indicates that data has already been logged to - // the file, we need to return the I/O error rather than just - // re-creating the file. This may mean that someone changed the RFL - // directory without moving the RFL files properly. - - if (uiLastTransOffset > 512) - { - rc = RC_SET( FERR_RFL_FILE_NOT_FOUND); - goto Exit; - } - - // Create the RFL file if not found. Use the next serial number - // stored in the FDB's log header for the serial number on this RFL - // file. Use the serial number we just generated as the next RFL - // serial number. - - if (RC_BAD( rc = createFile( uiFileNum, - m_ucCurrSerialNum, m_ucNextSerialNum, - m_pFile->ucLastCommittedLogHdr [LOG_KEEP_RFL_FILES] - ? TRUE - : FALSE))) - { - goto Exit; - } - } - else - { - - // Read in enough of the buffer from the RFL file so that we are - // positioned on a 512 byte boundary. - - if (RC_BAD( positionTo( uiLastTransOffset))) - { - goto Exit; - } - } - - // These can only be changed when starting a transaction. - - if (m_pFile->FileHdr.uiVersionNum >= FLM_FILE_FORMAT_VER_4_3) - { - m_bKeepRflFiles = m_pFile->ucLastCommittedLogHdr[LOG_KEEP_RFL_FILES] - ? TRUE - : FALSE; - - m_uiRflMaxFileSize = (FLMUINT) FB2UD( &m_pFile->ucLastCommittedLogHdr[ - LOG_RFL_MAX_FILE_SIZE]); - - // Round maximum down to nearest 512 boundary. This is necessary - // because we always write a minimum of 512 byte units in direct IO - // mode. If we did not round the maximum down, our last packet could - // end at an offset that is less than the maximum, but greater than - // the nearest 512 byte boundary - technically within the - // user-specified size limit. However, because we always write a - // full 512 bytes of data to fill out the last sector when we are in - // direct IO mode, we would end up with a file that was slightly - // larger than the user-specified limit. The EOF in the header of - // the file would be below the limit, but the actual file size would - // not be. Thus, the need to round down. - - m_uiRflMaxFileSize = ROUND_DOWN_TO_NEAREST_512( m_uiRflMaxFileSize); - - // The maximum cannot go below a certain threshold - must have room - // for least one packet plus the header. - - if (m_uiRflMaxFileSize < RFL_MAX_PACKET_SIZE + 512) - { - m_uiRflMaxFileSize = RFL_MAX_PACKET_SIZE + 512; - } - else if (m_uiRflMaxFileSize > gv_FlmSysData.uiMaxFileSize) - { - m_uiRflMaxFileSize = gv_FlmSysData.uiMaxFileSize; - } - } - else - { - m_bKeepRflFiles = FALSE; - m_uiRflMaxFileSize = gv_FlmSysData.uiMaxFileSize; - } - - m_uiRflMinFileSize = (FLMUINT) FB2UD( &m_pFile->ucLastCommittedLogHdr[ - LOG_RFL_MIN_FILE_SIZE]); - - // Minimum RFL file size should not be allowed to be larger than - // maximum! - - if (m_uiRflMinFileSize > m_uiRflMaxFileSize) - { - m_uiRflMinFileSize = m_uiRflMaxFileSize; - } - - // Set the operation count to zero. - - m_uiOperCount = 0; - - // Set file extend sizes - - m_pFileHdl->setMaxAutoExtendSize( m_uiRflMaxFileSize); - m_pFileHdl->setExtendSize( m_pFile->uiFileExtendSize); - -Exit: - - return (rc); -} - -/******************************************************************** -Desc: Log transaction begin. This routine will also make sure - we have opened an RFL file. - NOTE: The prior version of FLAIM (before 4.3) would log - a time and set the RFL_TIME_LOGGED_FLAG bit in the packet - type. This is no longer done. Old code should be - compatible because it reads the flag. -*********************************************************************/ -RCODE F_Rfl::logBeginTransaction( - FDB * pDb) -{ - RCODE rc = FERR_OK; - FLMUINT uiDbVersion = pDb->pFile->FileHdr.uiVersionNum; - FLMBYTE * pucPacketBody; - FLMUINT uiPacketBodyLen; - FLMUINT uiGMTTime; - - // Do nothing if logging is disabled. - - if (m_bLoggingOff) - { - goto Exit; - } - - // Better not be in the middle of logging unknown stuff for the - // application - - flmAssert( !m_bLoggingUnknown); - - // Better not be in the middle of a transaction. - - flmAssert( !m_uiCurrTransID); - - if (RC_BAD( rc = setupTransaction())) - { - goto Exit; - } - - uiPacketBodyLen = uiDbVersion >= FLM_FILE_FORMAT_VER_4_31 ? 12 : 8; - - // Make sure we have space in the RFL buffer for a complete packet. - - if (!haveBuffSpace( uiPacketBodyLen + RFL_PACKET_OVERHEAD)) - { - if (RC_BAD( rc = flush( m_pCurrentBuf))) - { - goto Exit; - } - } - - // Get a pointer to where we will be laying down the packet body. - - pucPacketBody = getPacketBodyPtr(); - - // Output the transaction ID. - - UD2FBA( (FLMUINT32) pDb->LogHdr.uiCurrTransID, pucPacketBody); - pucPacketBody += 4; - - // This used to be a FLM_GET_TIMER() value in pre-4.3 code, but it was - // never really used. Set it to GMT time now. - - f_timeGetSeconds( &uiGMTTime); - UD2FBA( (FLMUINT32) uiGMTTime, pucPacketBody); - pucPacketBody += 4; - - // NOTE: In the pre-4.3 code the next four bytes would be zero, but - // that is really unnecessary. We will simply no longer set the - // RFL_TIME_LOGGED_FLAG bit in the packet type. Pre-4.3 code should be - // compatible. - - if (uiDbVersion >= FLM_FILE_FORMAT_VER_4_31) - { - FLMUINT uiLastLoggedCommitID; - - uiLastLoggedCommitID = FB2UD( - &m_pFile->ucLastCommittedLogHdr[LOG_LAST_RFL_COMMIT_ID]); - - UD2FBA( (FLMUINT32) uiLastLoggedCommitID, pucPacketBody); - pucPacketBody += 4; - - if (RC_BAD( rc = finishPacket( RFL_TRNS_BEGIN_EX_PACKET, uiPacketBodyLen, - TRUE))) - { - goto Exit; - } - } - else - { - if (RC_BAD( rc = finishPacket( RFL_TRNS_BEGIN_PACKET, uiPacketBodyLen, - TRUE))) - { - goto Exit; - } - } - - // Save the file offset for the start transaction packet. - - m_uiTransStartFile = m_pCurrentBuf->uiCurrFileNum; - m_uiTransStartAddr = m_pCurrentBuf->uiRflFileOffset + - m_pCurrentBuf->uiRflBufBytes - - uiPacketBodyLen - - RFL_PACKET_OVERHEAD; - m_uiCurrTransID = pDb->LogHdr.uiCurrTransID; - -Exit: - - return (rc); -} - -/******************************************************************** -Desc: Flushes the RFL and sets some things in the log header. -*********************************************************************/ -void F_Rfl::finalizeTransaction(void) -{ - FLMUINT uiRflTransEndOffset; - FLMBYTE * pucLogHdr = &m_pFile->ucUncommittedLogHdr[0]; - - // Save the serial numbers and file numbers into the file's - // uncommitted log header. - - UD2FBA( (FLMUINT32) m_pCurrentBuf->uiCurrFileNum, - &pucLogHdr[LOG_RFL_FILE_NUM]); - - uiRflTransEndOffset = getCurrWriteOffset(); - UD2FBA( (FLMUINT32) uiRflTransEndOffset, - &pucLogHdr[LOG_RFL_LAST_TRANS_OFFSET]); - - f_memcpy( &pucLogHdr[LOG_LAST_TRANS_RFL_SERIAL_NUM], m_ucCurrSerialNum, - F_SERIAL_NUM_SIZE); - - f_memcpy( &pucLogHdr[LOG_RFL_NEXT_SERIAL_NUM], m_ucNextSerialNum, - F_SERIAL_NUM_SIZE); -} - -/******************************************************************** -Desc: Handles the commit and abort log operations. If aborting - the transaction, or if the transaction was empty, we will - simply throw away the entire transaction and not bother - to log it. In that case we will reset transaction pointers, - etc. back to the file and offset where the transaction began. - We will also delete RFL files that were created during the - transaction if necessary. NOTE: It is not essential that - the RFL files be deleted. If they are not successfully - deleted, they will be overwritten if need be when creating - new ones. -Note: The prior version of FLAIM (before 4.3) would log - a time and set the RFL_TIME_LOGGED_FLAG bit in the packet - type. This is no longer done. Old code should be - compatible because it reads the flag. -*********************************************************************/ -RCODE F_Rfl::logEndTransaction( - FLMUINT uiPacketType, - FLMBOOL bThrowLogAway, - FLMBOOL * pbLoggedTransEnd) -{ - RCODE rc = FERR_OK; - RCODE rc2 = FERR_OK; - FLMUINT uiLowFileNum; - FLMUINT uiHighFileNum; - char szRflFileName[F_PATH_MAX_SIZE]; - FLMBYTE * pucPacketBody; - FLMUINT uiPacketBodyLen; - - // Better not be in the middle of logging unknown stuff for the - // application - - flmAssert( !m_bLoggingUnknown); - - // Initialize the "logged trans end" flag - - if (pbLoggedTransEnd) - { - *pbLoggedTransEnd = FALSE; - } - - // Do nothing if logging is disabled. - - if (m_bLoggingOff) - { - goto Exit; - } - - flmAssert( m_pFileHdl); - flmAssert( m_pFile); - - // If the transaction had no operations, throw it away - don't even - // log the packet. An abort operation may also elect to throw the log - // away even if there were operations. That is determined by the - // bThrowLogAway flag. The bThrowLogAway flag may be TRUE when doing a - // commit if the caller knows that nothing happened during the - // transction. - - if (bThrowLogAway || !m_uiOperCount) - { -Throw_Away_Transaction: - - // If we have switched files, delete all but the file we started in. - - if (m_pCurrentBuf->uiCurrFileNum != m_uiTransStartFile) - { - flmAssert( m_pCurrentBuf->uiCurrFileNum > m_uiTransStartFile); - - // File number in uncommitted log header better not have been - // changed yet. It is only supposed to be changed when the - // transaction finishes - i.e., in this routine. Up until this - // point, it should only be changed in - // m_pCurrentBuf->uiCurrFileNum. - - flmAssert( m_uiTransStartFile == (FLMUINT) FB2UD( - &m_pFile->ucUncommittedLogHdr[LOG_RFL_FILE_NUM])); - - uiLowFileNum = m_uiTransStartFile + 1; - uiHighFileNum = m_pCurrentBuf->uiCurrFileNum; - - // Close the current file so it can be deleted. - - if (RC_BAD( rc = waitForCommit())) - { - goto Exit; - } - - closeFile(); - - // Delete as many of the files as possible. Don't worry about - // errors here. - - while (uiLowFileNum <= uiHighFileNum) - { - if (RC_OK( getFullRflFileName( uiLowFileNum, szRflFileName))) - { - (void) gv_FlmSysData.pFileSystem->Delete( szRflFileName); - } - - uiLowFileNum++; - } - } - else - { - - // If we are in the file the transaction started in, simply - // reset to where the transaction started. - - if (RC_BAD( rc2 = positionTo( m_uiTransStartAddr))) - { - - // If we got to this point because of a "goto - // Throw_Away_Transaction", we don't want to clobber the - // original error code. So, we use rc2 temporarily and then - // determine if its value should be set into rc. - - if (RC_OK( rc)) - { - rc = rc2; - } - - rc2 = FERR_OK; - goto Exit; - } - } - } - else - { - - // Log a commit or abort packet. - - uiPacketBodyLen = 8; - - // Make sure we have space in the RFL buffer for a complete packet. - - if (!haveBuffSpace( uiPacketBodyLen + RFL_PACKET_OVERHEAD)) - { - if (RC_BAD( rc = flush( m_pCurrentBuf))) - { - goto Throw_Away_Transaction; - } - } - - // Get a pointer to where we will be laying down the packet body. - - pucPacketBody = getPacketBodyPtr(); - - // Output the transaction ID. - - UD2FBA( (FLMUINT32) m_uiCurrTransID, pucPacketBody); - pucPacketBody += 4; - UD2FBA( (FLMUINT32) m_uiTransStartAddr, pucPacketBody); - pucPacketBody += 4; - if (RC_BAD( rc = finishPacket( uiPacketType, uiPacketBodyLen, FALSE))) - { - goto Throw_Away_Transaction; - } - - finalizeTransaction(); - - if (pbLoggedTransEnd) - { - *pbLoggedTransEnd = TRUE; - } - } - -Exit: - - if (!m_bLoggingOff) - { - m_uiCurrTransID = 0; - } - - return (RC_BAD( rc) ? rc : rc2); -} - -/******************************************************************** -Desc: Log add, modify, delete, and reserve DRN packets -*********************************************************************/ -RCODE F_Rfl::logUpdatePacket( - FLMUINT uiPacketType, - FLMUINT uiContainer, - FLMUINT uiDrn, - FLMUINT uiAutoTrans) -{ - RCODE rc = FERR_OK; - FLMBYTE * pucPacketBody; - FLMUINT uiPacketBodyLen; - - // Do nothing if logging is disabled. - - if (m_bLoggingOff) - { - goto Exit; - } - - // Better not be in the middle of logging unknown stuff for the - // application - - flmAssert( !m_bLoggingUnknown); - - // Better be in the middle of a transaction. - - flmAssert( m_uiCurrTransID); - - m_uiOperCount++; - - if (uiPacketType == RFL_ADD_RECORD_PACKET_VER_2 || - uiPacketType == RFL_MODIFY_RECORD_PACKET_VER_2 || - uiPacketType == RFL_DELETE_RECORD_PACKET_VER_2) - { - uiPacketBodyLen = 11; - } - else - { - uiPacketBodyLen = 10; - } - - // Make sure we have space in the RFL buffer for a complete packet. - - if (!haveBuffSpace( uiPacketBodyLen + RFL_PACKET_OVERHEAD)) - { - if (RC_BAD( rc = flush( m_pCurrentBuf))) - { - goto Exit; - } - } - - // Get a pointer to where we will be laying down the packet body. - - pucPacketBody = getPacketBodyPtr(); - - // Output the transaction ID. - - UD2FBA( (FLMUINT32) m_uiCurrTransID, pucPacketBody); - pucPacketBody += 4; - - // Output the container number. - - UW2FBA( (FLMUINT16) uiContainer, pucPacketBody); - pucPacketBody += 2; - - // Output the DRN. - - UD2FBA( (FLMUINT32) uiDrn, pucPacketBody); - pucPacketBody += 4; - - // Output the flags - - if (uiPacketType == RFL_ADD_RECORD_PACKET_VER_2 || - uiPacketType == RFL_MODIFY_RECORD_PACKET_VER_2 || - uiPacketType == RFL_DELETE_RECORD_PACKET_VER_2) - { - FLMUINT uiFlags = 0; - - // For now, these are the only flags we log - - if (uiAutoTrans & FLM_DO_IN_BACKGROUND) - { - uiFlags |= RFL_UPDATE_BACKGROUND; - } - - if (uiAutoTrans & FLM_SUSPENDED) - { - uiFlags |= RFL_UPDATE_SUSPENDED; - } - - *pucPacketBody++ = (FLMBYTE) uiFlags; - } - - // Finish the packet - - if (RC_BAD( rc = finishPacket( uiPacketType, uiPacketBodyLen, FALSE))) - { - goto Exit; - } - -Exit: - - return (rc); -} - -/******************************************************************** -Desc: Log index suspend and resume packets -*********************************************************************/ -RCODE F_Rfl::logIndexSuspendOrResume( - FLMUINT uiIndexNum, - FLMUINT uiPacketType) -{ - RCODE rc = FERR_OK; - FLMBYTE * pucPacketBody; - FLMUINT uiPacketBodyLen; - - // This call is new with 4.51 databases - not supported in older - // versions, so don't log it. - - if (m_pFile->FileHdr.uiVersionNum < FLM_FILE_FORMAT_VER_4_51) - { - goto Exit; - } - - // Do nothing if logging is disabled. - - if (m_bLoggingOff) - { - goto Exit; - } - - // Better not be in the middle of logging unknown stuff for the - // application - - flmAssert( !m_bLoggingUnknown); - - // Better be in the middle of a transaction. - - flmAssert( m_uiCurrTransID); - - m_uiOperCount++; - uiPacketBodyLen = 6; - - // Make sure we have space in the RFL buffer for a complete packet. - - if (!haveBuffSpace( uiPacketBodyLen + RFL_PACKET_OVERHEAD)) - { - if (RC_BAD( rc = flush( m_pCurrentBuf))) - { - goto Exit; - } - } - - // Get a pointer to where we will be laying down the packet body. - - pucPacketBody = getPacketBodyPtr(); - - // Output the transaction ID. - - UD2FBA( (FLMUINT32) m_uiCurrTransID, pucPacketBody); - pucPacketBody += 4; - - // Output the index number. - - UW2FBA( (FLMUINT16) uiIndexNum, pucPacketBody); - pucPacketBody += 2; - - // Finish the packet - - if (RC_BAD( rc = finishPacket( uiPacketType, uiPacketBodyLen, FALSE))) - { - goto Exit; - } - -Exit: - - return (rc); -} - -/******************************************************************** -Desc: -*********************************************************************/ -RCODE F_Rfl::logSizeEventConfig( - FLMUINT uiTransID, - FLMUINT uiSizeThreshold, - FLMUINT uiSecondsBetweenEvents, - FLMUINT uiBytesBetweenEvents) -{ - RCODE rc = FERR_OK; - FLMBYTE * pucPacketBody; - FLMUINT uiPacketBodyLen; - - // Don't log the operation if it isn't supported by the current database - // version - - if (m_pFile->FileHdr.uiVersionNum < FLM_FILE_FORMAT_VER_4_61) - { - goto Exit; - } - - // Do nothing if logging is disabled. - - if (m_bLoggingOff) - { - goto Exit; - } - - // Better not be in the middle of logging unknown stuff for the - // application - - flmAssert( !m_bLoggingUnknown); - - // We need to set up to log this packet as if we were logging a - // transaction. The only difference is that we don't log the begin - // transaction packet. - - if (RC_BAD( rc = setupTransaction())) - { - goto Exit; - } - - uiPacketBodyLen = 16; - - // Make sure we have space in the RFL buffer for a complete packet. - - if( !haveBuffSpace( uiPacketBodyLen + RFL_PACKET_OVERHEAD)) - { - if (RC_BAD( rc = flush( m_pCurrentBuf))) - { - goto Exit; - } - } - - // Get a pointer to where we will be laying down the packet body. - - pucPacketBody = getPacketBodyPtr(); - - // Output the transaction ID. - - UD2FBA( (FLMUINT32) uiTransID, pucPacketBody); - pucPacketBody += 4; - - // Output the size threshold - - UD2FBA( (FLMUINT32) uiSizeThreshold, pucPacketBody); - pucPacketBody += 4; - - // Output the time frequency - - UD2FBA( (FLMUINT32) uiSecondsBetweenEvents, pucPacketBody); - pucPacketBody += 4; - - // Output the size frequency - - UD2FBA( (FLMUINT32) uiBytesBetweenEvents, pucPacketBody); - pucPacketBody += 4; - - // Finish the packet - - if (RC_BAD( rc = finishPacket( RFL_CONFIG_SIZE_EVENT_PACKET, - uiPacketBodyLen, TRUE))) - { - goto Exit; - } - -Exit: - - return (rc); -} - -/******************************************************************** -Desc: Make room in the RFL buffer for the additional bytes. - This is done by flushing the log buffer and shifting down - the bytes already used in the current packet. If that doesn't - make room, the current packet will be finished and a new one - started. -*********************************************************************/ -RCODE F_Rfl::makeRoom( - FLMUINT uiAdditionalBytesNeeded, - FLMUINT * puiCurrPacketLenRV, - FLMUINT uiPacketType, - FLMUINT * puiBytesAvailableRV, - FLMUINT * puiPacketCountRV) -{ - RCODE rc = FERR_OK; - FLMUINT uiBytesNeeded; - - // Must account for encryption, so round bytes needed to nearest four - // byte boundary. - - uiBytesNeeded = *puiCurrPacketLenRV + uiAdditionalBytesNeeded; - if (uiBytesNeeded & 0x3) - { - uiBytesNeeded += (4 - (uiBytesNeeded & 0x3)); - } - - if (uiBytesNeeded <= (FLMUINT) RFL_MAX_PACKET_SIZE) - { - FLMUINT uiTmp = uiBytesNeeded; - - if (haveBuffSpace( uiTmp)) - { - if (puiBytesAvailableRV) - { - *puiBytesAvailableRV = uiAdditionalBytesNeeded; - } - } - else - { - - // Bytes requested will fit into a packet, but not the buffer, - // so we need to shift the packets in the buffer down. The - // shiftPacketsDown guarantees that there is room in the buffer - // for a full size packet. - - if (RC_BAD( rc = shiftPacketsDown( *puiCurrPacketLenRV, FALSE))) - { - goto Exit; - } - - // If a non-NULL puwBytesAvailableRV is passed in it means that - // we are to return the number of bytes that we can actually - // output. Since we know there is enough for the bytes needed, we - // simply return the number of bytes that were requested. - - if (puiBytesAvailableRV) - { - *puiBytesAvailableRV = uiAdditionalBytesNeeded; - } - } - } - else // (uiBytesNeeded > RFL_MAX_PACKET_SIZE) - { - - // This is the case where the bytes needed would overflow the - // maximum packet size. If puwBytesAvailableRV is NULL, it means - // that all of the requested additional bytes must fit into the - // packet. In that case, since the requested bytes would put us over - // the packet size limit, we must finish the current packet and then - // flush the packets out of the buffer so we can start a new packet. - - if (!puiBytesAvailableRV) - { - - // Finish the current packet and start a new one. - - if (puiPacketCountRV) - { - (*puiPacketCountRV)++; - } - - if (RC_BAD( rc = finishPacket( uiPacketType, - *puiCurrPacketLenRV - RFL_PACKET_OVERHEAD, FALSE))) - { - goto Exit; - } - - if (RC_BAD( rc = flush( m_pCurrentBuf))) - { - goto Exit; - } - - *puiCurrPacketLenRV = RFL_PACKET_OVERHEAD; - } - else - { - - // When puiBytesAvailableRV is non-NULL, it means we can fill up - // the rest of the packet with part of the bytes. In this case we - // return the number of bytes available and then shift the - // packets down in the buffer to make sure there is room for a - // full-size packet. - - *puiBytesAvailableRV = RFL_MAX_PACKET_SIZE -*puiCurrPacketLenRV; - if (RC_BAD( rc = shiftPacketsDown( *puiCurrPacketLenRV, FALSE))) - { - goto Exit; - } - } - } - -Exit: - - return (rc); -} - -/******************************************************************** -Desc: Log a chunk of data to the RFL log - typically used to log - field data. Will spill over into multiple packets if - necessary. -*********************************************************************/ -RCODE F_Rfl::logData( - FLMUINT uiDataLen, - const FLMBYTE * pucData, - FLMUINT uiPacketType, - FLMUINT * puiPacketLenRV, - FLMUINT * puiPacketCountRV, - FLMUINT * puiMaxLogBytesNeededRV, - FLMUINT * puiTotalBytesLoggedRV) -{ - RCODE rc = FERR_OK; - FLMUINT uiBytesAvail; - FLMBYTE * pucDest; - - while (uiDataLen) - { - if (RC_BAD( rc = makeRoom( uiDataLen, puiPacketLenRV, uiPacketType, - &uiBytesAvail, puiPacketCountRV))) - { - goto Exit; - } - - if (uiBytesAvail) - { - if (puiMaxLogBytesNeededRV) - { - if (RC_BAD( rc = RflCheckMaxLogged( puiMaxLogBytesNeededRV, - *puiPacketCountRV, puiTotalBytesLoggedRV, uiBytesAvail))) - { - goto Exit; - } - } - - pucDest = getPacketPtr() + (*puiPacketLenRV); - f_memcpy( pucDest, pucData, uiBytesAvail); - uiDataLen -= uiBytesAvail; - pucData += uiBytesAvail; - (*puiPacketLenRV) += uiBytesAvail; - } - - // If we didn't get all of the data into the RFL buffer, finish and - // flush the current packet. - - if (uiDataLen) - { - if (puiPacketCountRV) - { - (*puiPacketCountRV)++; - } - - if (RC_BAD( rc = finishPacket( uiPacketType, - *puiPacketLenRV - RFL_PACKET_OVERHEAD, FALSE))) - { - goto Exit; - } - - if (RC_BAD( rc = flush( m_pCurrentBuf))) - { - goto Exit; - } - - *puiPacketLenRV = RFL_PACKET_OVERHEAD; - if (puiMaxLogBytesNeededRV) - { - if (RC_BAD( rc = RflCheckMaxLogged( puiMaxLogBytesNeededRV, - *puiPacketCountRV, puiTotalBytesLoggedRV, - RFL_PACKET_OVERHEAD))) - { - goto Exit; - } - } - } - } - -Exit: - - return (rc); -} - -/******************************************************************** -Desc: Check to see if by logging the requested number of bytes we - will end up exceeding the maximum bytes needed. If so, and - we have not yet actually logged a packet, return - FERR_FAILURE so that we will discard this packet that is - being built. If we have already logged a packet, it is - too late to discard what has been done. -*********************************************************************/ -FSTATIC RCODE RflCheckMaxLogged( - FLMUINT * puiMaxBytesNeededRV, - FLMUINT uiPacketsLogged, - FLMUINT * puiCurrTotalLoggedRV, - FLMUINT uiBytesToLog) -{ - RCODE rc = FERR_OK; - - *puiCurrTotalLoggedRV += uiBytesToLog; - - if ((!uiPacketsLogged) && (*puiCurrTotalLoggedRV > *puiMaxBytesNeededRV)) - { - rc = RC_SET( FERR_FAILURE); - goto Exit; - } - -Exit: - - return (rc); -} - -/******************************************************************** -Desc: Callback function that captures the changes being logged by - the call to flmRecordDifference. -*********************************************************************/ -FSTATIC void RflChangeCallback( - GRD_DifferenceData& DiffData, - void * CallbackData) -{ - RFL_CHANGE_DATA * pRflChangeData = (RFL_CHANGE_DATA*) CallbackData; - F_Rfl * pRfl = pRflChangeData->pRfl; - void * pvField; - const FLMBYTE * pucExportPtr; - FLMBYTE * pucTmp; - FLMUINT uiOverhead = 0; - FLMUINT uiBytesToLog; - FLMUINT uiPos; - FLMUINT uiTagNum; - FLMUINT uiDataLen = 0; - FLMBOOL bEncrypted = FALSE; - FLMUINT uiEncId; - FLMBYTE ucChangeType = 0; - - // If we had an error before this callback, do nothing. - - if (RC_BAD( pRflChangeData->rc)) - { - goto Exit; - } - - switch (DiffData.type) - { - case GRD_Inserted: - { - bEncrypted = DiffData.pAfterRecord->isEncryptedField( DiffData.pvAfterField); - uiDataLen = DiffData.pAfterRecord->getDataLength( DiffData.pvAfterField); - if (pRflChangeData->uiVersionNum < FLM_FILE_FORMAT_VER_4_61) - { - if (bEncrypted) - { - uiOverhead = 13; - ucChangeType = RFL_INSERT_ENC_FIELD; - } - else - { - uiOverhead = 9; - ucChangeType = RFL_INSERT_FIELD; - } - } - else - { - if (bEncrypted) - { - uiOverhead = 17; - ucChangeType = RFL_INSERT_ENC_LARGE_FIELD; - } - else - { - uiOverhead = 11; - ucChangeType = RFL_INSERT_LARGE_FIELD; - } - } - break; - } - - case GRD_Deleted: - { - - // Ignore these for versions of the database >= 4.60 - - if (pRflChangeData->uiVersionNum >= FLM_FILE_FORMAT_VER_4_60) - { - goto Exit; - } - - uiOverhead = 3; - break; - } - - case GRD_DeletedSubtree: - { - - // Ignore these for versions of the database < 4.60 - - if (pRflChangeData->uiVersionNum < FLM_FILE_FORMAT_VER_4_60) - { - goto Exit; - } - - uiOverhead = 3; - break; - } - - case GRD_Modified: - { - bEncrypted = DiffData.pAfterRecord->isEncryptedField( DiffData.pvAfterField); - uiDataLen = DiffData.pAfterRecord->getDataLength( DiffData.pvAfterField); - if (pRflChangeData->uiVersionNum < FLM_FILE_FORMAT_VER_4_61) - { - if (bEncrypted) - { - uiOverhead = 10; - ucChangeType = RFL_MODIFY_ENC_FIELD; - } - else - { - uiOverhead = 6; - ucChangeType = RFL_MODIFY_FIELD; - } - } - else - { - if (bEncrypted) - { - uiOverhead = 14; - ucChangeType = RFL_MODIFY_ENC_LARGE_FIELD; - } - else - { - uiOverhead = 8; - ucChangeType = RFL_MODIFY_LARGE_FIELD; - } - } - break; - } - - default: - { - flmAssert( 0); - break; - } - } - - // Determine the number of bytes that will actually be logged with - // this overhead. If it won't fit in the current packet, we will have - // to create a new packet - hence, we add RFL_PACKET_OVERHEAD to the - // amount that will be logged. - - uiBytesToLog = uiOverhead; - if (RFL_MAX_PACKET_SIZE - uiOverhead < pRflChangeData->uiCurrPacketLen) - { - uiBytesToLog += RFL_PACKET_OVERHEAD; - } - - // See if the bytes we are going log will exceed the maximum bytes - // needed. - - if (RC_BAD( pRflChangeData->rc = RflCheckMaxLogged( - &pRflChangeData->uiMaxLogBytesNeeded, - pRflChangeData->uiPacketCount, - &pRflChangeData->uiTotalBytesLogged, uiBytesToLog))) - { - goto Exit; - } - - // Make room to log the overhead - - if (RC_BAD( pRflChangeData->rc = pRfl->makeRoom( uiOverhead, - &pRflChangeData->uiCurrPacketLen, RFL_CHANGE_FIELDS_PACKET, NULL, - &pRflChangeData->uiPacketCount))) - { - goto Exit; - } - - pucTmp = pRfl->getPacketPtr() + pRflChangeData->uiCurrPacketLen; - uiPos = DiffData.uiAbsolutePosition; - UW2FBA( (FLMUINT16) uiPos, &pucTmp[1]); - pRflChangeData->uiCurrPacketLen += uiOverhead; - pvField = DiffData.pvAfterField; - - switch (DiffData.type) - { - case GRD_Inserted: - { - *pucTmp = ucChangeType; - pucTmp += 3; - uiTagNum = DiffData.pAfterRecord->getFieldID( pvField); - UW2FBA( (FLMUINT16) uiTagNum, pucTmp); - pucTmp += 2; - *pucTmp++ = (FLMBYTE) DiffData.pAfterRecord->getDataType( pvField); - *pucTmp++ = (FLMBYTE) DiffData.pAfterRecord->getLevel( pvField); - if (pRflChangeData->uiVersionNum < FLM_FILE_FORMAT_VER_4_61) - { - UW2FBA( (FLMUINT16)uiDataLen, pucTmp); - pucTmp += 2; - } - else - { - UD2FBA( (FLMUINT32)uiDataLen, pucTmp); - pucTmp += 4; - } - - if (bEncrypted) - { - uiEncId = DiffData.pAfterRecord->getEncryptionID( pvField); - flmAssert( uiEncId); - UW2FBA( (FLMUINT16) uiEncId, pucTmp); - pucTmp += 2; - - uiDataLen = DiffData.pAfterRecord->getEncryptedDataLength( pvField); - if (pRflChangeData->uiVersionNum < FLM_FILE_FORMAT_VER_4_61) - { - UW2FBA( (FLMUINT16)uiDataLen, pucTmp); - pucTmp += 2; - } - else - { - UD2FBA( (FLMUINT32)uiDataLen, pucTmp); - pucTmp += 4; - } - } - - // Log the data, if any. - - if (uiDataLen) - { - if (bEncrypted) - { - pucExportPtr = DiffData.pAfterRecord->getEncryptionDataPtr( pvField); - } - else - { - pucExportPtr = DiffData.pAfterRecord->getDataPtr( pvField); - } - - if (!pucExportPtr) - { - pRflChangeData->rc = RC_SET( FERR_MEM); - goto Exit; - } - - if (RC_BAD( pRflChangeData->rc = pRfl->logData( uiDataLen, - pucExportPtr, RFL_CHANGE_FIELDS_PACKET, - &pRflChangeData->uiCurrPacketLen, - &pRflChangeData->uiPacketCount, - &pRflChangeData->uiMaxLogBytesNeeded, - &pRflChangeData->uiTotalBytesLogged))) - { - goto Exit; - } - } - - break; - } - - case GRD_Deleted: - case GRD_DeletedSubtree: - { - *pucTmp = RFL_DELETE_FIELD; - break; - } - - case GRD_Modified: - { - *pucTmp = ucChangeType; - pucTmp += 3; - - // For now, just log the new bytes using RFL_REPLACE_BYTES option - - *pucTmp++ = RFL_REPLACE_BYTES; - uiDataLen = DiffData.pAfterRecord->getDataLength( pvField); - if (pRflChangeData->uiVersionNum < FLM_FILE_FORMAT_VER_4_61) - { - UW2FBA( (FLMUINT16)uiDataLen, pucTmp); - pucTmp += 2; - } - else - { - UD2FBA( (FLMUINT32)uiDataLen, pucTmp); - pucTmp += 4; - } - - if (bEncrypted) - { - uiEncId = DiffData.pAfterRecord->getEncryptionID( pvField); - flmAssert( uiEncId); - UW2FBA( (FLMUINT16) uiEncId, pucTmp); - pucTmp += 2; - - uiDataLen = DiffData.pAfterRecord->getEncryptedDataLength( pvField); - if (pRflChangeData->uiVersionNum < FLM_FILE_FORMAT_VER_4_61) - { - UW2FBA( (FLMUINT16)uiDataLen, pucTmp); - pucTmp += 2; - } - else - { - UD2FBA( (FLMUINT32)uiDataLen, pucTmp); - pucTmp += 4; - } - } - - // Log the data, if any. - - if (uiDataLen) - { - if (bEncrypted) - { - pucExportPtr = DiffData.pAfterRecord->getEncryptionDataPtr( pvField); - } - else - { - pucExportPtr = DiffData.pAfterRecord->getDataPtr( pvField); - } - - if (pucExportPtr == NULL) - { - pRflChangeData->rc = RC_SET( FERR_MEM); - goto Exit; - } - - if (RC_BAD( pRflChangeData->rc = pRfl->logData( uiDataLen, - pucExportPtr, RFL_CHANGE_FIELDS_PACKET, - &pRflChangeData->uiCurrPacketLen, - &pRflChangeData->uiPacketCount, - &pRflChangeData->uiMaxLogBytesNeeded, - &pRflChangeData->uiTotalBytesLogged))) - { - goto Exit; - } - } - - break; - } - - default: - { - flmAssert( 0); - break; - } - } - -Exit: - - return; -} - -/******************************************************************** -Desc: Log change fields for a record modify operation. -*********************************************************************/ -RCODE F_Rfl::logChangeFields( - FlmRecord * pOldRecord, - FlmRecord * pNewRecord) -{ - RFL_CHANGE_DATA RflChangeData; - FLMUINT uiTmpBodyLen; - FLMUINT uiDataLen; - void * pvNewField; - FLMBOOL bEncrypted; - FLMUINT uiOverhead; - - RflChangeData.rc = FERR_OK; - RflChangeData.pRfl = this; - RflChangeData.uiVersionNum = m_pFile->FileHdr.uiVersionNum; - - // Determine the total amount that would have to be logged if we just - // logged the new record. - - RflChangeData.uiMaxLogBytesNeeded = RFL_PACKET_OVERHEAD; - uiTmpBodyLen = 0; - pvNewField = pNewRecord->root(); - for (; pvNewField; pvNewField = pNewRecord->next( pvNewField)) - { - bEncrypted = pNewRecord->isEncryptedField( pvNewField); - uiOverhead = (bEncrypted ? 10 : 6); - if (uiTmpBodyLen + uiOverhead <= RFL_MAX_PACKET_BODY_SIZE) - { - uiTmpBodyLen += uiOverhead; - } - else - { - uiTmpBodyLen = uiOverhead; - RflChangeData.uiMaxLogBytesNeeded += RFL_PACKET_OVERHEAD; - } - - RflChangeData.uiMaxLogBytesNeeded += uiOverhead; - if (bEncrypted) - { - uiDataLen = pNewRecord->getEncryptedDataLength( pvNewField); - } - else - { - uiDataLen = pNewRecord->getDataLength( pvNewField); - } - - while (uiDataLen) - { - FLMUINT uiTmp; - - uiTmp = RFL_MAX_PACKET_BODY_SIZE - uiTmpBodyLen; - if (uiTmp >= uiDataLen) - { - uiTmp = uiDataLen; - uiTmpBodyLen += uiDataLen; - } - else - { - uiTmpBodyLen = 0; - RflChangeData.uiMaxLogBytesNeeded += RFL_PACKET_OVERHEAD; - } - - RflChangeData.uiMaxLogBytesNeeded += uiTmp; - uiDataLen -= uiTmp; - } - } - - // Account for terminating 0 at the end. - - if (uiTmpBodyLen + 2 > RFL_MAX_PACKET_BODY_SIZE) - { - RflChangeData.uiMaxLogBytesNeeded += RFL_PACKET_OVERHEAD; - } - - RflChangeData.uiMaxLogBytesNeeded += 2; - - RflChangeData.uiPacketCount = 0; - RflChangeData.uiTotalBytesLogged = RFL_PACKET_OVERHEAD; - RflChangeData.uiCurrPacketLen = RFL_PACKET_OVERHEAD; - - if (!haveBuffSpace( RFL_PACKET_OVERHEAD)) - { - if (RC_BAD( RflChangeData.rc = flush( m_pCurrentBuf))) - { - goto Exit; - } - } - - flmRecordDifference( pOldRecord, pNewRecord, RflChangeCallback, - (void *) &RflChangeData); - - // See if we exceeded the maximum log bytes. If so, just log the - // changed record in its entirety. - - if (RC_BAD( RflChangeData.rc)) - { - if (RflChangeData.rc == FERR_FAILURE) - { - RflChangeData.rc = logRecord( pNewRecord); - } - - goto Exit; - } - else - { - FLMBYTE * pucTmp; - - // Make room to log the 3 bytes of terminator - - if (RC_BAD( RflChangeData.rc = makeRoom( 3, - &RflChangeData.uiCurrPacketLen, RFL_CHANGE_FIELDS_PACKET, NULL, - &RflChangeData.uiPacketCount))) - { - if (RflChangeData.rc == FERR_FAILURE) - { - RflChangeData.rc = logRecord( pNewRecord); - } - - goto Exit; - } - - pucTmp = getPacketPtr() + RflChangeData.uiCurrPacketLen; - *pucTmp++ = RFL_END_FIELD_CHANGES; - UW2FBA( (FLMUINT16) 0, pucTmp); - RflChangeData.uiCurrPacketLen += 3; - - if (RC_BAD( RflChangeData.rc = finishPacket( RFL_CHANGE_FIELDS_PACKET, - RflChangeData.uiCurrPacketLen - RFL_PACKET_OVERHEAD, FALSE))) - { - goto Exit; - } - } - -Exit: - - return (RflChangeData.rc); -} - -/******************************************************************** -Desc: Log a record for the record add or modify operations. -*********************************************************************/ -RCODE F_Rfl::logRecord( - FlmRecord * pRecord) -{ - RCODE rc = FERR_OK; - FLMUINT uiPacketLen = RFL_PACKET_OVERHEAD; - void * pvField; - FLMBYTE * pucTmp; - FLMUINT uiTagNum; - FLMUINT uiDataLen; - FLMBOOL bEncrypted; - FLMUINT uiEncId; - FLMUINT uiPacketType; - FLMUINT uiOverhead; - - if (!haveBuffSpace( RFL_PACKET_OVERHEAD)) - { - if (RC_BAD( rc = flush( m_pCurrentBuf))) - { - goto Exit; - } - } - - if (m_pFile->FileHdr.uiVersionNum < FLM_FILE_FORMAT_VER_4_60) - { - uiPacketType = RFL_DATA_RECORD_PACKET; - } - else if (m_pFile->FileHdr.uiVersionNum < FLM_FILE_FORMAT_VER_4_61) - { - uiPacketType = RFL_ENC_DATA_RECORD_PACKET; - } - else - { - uiPacketType = RFL_DATA_RECORD_PACKET_VER_3; - } - - pvField = pRecord->root(); - for (; pvField; pvField = pRecord->next( pvField)) - { - if (uiPacketType == RFL_DATA_RECORD_PACKET) - { - bEncrypted = FALSE; - uiOverhead = 6; - } - else if (uiPacketType == RFL_ENC_DATA_RECORD_PACKET) - { - bEncrypted = pRecord->isEncryptedField( pvField); - uiOverhead = (bEncrypted ? 11 : 7); - } - else - { - bEncrypted = pRecord->isEncryptedField( pvField); - uiOverhead = (bEncrypted ? 15 : 9); - } - - if (RC_BAD( rc = makeRoom( uiOverhead, &uiPacketLen, uiPacketType, NULL, - NULL))) - { - goto Exit; - } - - pucTmp = getPacketPtr() + uiPacketLen; - uiPacketLen += uiOverhead; - - uiTagNum = pRecord->getFieldID( pvField); - UW2FBA( (FLMUINT16) uiTagNum, pucTmp); - pucTmp += 2; - *pucTmp++ = (FLMBYTE) pRecord->getDataType( pvField); - *pucTmp++ = (FLMBYTE) pRecord->getLevel( pvField); - uiDataLen = pRecord->getDataLength( pvField); - if (uiPacketType != RFL_DATA_RECORD_PACKET_VER_3) - { - UW2FBA( (FLMUINT16) uiDataLen, pucTmp); - pucTmp += 2; - } - else - { - UD2FBA( (FLMUINT32) uiDataLen, pucTmp); - pucTmp += 4; - } - - // Record if this field is encrypted. If it is, then there will be - // more data to follow. - - if (uiPacketType != RFL_DATA_RECORD_PACKET) - { - *pucTmp = (bEncrypted ? (FLMBYTE) 1 : (FLMBYTE) 0); - pucTmp++; - - // Check for encrypted field and add the results. - - if (bEncrypted) - { - uiEncId = pRecord->getEncryptionID( pvField); - flmAssert( uiEncId); - UW2FBA( (FLMUINT16) uiEncId, pucTmp); - pucTmp += 2; - - uiDataLen = pRecord->getEncryptedDataLength( pvField); - if (uiPacketType == RFL_DATA_RECORD_PACKET_VER_3) - { - UD2FBA( (FLMUINT32)uiDataLen, pucTmp); - pucTmp += 4; - } - else - { - UW2FBA( (FLMUINT16)uiDataLen, pucTmp); - pucTmp += 2; - } - } - } - - // Log the data, if any. - - if (uiDataLen) - { - const FLMBYTE * pucExportPtr; - - if (bEncrypted) - { - pucExportPtr = pRecord->getEncryptionDataPtr( pvField); - } - else - { - pucExportPtr = pRecord->getDataPtr( pvField); - } - - if (pucExportPtr == NULL) - { - rc = RC_SET( FERR_MEM); - goto Exit; - } - - if (RC_BAD( rc = logData( uiDataLen, pucExportPtr, uiPacketType, - &uiPacketLen, NULL, NULL, NULL))) - { - goto Exit; - } - } - } - - // Add null to terminate the record. - - if (RC_BAD( rc = makeRoom( 2, &uiPacketLen, uiPacketType, NULL, NULL))) - { - goto Exit; - } - - pucTmp = getPacketPtr() + uiPacketLen; - uiPacketLen += 2; - UW2FBA( 0, pucTmp); - pucTmp += 2; - - // Finish the packet. - - if (RC_BAD( rc = finishPacket( uiPacketType, - uiPacketLen - RFL_PACKET_OVERHEAD, FALSE))) - { - goto Exit; - } - -Exit: - - return (rc); -} - -/******************************************************************** -Desc: Log record add, modify, or delete operation -*********************************************************************/ -RCODE F_Rfl::logUpdate( - FLMUINT uiContainer, - FLMUINT uiDrn, - FLMUINT uiAutoTrans, - FlmRecord * pOldRecord, - FlmRecord * pNewRecord) -{ - RCODE rc = FERR_OK; - FLMUINT uiPacketType; - - // Do nothing if logging is disabled. - - if (m_bLoggingOff) - { - goto Exit; - } - - // Better not be in the middle of logging unknown stuff for the - // application - - flmAssert( !m_bLoggingUnknown); - - // Better be in the middle of a transaction. - - flmAssert( m_uiCurrTransID); - - if (pOldRecord && pNewRecord) - { - if (m_pFile->FileHdr.uiVersionNum >= FLM_FILE_FORMAT_VER_4_60) - { - uiPacketType = RFL_MODIFY_RECORD_PACKET_VER_2; - } - else - { - uiPacketType = RFL_MODIFY_RECORD_PACKET; - } - } - else if (pNewRecord) - { - if (m_pFile->FileHdr.uiVersionNum >= FLM_FILE_FORMAT_VER_4_60) - { - uiPacketType = RFL_ADD_RECORD_PACKET_VER_2; - } - else - { - uiPacketType = RFL_ADD_RECORD_PACKET; - } - } - else - { - if (m_pFile->FileHdr.uiVersionNum >= FLM_FILE_FORMAT_VER_4_60) - { - uiPacketType = RFL_DELETE_RECORD_PACKET_VER_2; - } - else - { - uiPacketType = RFL_DELETE_RECORD_PACKET; - } - } - - if (RC_BAD( rc = logUpdatePacket( uiPacketType, uiContainer, uiDrn, - uiAutoTrans))) - { - goto Exit; - } - - // If it is a record modify, log the change fields. If it is a record - // add, log the new record. - - if (pOldRecord && pNewRecord) - { - if (RC_BAD( rc = logChangeFields( pOldRecord, pNewRecord))) - { - goto Exit; - } - } - else if (pNewRecord) - { - if (RC_BAD( rc = logRecord( pNewRecord))) - { - goto Exit; - } - } - -Exit: - - return (rc); -} - -/******************************************************************** -Desc: Log a set of records that is indexed for a specific index. -*********************************************************************/ -RCODE F_Rfl::logIndexSet( - FLMUINT uiIndex, - FLMUINT uiContainerNum, - FLMUINT uiStartDrn, - FLMUINT uiEndDrn) -{ - RCODE rc = FERR_OK; - FLMBYTE * pucPacketBody; - FLMUINT uiPacketBodyLen; - - // This call is a new database version. Database better have been - // upgraded. - - flmAssert( m_pFile->FileHdr.uiVersionNum >= FLM_FILE_FORMAT_VER_3_02); - - // Do nothing if logging is disabled. - - if (m_bLoggingOff) - { - goto Exit; - } - - // Better not be in the middle of logging unknown stuff for the - // application - - flmAssert( !m_bLoggingUnknown); - - // Better be in the middle of a transaction. - - flmAssert( m_uiCurrTransID); - - m_uiOperCount++; - uiPacketBodyLen = - (FLMUINT)((m_pFile->FileHdr.uiVersionNum >= FLM_FILE_FORMAT_VER_4_50) - ? (FLMUINT) 16 - : (FLMUINT) 14); - - // Make sure we have space in the RFL buffer for a complete packet. - - if (!haveBuffSpace( uiPacketBodyLen + RFL_PACKET_OVERHEAD)) - { - if (RC_BAD( rc = flush( m_pCurrentBuf))) - { - goto Exit; - } - } - - // Get a pointer to where we will be laying down the packet body. - - pucPacketBody = getPacketBodyPtr(); - - // Output the transaction ID. - - UD2FBA( (FLMUINT32) m_uiCurrTransID, pucPacketBody); - pucPacketBody += 4; - - // Output the container number, if db version is >= 4.50 - - if (m_pFile->FileHdr.uiVersionNum >= FLM_FILE_FORMAT_VER_4_50) - { - UW2FBA( (FLMUINT16) uiContainerNum, pucPacketBody); - pucPacketBody += 2; - } - - // Output the index number. - - UW2FBA( (FLMUINT16) uiIndex, pucPacketBody); - pucPacketBody += 2; - - // Output the starting DRN. - - UD2FBA( (FLMUINT32) (uiStartDrn), pucPacketBody); - pucPacketBody += 4; - - // Output the ending DRN. - - UD2FBA( (FLMUINT32) (uiEndDrn), pucPacketBody); - pucPacketBody += 4; - - // Finish the packet - - if (RC_BAD( rc = finishPacket( (FLMUINT) ( - (m_pFile->FileHdr.uiVersionNum >= FLM_FILE_FORMAT_VER_4_50) - ? (FLMUINT) RFL_INDEX_SET_PACKET_VER_2 - : (FLMUINT) RFL_INDEX_SET_PACKET), - uiPacketBodyLen, FALSE))) - { - goto Exit; - } - -Exit: - - return (rc); -} - -/******************************************************************** -Desc: Start logging unknown packets. -*********************************************************************/ -RCODE F_Rfl::startLoggingUnknown(void) -{ - RCODE rc = FERR_OK; - FLMBYTE * pucPacketBody; - FLMUINT uiPacketBodyLen; - - flmAssert( m_pFile); - - // Do nothing if logging is disabled. Also, ignore these packets if we - // are operating on a pre-4.3 database. - - if (m_bLoggingOff || m_pFile->FileHdr.uiVersionNum < FLM_FILE_FORMAT_VER_4_3) - { - goto Exit; - } - - // Better not already be in the middle of logging unknown stuff for - // the application - - flmAssert( !m_bLoggingUnknown); - - // Better be inside a transaction. - - flmAssert( m_uiCurrTransID); - - m_uiOperCount++; - uiPacketBodyLen = 4; - - // Make sure we have space in the RFL buffer for a complete packet. - - if (!haveBuffSpace( uiPacketBodyLen + RFL_PACKET_OVERHEAD)) - { - if (RC_BAD( rc = flush( m_pCurrentBuf))) - { - goto Exit; - } - } - - // Get a pointer to where we will be laying down the packet body. - - pucPacketBody = getPacketBodyPtr(); - - // Output the transaction ID. - - UD2FBA( (FLMUINT32) m_uiCurrTransID, pucPacketBody); - pucPacketBody += 4; - - // Finish the packet - - if (RC_BAD( rc = finishPacket( RFL_START_UNKNOWN_PACKET, uiPacketBodyLen, - FALSE))) - { - goto Exit; - } - - m_bLoggingUnknown = TRUE; - m_uiUnknownPacketLen = RFL_PACKET_OVERHEAD; - -Exit: - - return (rc); -} - -/******************************************************************** -Desc: Log unknown data. -*********************************************************************/ -RCODE F_Rfl::logUnknown( - FLMBYTE * pucUnknown, - FLMUINT uiLen) -{ - RCODE rc = FERR_OK; - - // Do nothing if logging is disabled. Also, ignore these packets if we - // are operating on a pre-4.3 database. - - if (m_bLoggingOff || m_pFile->FileHdr.uiVersionNum < FLM_FILE_FORMAT_VER_4_3) - { - goto Exit; - } - - flmAssert( m_bLoggingUnknown); - if (RC_BAD( rc = logData( uiLen, pucUnknown, RFL_UNKNOWN_PACKET, - &m_uiUnknownPacketLen, NULL, NULL, NULL))) - { - goto Exit; - } - -Exit: - - return (rc); -} - -/******************************************************************** -Desc: End logging unknown packets. -*********************************************************************/ -RCODE F_Rfl::endLoggingUnknown(void) -{ - RCODE rc = FERR_OK; - - flmAssert( m_pFile); - - // Do nothing if logging is disabled. Also, ignore these packets if we - // are operating on a pre-4.3 database. - - if (m_bLoggingOff || m_pFile->FileHdr.uiVersionNum < FLM_FILE_FORMAT_VER_4_3) - { - goto Exit; - } - - // Better be in the middle of logging unknown stuff for the application - - flmAssert( m_bLoggingUnknown); - if (m_uiUnknownPacketLen > RFL_PACKET_OVERHEAD) - { - if (RC_BAD( rc = finishPacket( RFL_UNKNOWN_PACKET, - m_uiUnknownPacketLen - RFL_PACKET_OVERHEAD, FALSE))) - { - goto Exit; - } - } - -Exit: - - m_bLoggingUnknown = FALSE; - m_uiUnknownPacketLen = RFL_PACKET_OVERHEAD; - return (rc); -} - -/******************************************************************** -Desc: Log a reduce packet -*********************************************************************/ -RCODE F_Rfl::logReduce( - FLMUINT uiTransID, - FLMUINT uiCount) -{ - RCODE rc = FERR_OK; - FLMBYTE * pucPacketBody; - FLMUINT uiPacketBodyLen; - - // This call is new with 4.3 databases - not supported in older - // versions, so don't log it. - - if (m_pFile->FileHdr.uiVersionNum < FLM_FILE_FORMAT_VER_4_3) - { - goto Exit; - } - - // Do nothing if logging is disabled. - - if (m_bLoggingOff) - { - goto Exit; - } - - // Better not be in the middle of logging unknown stuff for the - // application - - flmAssert( !m_bLoggingUnknown); - - // We need to set up to log this packet as if we were logging a - // transaction. The only difference is that we don't log the begin - // transaction packet. - - if (RC_BAD( rc = setupTransaction())) - { - goto Exit; - } - - uiPacketBodyLen = 8; - - // Make sure we have space in the RFL buffer for a complete packet. - - if (!haveBuffSpace( uiPacketBodyLen + RFL_PACKET_OVERHEAD)) - { - if (RC_BAD( rc = flush( m_pCurrentBuf))) - { - goto Exit; - } - } - - // Get a pointer to where we will be laying down the packet body. - - pucPacketBody = getPacketBodyPtr(); - - // Output the transaction ID. - - UD2FBA( (FLMUINT32) uiTransID, pucPacketBody); - pucPacketBody += 4; - - // Output the count - - UD2FBA( (FLMUINT32) uiCount, pucPacketBody); - pucPacketBody += 4; - - // Finish the packet - - if (RC_BAD( rc = finishPacket( RFL_REDUCE_PACKET, uiPacketBodyLen, TRUE))) - { - goto Exit; - } - - // Finalize the transaction (as if we were committing a transaction) - - finalizeTransaction(); - -Exit: - - return (rc); -} - -/******************************************************************** -Desc: Log a database conversion packet -Note: This routine performs most of the setup for logging a full - transaction, but it does not cause begin and commit packets - to be logged. It is a "standalone" transaction. -*********************************************************************/ -RCODE F_Rfl::logUpgrade( - FLMUINT uiTransID, - FLMUINT uiOldVersion, - FLMBYTE * pucDBKey, - FLMUINT32 ui32DBKeyLen) -{ - RCODE rc = FERR_OK; - FLMBYTE * pucPacketBody; - FLMUINT uiPacketBodyLen; - - // Do nothing if logging is disabled. - - if (m_bLoggingOff) - { - goto Exit; - } - - // Better not be in the middle of logging unknown stuff for the - // application - - flmAssert( !m_bLoggingUnknown); - - // We need to set up to log this packet as if we were logging a - // transaction. The only difference is that we don't log the begin - // transaction packet. - - if (RC_BAD( rc = setupTransaction())) - { - goto Exit; - } - - uiPacketBodyLen = 14 + ui32DBKeyLen; - - // Make sure we have space in the RFL buffer for a complete packet. - - if (!haveBuffSpace( uiPacketBodyLen + RFL_PACKET_OVERHEAD)) - { - if (RC_BAD( rc = flush( m_pCurrentBuf))) - { - goto Exit; - } - } - - // Get a pointer to where we will be laying down the packet body. - - pucPacketBody = getPacketBodyPtr(); - - // Output the transaction ID - - UD2FBA( (FLMUINT32) uiTransID, pucPacketBody); - pucPacketBody += 4; - - // Output the old database version - - UD2FBA( (FLMUINT32) uiOldVersion, pucPacketBody); - pucPacketBody += 4; - - // Output the new database version - - UD2FBA( (FLMUINT32) FLM_CUR_FILE_FORMAT_VER_NUM, pucPacketBody); - pucPacketBody += 4; - - // For versions >= 4.60, the next two bytes will give the length of - // the DB Key. - - flmAssert( ui32DBKeyLen <= 0xFFFF); - UW2FBA( (FLMUINT16) ui32DBKeyLen, pucPacketBody); - pucPacketBody += 2; - - // If we were built without encryption, the key length will be zero, - // so no need to store the key. - - if (ui32DBKeyLen) - { - f_memcpy( pucPacketBody, pucDBKey, ui32DBKeyLen); - pucPacketBody += ui32DBKeyLen; - } - - // Finish the packet - - if (RC_BAD( rc = finishPacket( RFL_UPGRADE_PACKET, uiPacketBodyLen, TRUE))) - { - goto Exit; - } - - // Finalize the transaction (as if we were committing a transaction) - - finalizeTransaction(); - -Exit: - - if (!m_bLoggingOff) - { - m_uiCurrTransID = 0; - } - - return (rc); -} - -/******************************************************************** -Desc: Log the wrapped database key -*********************************************************************/ -RCODE F_Rfl::logWrappedKey( - FLMUINT uiTransID, - FLMBYTE * pucDBKey, - FLMUINT32 ui32DBKeyLen) -{ - RCODE rc = FERR_OK; - FLMBYTE * pucPacketBody; - FLMUINT uiPacketBodyLen; - - // Do nothing if logging is disabled. - - if (m_bLoggingOff) - { - goto Exit; - } - - // Better not be in the middle of logging unknown stuff for the - // application - - flmAssert( !m_bLoggingUnknown); - - if (RC_BAD( rc = setupTransaction())) - { - goto Exit; - } - - uiPacketBodyLen = 6 + ui32DBKeyLen; - - // Make sure we have space in the RFL buffer for a complete packet. - - if (!haveBuffSpace( uiPacketBodyLen + RFL_PACKET_OVERHEAD)) - { - if (RC_BAD( rc = flush( m_pCurrentBuf))) - { - goto Exit; - } - } - - // Get a pointer to where we will be laying down the packet body. - - pucPacketBody = getPacketBodyPtr(); - - // Output the transaction ID - - UD2FBA( (FLMUINT32) uiTransID, pucPacketBody); - pucPacketBody += 4; - - // The next two bytes will give the length of the DB Key. - - flmAssert( ui32DBKeyLen <= 0xFFFF); - UW2FBA( (FLMUINT16) ui32DBKeyLen, pucPacketBody); - pucPacketBody += 2; - - // If we were built without encryption, the key length will be zero, - // so no need to store the key. - - if (ui32DBKeyLen) - { - f_memcpy( pucPacketBody, pucDBKey, ui32DBKeyLen); - pucPacketBody += ui32DBKeyLen; - } - - // Finish the packet - - if (RC_BAD( rc = finishPacket( RFL_WRAP_KEY_PACKET, uiPacketBodyLen, TRUE))) - { - goto Exit; - } - - finalizeTransaction(); - -Exit: - - return (rc); -} - -/******************************************************************** -Desc: Log that we have enabled encryption -*********************************************************************/ -RCODE F_Rfl::logEnableEncryption( - FLMUINT uiTransID, - FLMBYTE * pucDBKey, - FLMUINT32 ui32DBKeyLen) -{ - RCODE rc = FERR_OK; - FLMBYTE * pucPacketBody; - FLMUINT uiPacketBodyLen; - - // Do nothing if logging is disabled. - - if (m_bLoggingOff) - { - goto Exit; - } - - // Better not be in the middle of logging unknown stuff for the - // application - - flmAssert( !m_bLoggingUnknown); - - if (RC_BAD( rc = setupTransaction())) - { - goto Exit; - } - - uiPacketBodyLen = 6 + ui32DBKeyLen; - - // Make sure we have space in the RFL buffer for a complete packet. - - if (!haveBuffSpace( uiPacketBodyLen + RFL_PACKET_OVERHEAD)) - { - if (RC_BAD( rc = flush( m_pCurrentBuf))) - { - goto Exit; - } - } - - // Get a pointer to where we will be laying down the packet body. - - pucPacketBody = getPacketBodyPtr(); - - // Output the transaction ID - - UD2FBA( (FLMUINT32) uiTransID, pucPacketBody); - pucPacketBody += 4; - - // The next two bytes will give the length of the DB Key. - - flmAssert( ui32DBKeyLen <= 0xFFFF); - UW2FBA( (FLMUINT16) ui32DBKeyLen, pucPacketBody); - pucPacketBody += 2; - - // If we were built without encryption, the key length will be zero, - // so no need to store the key. - - if (ui32DBKeyLen) - { - f_memcpy( pucPacketBody, pucDBKey, ui32DBKeyLen); - pucPacketBody += ui32DBKeyLen; - } - - // Finish the packet - - if (RC_BAD( rc = finishPacket( RFL_ENABLE_ENCRYPTION_PACKET, uiPacketBodyLen, - TRUE))) - { - goto Exit; - } - - finalizeTransaction(); - -Exit: - - return (rc); -} - -/******************************************************************** -Desc: Log a block chain free operation -*********************************************************************/ -RCODE F_Rfl::logBlockChainFree( - FLMUINT uiTrackerDrn, - FLMUINT uiCount, - FLMUINT uiEndAddr) -{ - RCODE rc = FERR_OK; - FLMBYTE * pucPacketBody; - FLMUINT uiPacketBodyLen; - - // This call is new with 4.52 databases - not supported in older - // versions, so don't log it. - - if (m_pFile->FileHdr.uiVersionNum < FLM_FILE_FORMAT_VER_4_60) - { - flmAssert( 0); - goto Exit; - } - - // Do nothing if logging is disabled. - - if (m_bLoggingOff) - { - goto Exit; - } - - // Better not be in the middle of logging unknown stuff for the - // application - - flmAssert( !m_bLoggingUnknown); - - // Better be in the middle of a transaction. - - flmAssert( m_uiCurrTransID); - m_uiOperCount++; - - uiPacketBodyLen = 16; - - // Make sure we have space in the RFL buffer for a complete packet. - - if (!haveBuffSpace( uiPacketBodyLen + RFL_PACKET_OVERHEAD)) - { - if (RC_BAD( rc = flush( m_pCurrentBuf))) - { - goto Exit; - } - } - - // Get a pointer to where we will be laying down the packet body. - - pucPacketBody = getPacketBodyPtr(); - - // Output the transaction ID. - - UD2FBA( (FLMUINT32) m_uiCurrTransID, pucPacketBody); - pucPacketBody += 4; - - // Output the tracker record number - - UD2FBA( (FLMUINT32) uiTrackerDrn, pucPacketBody); - pucPacketBody += 4; - - // Output the count - - UD2FBA( (FLMUINT32) uiCount, pucPacketBody); - pucPacketBody += 4; - - // Output the ending block address - - UD2FBA( (FLMUINT32) uiEndAddr, pucPacketBody); - pucPacketBody += 4; - - // Finish the packet - - if (RC_BAD( rc = finishPacket( RFL_BLK_CHAIN_FREE_PACKET, uiPacketBodyLen, - TRUE))) - { - goto Exit; - } - -Exit: - - return (rc); -} - -/******************************************************************** -Desc: Reads a full packet, based on what file offset and read - offset are currently set to. -*********************************************************************/ -RCODE F_Rfl::readPacket( - FLMUINT uiMinBytesNeeded) -{ - RCODE rc = FERR_OK; - FLMUINT uiTmpOffset; - FLMUINT uiReadLen; - FLMUINT uiBytesRead; - - // If we have enough bytes in the buffer for the minimum bytes needed, - // we don't need to retrieve any more bytes. - - if (m_pCurrentBuf->uiRflBufBytes - m_uiRflReadOffset >= uiMinBytesNeeded) - { - goto Exit; - } - - // If we are doing restore, we have to do only sequential reads - - // cannot depend on doing reads on 512 byte boundaries. Otherwise, we - // read directly from disk on 512 byte boundaries. - - if (m_pRestore) - { - FLMUINT uiCurrFilePos = m_pCurrentBuf->uiRflFileOffset + - m_pCurrentBuf->uiRflBufBytes; - - if (m_uiRflReadOffset > 0) - { - - // Move the bytes left in the buffer down to the beginning of - // the buffer. - - f_memmove( m_pCurrentBuf->pIOBuffer->m_pucBuffer, - &(m_pCurrentBuf->pIOBuffer->m_pucBuffer[m_uiRflReadOffset]), - m_pCurrentBuf->uiRflBufBytes - m_uiRflReadOffset); - m_pCurrentBuf->uiRflBufBytes -= m_uiRflReadOffset; - m_pCurrentBuf->uiRflFileOffset += m_uiRflReadOffset; - m_uiRflReadOffset = 0; - } - - uiReadLen = m_uiBufferSize - m_pCurrentBuf->uiRflBufBytes; - - // Read enough to fill the rest of the buffer, which is guaranteed - // to hold at least one full packet. - - if (!m_uiFileEOF) - { - if (uiCurrFilePos > (FLMUINT) (-1) - uiReadLen) - { - uiReadLen = (FLMUINT) (-1) - uiCurrFilePos; - } - } - else - { - if (uiCurrFilePos + uiReadLen > m_uiFileEOF) - { - uiReadLen = m_uiFileEOF - uiCurrFilePos; - } - } - - // If reading will not give us the minimum bytes needed, we cannot - // satisfy this request from the current file. - - if (uiReadLen + m_pCurrentBuf->uiRflBufBytes < uiMinBytesNeeded) - { - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - - // Read enough to get the entire packet. - - if (RC_BAD( rc = m_pRestore->read( uiReadLen, &( - m_pCurrentBuf->pIOBuffer->m_pucBuffer[ - m_pCurrentBuf->uiRflBufBytes]), &uiBytesRead))) - { - if (rc == FERR_IO_END_OF_FILE) - { - rc = FERR_OK; - } - else - { - goto Exit; - } - } - - // If we didn't read enough to satisfy the minimum bytes needed, we - // cannot satisfy this request from the current file. - - if (uiBytesRead + m_pCurrentBuf->uiRflBufBytes < uiMinBytesNeeded) - { - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - - m_pCurrentBuf->uiRflBufBytes += uiBytesRead; - } - else - { - - // Set offsets so we are on a 512 byte boundary for our next read. - // No need to move data, since we will be re-reading it anyway. - - if (m_uiRflReadOffset > 0) - { - uiTmpOffset = m_uiRflReadOffset - (m_uiRflReadOffset & 511); - m_pCurrentBuf->uiRflFileOffset += uiTmpOffset; - m_uiRflReadOffset -= uiTmpOffset; - } - else if (m_pCurrentBuf->uiRflFileOffset & 511) - { - m_uiRflReadOffset = m_pCurrentBuf->uiRflFileOffset & 511; - m_pCurrentBuf->uiRflFileOffset -= m_uiRflReadOffset; - } - - m_pCurrentBuf->uiRflBufBytes = 0; - - // Read enough to fill the rest of the buffer, which is guaranteed - // to hold at least one full packet. - - uiReadLen = m_uiBufferSize; - - // m_uiFileEOF better not be zero at this point - we should always - // know precisely where the RFL file ends when we are doing recovery - // as opposed to doing a restore. - - flmAssert( m_uiFileEOF >= 512); - if (m_pCurrentBuf->uiRflFileOffset + uiReadLen > m_uiFileEOF) - { - uiReadLen = m_uiFileEOF - m_pCurrentBuf->uiRflFileOffset; - } - - // If reading will not give us the minimum number of bytes needed, - // we have a bad packet. - - if (uiReadLen < m_uiRflReadOffset || - uiReadLen - m_uiRflReadOffset < uiMinBytesNeeded) - { - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - - // Read to get the entire packet. - - if (RC_BAD( rc = m_pFileHdl->SectorRead( m_pCurrentBuf->uiRflFileOffset, - uiReadLen, m_pCurrentBuf->pIOBuffer->m_pucBuffer, - &uiBytesRead))) - { - if (rc == FERR_IO_END_OF_FILE) - { - rc = FERR_OK; - } - else - { - m_bRflVolumeOk = FALSE; - goto Exit; - } - } - - if (uiBytesRead < uiReadLen) - { - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - - m_pCurrentBuf->uiRflBufBytes = uiReadLen; - } - -Exit: - - return (rc); -} - -/******************************************************************** -Desc: Gets and verifies the next packet from the roll-forward - log file. Packet checksum will be verified. -*********************************************************************/ -RCODE F_Rfl::getPacket( - FLMBOOL bForceNextFile, - FLMUINT * puiPacketTypeRV, - FLMBYTE ** ppucPacketBodyRV, - FLMUINT * puiPacketBodyLenRV, - FLMBOOL * pbLoggedTimes) -{ - RCODE rc = FERR_OK; - FLMBYTE * pucPacket; - FLMBYTE * pucPacketBody; - FLMUINT uiPacketType; - FLMUINT uiEncryptPacketBodyLen; - FLMBYTE ucHdr[512]; - FLMUINT uiBytesRead; - - // See if we need to go to the next file. Note that we only check for - // this exactly on packet boundaries. We do not expect packets to be - // split across files. If we are not at the end of processing what is - // in the buffer, we should be able to read the rest of the packet from - // the current file. - -Get_Next_File: - - if (bForceNextFile || - (m_uiFileEOF && m_uiRflReadOffset == m_pCurrentBuf->uiRflBufBytes && - m_pCurrentBuf->uiRflFileOffset + m_pCurrentBuf->uiRflBufBytes == - m_uiFileEOF)) - { - if (m_bKeepRflFiles) - { - if (!m_pRestore) - { - - // Only doing recovery after a failure, see if we are at the - // last file already. - - if (m_pCurrentBuf->uiCurrFileNum == m_uiLastRecoverFileNum) - { - rc = RC_SET( FERR_END); - goto Exit; - } - else if( (m_pCurrentBuf->uiCurrFileNum + 1 ) == - m_uiLastRecoverFileNum && - !(FLMUINT)FB2UD( &m_pFile->ucLastCommittedLogHdr[ - LOG_RFL_LAST_TRANS_OFFSET])) - { - - // We are going to try to open the last file. Since the - // log header shows a current offset of 0, the file may - // have been created but nothing was logged to it. We don't - // want to try to open it here because it may not have been - // initialized fully at the time of the server crash. - - m_pCurrentBuf->uiCurrFileNum = m_uiLastRecoverFileNum; - rc = RC_SET( FERR_END); - goto Exit; - } - - // Open the next file in the sequence. - - if (RC_BAD( rc = openFile( m_pCurrentBuf->uiCurrFileNum + 1, - m_ucNextSerialNum))) - { - if (rc == FERR_IO_PATH_NOT_FOUND || rc == FERR_IO_INVALID_PATH) - { - rc = RC_SET( FERR_END); - } - - goto Exit; - } - - // If this is the last RFL file, the EOF is contained in the - // log header. Otherwise, it will be in the RFL file's header, - // and openFile will already have retrieved it. - - if (m_pCurrentBuf->uiCurrFileNum == m_uiLastRecoverFileNum) - { - m_uiFileEOF = (FLMUINT) FB2UD( &m_pFile->ucLastCommittedLogHdr[ - LOG_RFL_LAST_TRANS_OFFSET]); - - // Could be zero if RFL file wasn't created yet. - - if (!m_uiFileEOF) - { - m_uiFileEOF = 512; - } - } - - // By this point, the EOF better be greater than or equal to - // 512. - - flmAssert( m_uiFileEOF >= 512); - } - else - { - if (RC_BAD( rc = m_pRestore->close())) - { - goto Exit; - } - - // Ask the recovery object to open the file. - - if (RC_BAD( rc = m_pRestore->openRflFile( - m_pCurrentBuf->uiCurrFileNum + 1))) - { - if (rc == FERR_IO_PATH_NOT_FOUND) - { - rc = RC_SET( FERR_END); - } - - goto Exit; - } - - // Get the first 512 bytes from the file and verify the - // header. - - if (RC_BAD( rc = m_pRestore->read( 512, ucHdr, &uiBytesRead))) - { - goto Exit; - } - - if (uiBytesRead < 512) - { - rc = RC_SET( FERR_NOT_RFL); - goto Exit; - } - - if (RC_BAD( rc = verifyHeader( ucHdr, - m_pCurrentBuf->uiCurrFileNum + 1, m_ucNextSerialNum))) - { - goto Exit; - } - - // We may not know the actual EOF of files during restore - // operations. m_uiFileEOF could be zero here. - - m_uiFileEOF = (FLMUINT) FB2UD( &ucHdr[RFL_EOF_POS]); - - // File EOF may be zero or >= 512 at this point. - - flmAssert( !m_uiFileEOF || m_uiFileEOF >= 512); - - // Need to increment current file number. - - m_pCurrentBuf->uiCurrFileNum++; - } - - m_pCurrentBuf->uiRflFileOffset = 512; - m_uiRflReadOffset = 0; - m_pCurrentBuf->uiRflBufBytes = 0; - - // Get the next packet from the new file. - - if (RC_BAD( rc = readPacket( RFL_PACKET_OVERHEAD))) - { - if (m_uiFileEOF == 512 && m_bKeepRflFiles) - { - - // File was empty, try to go to the next file. - - bForceNextFile = TRUE; - goto Get_Next_File; - } - - goto Exit; - } - } - else - { - - // This is the case where we are not keeping the RFL files. So, - // there is no next file to go to. If we get to this point, we - // had better not be doing a restore. - - flmAssert( m_pRestore == NULL && !bForceNextFile); - rc = RC_SET( FERR_END); - goto Exit; - } - } - - // Make sure we at least have a packet header in the buffer. - - if (RC_BAD( rc = readPacket( RFL_PACKET_OVERHEAD))) - { - goto Exit; - } - - // Verify the packet address. - - m_uiPacketAddress = m_pCurrentBuf->uiRflFileOffset + m_uiRflReadOffset; - pucPacket = &(m_pCurrentBuf->pIOBuffer->m_pucBuffer[m_uiRflReadOffset]); - if ((FLMUINT) FB2UD( &pucPacket[RFL_PACKET_ADDRESS_OFFSET]) != m_uiPacketAddress) - { - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - - // Get packet type, time flag, and packet body length - - *puiPacketTypeRV = uiPacketType = RFL_GET_PACKET_TYPE( - pucPacket[RFL_PACKET_TYPE_OFFSET]); - - if (pbLoggedTimes) - { - *pbLoggedTimes = (pucPacket[RFL_PACKET_TYPE_OFFSET] & RFL_TIME_LOGGED_FLAG) - ? TRUE - : FALSE; - } - - *puiPacketBodyLenRV = (FLMUINT) FB2UW( - &pucPacket[RFL_PACKET_BODY_LENGTH_OFFSET]); - - // Adjust the packet body length for encryption if necessary. NOTE: - // This adjusted length is NOT returned to the caller. The actual body - // length is what will be returned. - - uiEncryptPacketBodyLen = getEncryptPacketBodyLen( uiPacketType, - *puiPacketBodyLenRV); - - // Make sure we have the entire packet in the buffer. - - if (RC_BAD( rc = readPacket( uiEncryptPacketBodyLen + RFL_PACKET_OVERHEAD))) - { - goto Exit; - } - - pucPacket = &(m_pCurrentBuf->pIOBuffer->m_pucBuffer[m_uiRflReadOffset]); - - // At this point, we are guaranteed to have the entire packet in the - // buffer. - - *ppucPacketBodyRV = pucPacketBody = &pucPacket[RFL_PACKET_OVERHEAD]; - - // Validate the packet checksum - - if (RflCalcChecksum( pucPacket, uiEncryptPacketBodyLen) != - pucPacket[RFL_PACKET_CHECKSUM_OFFSET]) - { - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - - if (uiPacketType == RFL_TRNS_BEGIN_PACKET || - uiPacketType == RFL_TRNS_BEGIN_EX_PACKET || - uiPacketType == RFL_UPGRADE_PACKET || - uiPacketType == RFL_REDUCE_PACKET || - uiPacketType == RFL_WRAP_KEY_PACKET || - uiPacketType == RFL_ENABLE_ENCRYPTION_PACKET) - { - - // Current transaction ID better be zero, otherwise, we have two or - // more begin packets in a row. - - if (m_uiCurrTransID) - { - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - - m_uiCurrTransID = (FLMUINT) FB2UD( pucPacketBody); - pucPacketBody += 4; - - // Make sure the transaction numbers are ascending - - if (m_uiCurrTransID <= m_uiLastTransID) - { - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - - if (uiPacketType == RFL_TRNS_BEGIN_EX_PACKET) - { - FLMUINT uiLastLoggedCommitTransID; - - // Skip past seconds - - pucPacketBody += 4; - - uiLastLoggedCommitTransID = (FLMUINT) FB2UD( pucPacketBody); - pucPacketBody += 4; - - if (m_pFile->FileHdr.uiVersionNum >= FLM_FILE_FORMAT_VER_4_31 && - m_uiLastLoggedCommitTransID != uiLastLoggedCommitTransID) - { - rc = RC_SET( FERR_RFL_TRANS_GAP); - goto Exit; - } - } - } - else - { - - // If transaction ID is not zero, we are not inside a transaction, - // and it is likely that we have a corrupt packet. - - if (!m_uiCurrTransID) - { - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - - // Decrypt the packet if it is a packet type that was encrypted. - - if (uiPacketType == RFL_TRNS_COMMIT_PACKET || - uiPacketType == RFL_TRNS_ABORT_PACKET) - { - if ((FLMUINT) FB2UD( pucPacketBody) != m_uiCurrTransID) - { - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - } - } - - // Set read offset to beginning of next packet. - - m_uiRflReadOffset += (RFL_PACKET_OVERHEAD + uiEncryptPacketBodyLen); - -Exit: - - return (rc); -} - -/******************************************************************** -Desc: Get a record from the packets in the roll-forward log. - This expects a series of RFL_DATA_RECORD_PACKETs. -*********************************************************************/ -RCODE F_Rfl::getRecord( - FDB * pDb, - FLMUINT uiPacketType, - FLMBYTE * pucPacketBody, - FLMUINT uiPacketBodyLen, - FlmRecord * pRecord) -{ - RCODE rc = FERR_OK; - FLMUINT uiTagNum; - FLMUINT uiDataType; - FLMUINT uiLevel; - FLMUINT uiDataLen; - FLMBYTE * pucFieldData = NULL; - void * pvField; - FLMBOOL bEncrypted = FALSE; - FLMUINT uiEncId = 0; - FLMUINT uiEncDataLen = 0; - POOL pool; - - GedPoolInit( &pool, 512); - - // Go into a loop processing packets until we have retrieved all of - // the fields of the record. At that point, we had better be at the end - // of the record. - - for (;;) - { - - // If we don't currently have a packet, get one Packet type had - // better be RFL_DATA_RECORD_PACKET. - - if (!uiPacketBodyLen) - { - if (RC_BAD( rc = getPacket( FALSE, &uiPacketType, &pucPacketBody, - &uiPacketBodyLen, NULL))) - { - goto Exit; - } - - if (uiPacketType != RFL_DATA_RECORD_PACKET && - uiPacketType != RFL_ENC_DATA_RECORD_PACKET && - uiPacketType != RFL_DATA_RECORD_PACKET_VER_3) - { - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - } - - // Packet body length better be at least two or we have an - // incomplete packet - we need to at least be able to get the tag - // number at this point. - - if (uiPacketBodyLen < 2) - { - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - else if (uiPacketBodyLen == 2) - { - - // If the packet body length is only two, we had better be at - // the end of the record with a tag number of zero. Otherwise, we - // have an incomplete packet. - - if ((uiTagNum = (FLMUINT) FB2UW( pucPacketBody)) != 0) - { - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - break; - } - else if (uiPacketType == RFL_DATA_RECORD_PACKET) - { - flmAssert( m_pFile->FileHdr.uiVersionNum <= FLM_FILE_FORMAT_VER_4_60); - if (uiPacketBodyLen < 6) - { - - // If we have a packet body length less than six (for - // RFL_DATA_RECORD_PACKETs), we have an incomplete field - // header. - - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - } - else if (uiPacketType == RFL_ENC_DATA_RECORD_PACKET) - { - - // This type of packet is only valid with versions of flaim >= - // 4.60 - - flmAssert( m_pFile->FileHdr.uiVersionNum == FLM_FILE_FORMAT_VER_4_60); - - if (uiPacketBodyLen < 7) - { - - // If we have a packet body length less than seven we have an - // incomplete field header. - - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - } - else - { - flmAssert( uiPacketType == RFL_DATA_RECORD_PACKET_VER_3); - flmAssert( m_pFile->FileHdr.uiVersionNum >= FLM_FILE_FORMAT_VER_4_61); - if (uiPacketBodyLen < 9) - { - - // If we have a packet body length less than nine we have an - // incomplete field header. - - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - } - - // At this point, we have a packet body length that is greater than - // or equal to seven (or six), meaning we could not possibly be on - // the last field of the record. Hence, a zero tag number is invalid - // here. - - if ((uiTagNum = (FLMUINT) FB2UW( pucPacketBody)) == 0) - { - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - - pucPacketBody += 2; - uiDataType = *pucPacketBody++; - uiLevel = *pucPacketBody++; - if (uiPacketType != RFL_DATA_RECORD_PACKET_VER_3) - { - uiDataLen = (FLMUINT) FB2UW( pucPacketBody); - pucPacketBody += 2; - uiPacketBodyLen -= 6; - } - else - { - uiDataLen = (FLMUINT) FB2UD( pucPacketBody); - pucPacketBody += 4; - uiPacketBodyLen -= 8; - } - - // If the database version supports encryption, we need to check - // for it. - - if (uiPacketType == RFL_ENC_DATA_RECORD_PACKET) - { - bEncrypted = (FLMBOOL) * pucPacketBody++; - --uiPacketBodyLen; - - if (bEncrypted) - { - if (uiPacketBodyLen < 4) - { - flmAssert( 0); - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - - // Extract the encryption ID and the encrypted length. - - uiEncId = FB2UW( pucPacketBody); - pucPacketBody += 2; - - uiEncDataLen = FB2UW( pucPacketBody); - pucPacketBody += 2; - - uiPacketBodyLen -= 4; - } - } - else if (uiPacketType == RFL_DATA_RECORD_PACKET_VER_3) - { - bEncrypted = (FLMBOOL) * pucPacketBody++; - --uiPacketBodyLen; - - if (bEncrypted) - { - if (uiPacketBodyLen < 6) - { - flmAssert( 0); - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - - // Extract the encryption ID and the encrypted length. - - uiEncId = FB2UW( pucPacketBody); - pucPacketBody += 2; - - uiEncDataLen = FB2UD( pucPacketBody); - pucPacketBody += 4; - - uiPacketBodyLen -= 6; - } - } - - // Create a new field. - - if (RC_BAD( rc = pRecord->insertLast( uiLevel, uiTagNum, uiDataType, - &pvField))) - { - goto Exit; - } - - if (!bEncrypted && uiDataLen) - { - if (RC_BAD( rc = pRecord->allocStorageSpace( pvField, uiDataType, - uiDataLen, 0, 0, 0, &pucFieldData, NULL))) - { - goto Exit; - } - - while (uiDataLen) - { - if (uiDataLen > uiPacketBodyLen) - { - f_memcpy( pucFieldData, pucPacketBody, uiPacketBodyLen); - pucFieldData += uiPacketBodyLen; - pucPacketBody += uiPacketBodyLen; - uiDataLen -= uiPacketBodyLen; - - uiPacketBodyLen = 0; - - // Get the next packet. Packet type had better be - // RFL_DATA_RECORD_PACKET. - - if (RC_BAD( rc = getPacket( FALSE, &uiPacketType, &pucPacketBody, - &uiPacketBodyLen, NULL))) - { - goto Exit; - } - - if (uiPacketType != RFL_DATA_RECORD_PACKET && - uiPacketType != RFL_ENC_DATA_RECORD_PACKET && - uiPacketType != RFL_DATA_RECORD_PACKET_VER_3) - { - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - } - else - { - f_memcpy( pucFieldData, pucPacketBody, uiDataLen); - pucFieldData += uiDataLen; - uiPacketBodyLen -= uiDataLen; - pucPacketBody += uiDataLen; - uiDataLen = 0; - } - } - - pucFieldData = NULL; - } - else if (bEncrypted) - { - FLMBYTE * pucEncFieldData; - - if (uiEncDataLen) - { - if (RC_BAD( rc = pRecord->allocStorageSpace( pvField, uiDataType, - uiDataLen, uiEncDataLen, uiEncId, - FLD_HAVE_ENCRYPTED_DATA, &pucFieldData, &pucEncFieldData - ))) - { - goto Exit; - } - } - - while (uiEncDataLen) - { - if (uiEncDataLen > uiPacketBodyLen) - { - f_memcpy( pucEncFieldData, pucPacketBody, uiPacketBodyLen); - pucEncFieldData += uiPacketBodyLen; - pucPacketBody += uiPacketBodyLen; - uiEncDataLen -= uiPacketBodyLen; - - uiPacketBodyLen = 0; - - // Get the next packet. Packet type had better be - // RFL_ENC_DATA_RECORD_PACKET. - - if (RC_BAD( rc = getPacket( FALSE, &uiPacketType, &pucPacketBody, - &uiPacketBodyLen, NULL))) - { - goto Exit; - } - - if (uiPacketType != RFL_ENC_DATA_RECORD_PACKET && - uiPacketType != RFL_DATA_RECORD_PACKET_VER_3) - { - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - } - else - { - f_memcpy( pucEncFieldData, pucPacketBody, uiEncDataLen); - pucEncFieldData += uiEncDataLen; - uiPacketBodyLen -= uiEncDataLen; - pucPacketBody += uiEncDataLen; - uiEncDataLen = 0; - } - } - - pucEncFieldData = NULL; - - if (!m_pFile->bInLimitedMode) - { - if (RC_BAD( rc = flmDecryptField( pDb->pDict, pRecord, pvField, - uiEncId, &pool))) - { - goto Exit; - } - } - } - } - -Exit: - - GedPoolFree( &pool); - - return (rc); -} - -/******************************************************************** -Desc: Modify a record using RFL_DATA_RECORD_PACKETs or - RFL_CHANGE_FIELD_PACKETs. -*********************************************************************/ -RCODE F_Rfl::modifyRecord( - HFDB hDb, - FlmRecord * pRecord) -{ - RCODE rc = FERR_OK; - FLMUINT uiPacketType; - FLMBYTE * pucPacketBody; - FLMUINT uiPacketBodyLen; - - FLMUINT uiChangeType; - FLMUINT uiPosition; - FLMUINT uiTagNum = 0; - FLMUINT uiDataType = 0; - FLMUINT uiLevel = 0; - FLMUINT uiDataLen = 0; - FLMBYTE * pucData; - FLMBYTE * pucEncData; - FDB * pDb = (FDB *) hDb; - FLMBOOL bEncrypted = FALSE; - FLMUINT uiEncDataLen; - FLMUINT uiEncId; - FLMUINT uiFlags; - FlmField * pField; - FLMUINT uiCurPos = 1; - void * pvField; - - // Get the first packet and see what it is. If it is an - // RFL_DATA_RECORD_PACKET, just call Rfl3GetRecord to get the entire - // new record. - - if (RC_BAD( rc = getPacket( FALSE, &uiPacketType, &pucPacketBody, - &uiPacketBodyLen, NULL))) - { - goto Exit; - } - - if (uiPacketType == RFL_DATA_RECORD_PACKET || - uiPacketType == RFL_ENC_DATA_RECORD_PACKET || - uiPacketType == RFL_DATA_RECORD_PACKET_VER_3) - { - pRecord->clear(); - rc = getRecord( pDb, uiPacketType, pucPacketBody, uiPacketBodyLen, pRecord); - goto Exit; - } - else if (uiPacketType != RFL_CHANGE_FIELDS_PACKET) - { - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - - // Go into a loop processing packets until we have processed all of - // the changed fields for the record. - - pField = pRecord->getFieldPointer( pRecord->root()); - - flmAssert( pField); - - for (;;) - { - uiEncDataLen = 0; - uiEncId = 0; - - // If we don't currently have a packet, get one Packet type had - // better be RFL_CHANGE_FIELDS_PACKET. - - if (!uiPacketBodyLen) - { - if (RC_BAD( rc = getPacket( FALSE, &uiPacketType, &pucPacketBody, - &uiPacketBodyLen, NULL))) - { - goto Exit; - } - - if (uiPacketType != RFL_CHANGE_FIELDS_PACKET) - { - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - } - - // Packet body length better be at least three or we have an - // incomplete packet - we need to at least be able to get the type - // of change and the absolute position of the change. - - if (uiPacketBodyLen < 3) - { - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - - // Get the change type and the absolute position where the change - // is to be put. A position of zero is illegal. - - uiChangeType = *pucPacketBody++; - uiPosition = (FLMUINT) FB2UW( pucPacketBody); - pucPacketBody += 2; - uiPacketBodyLen -= 3; - - if (uiChangeType == RFL_END_FIELD_CHANGES) - { - - // If we are not at the end of the packet, it must be a bad - // packet. Also, uiPosition should be a zero for this packet. - - if (uiPacketBodyLen || uiPosition) - { - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - break; - } - - // If not RFL_END_FIELD_CHANGES, a position of zero is illegal. - - if (!uiPosition) - { - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - - switch (uiChangeType) - { - case RFL_INSERT_FIELD: - { - flmAssert( m_pFile->FileHdr.uiVersionNum <= FLM_FILE_FORMAT_VER_4_60); - bEncrypted = FALSE; - if (uiPacketBodyLen < 6) - { - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - - uiTagNum = (FLMUINT) FB2UW( pucPacketBody); - pucPacketBody += 2; - uiDataType = *pucPacketBody++; - uiLevel = *pucPacketBody++; - uiDataLen = (FLMUINT)FB2UW( pucPacketBody); - pucPacketBody += 2; - uiPacketBodyLen -= 6; - break; - } - case RFL_INSERT_ENC_FIELD: - { - flmAssert( m_pFile->FileHdr.uiVersionNum == FLM_FILE_FORMAT_VER_4_60); - bEncrypted = TRUE; - if (uiPacketBodyLen < 10) - { - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - - uiTagNum = (FLMUINT) FB2UW( pucPacketBody); - pucPacketBody += 2; - uiDataType = *pucPacketBody++; - uiLevel = *pucPacketBody++; - uiDataLen = (FLMUINT)FB2UW( pucPacketBody); - pucPacketBody += 2; - - uiEncId = FB2UW( pucPacketBody); - pucPacketBody += 2; - - uiEncDataLen = FB2UW( pucPacketBody); - pucPacketBody += 2; - - uiPacketBodyLen -= 10; - break; - } - case RFL_INSERT_LARGE_FIELD: - { - flmAssert( m_pFile->FileHdr.uiVersionNum >= FLM_FILE_FORMAT_VER_4_61); - bEncrypted = FALSE; - if (uiPacketBodyLen < 8) - { - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - uiTagNum = (FLMUINT) FB2UW( pucPacketBody); - pucPacketBody += 2; - uiDataType = *pucPacketBody++; - uiLevel = *pucPacketBody++; - uiDataLen = (FLMUINT)FB2UD( pucPacketBody); - pucPacketBody += 4; - uiPacketBodyLen -= 8; - break; - } - case RFL_INSERT_ENC_LARGE_FIELD: - { - flmAssert( m_pFile->FileHdr.uiVersionNum >= FLM_FILE_FORMAT_VER_4_61); - bEncrypted = TRUE; - if (uiPacketBodyLen < 14) - { - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - uiTagNum = (FLMUINT) FB2UW( pucPacketBody); - pucPacketBody += 2; - uiDataType = *pucPacketBody++; - uiLevel = *pucPacketBody++; - uiDataLen = (FLMUINT)FB2UD( pucPacketBody); - pucPacketBody += 4; - - uiEncId = FB2UW( pucPacketBody); - pucPacketBody += 2; - - uiEncDataLen = FB2UW( pucPacketBody); - pucPacketBody += 4; - - uiPacketBodyLen -= 14; - break; - } - - case RFL_MODIFY_FIELD: - { - flmAssert( m_pFile->FileHdr.uiVersionNum <= FLM_FILE_FORMAT_VER_4_60); - bEncrypted = FALSE; - if (uiPacketBodyLen < 3 || *pucPacketBody != RFL_REPLACE_BYTES) - { - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - - pucPacketBody++; - uiDataLen = (FLMUINT) FB2UW( pucPacketBody); - pucPacketBody += 2; - uiPacketBodyLen -= 3; - break; - } - - case RFL_MODIFY_ENC_FIELD: - { - flmAssert( m_pFile->FileHdr.uiVersionNum == FLM_FILE_FORMAT_VER_4_60); - bEncrypted = TRUE; - if (uiPacketBodyLen < 7 || *pucPacketBody != RFL_REPLACE_BYTES) - { - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - - pucPacketBody++; - uiDataLen = (FLMUINT) FB2UW( pucPacketBody); - pucPacketBody += 2; - - uiEncId = FB2UW( pucPacketBody); - pucPacketBody += 2; - - uiEncDataLen = FB2UW( pucPacketBody); - pucPacketBody += 2; - - uiPacketBodyLen -= 7; - break; - } - case RFL_MODIFY_LARGE_FIELD: - { - flmAssert( m_pFile->FileHdr.uiVersionNum >= FLM_FILE_FORMAT_VER_4_61); - bEncrypted = FALSE; - if (uiPacketBodyLen < 5 || *pucPacketBody != RFL_REPLACE_BYTES) - { - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - pucPacketBody++; - uiDataLen = (FLMUINT)FB2UD( pucPacketBody); - pucPacketBody += 4; - uiPacketBodyLen -= 5; - break; - } - case RFL_MODIFY_ENC_LARGE_FIELD: - { - flmAssert( m_pFile->FileHdr.uiVersionNum >= FLM_FILE_FORMAT_VER_4_61); - bEncrypted = TRUE; - if (uiPacketBodyLen < 11 || *pucPacketBody != RFL_REPLACE_BYTES) - { - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - - pucPacketBody++; - uiDataLen = (FLMUINT)FB2UD( pucPacketBody); - pucPacketBody += 4; - - uiEncId = FB2UW( pucPacketBody); - pucPacketBody += 2; - - uiEncDataLen = FB2UD( pucPacketBody); - pucPacketBody += 4; - - uiPacketBodyLen -= 11; - break; - } - - case RFL_DELETE_FIELD: - { - break; - } - - default: - { - - // Bad change type in packet. - - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - } - - // Now position to the target field. - - switch (uiChangeType) - { - case RFL_DELETE_FIELD: - case RFL_MODIFY_FIELD: - case RFL_MODIFY_ENC_FIELD: - case RFL_MODIFY_LARGE_FIELD: - case RFL_MODIFY_ENC_LARGE_FIELD: - { - while (uiCurPos != uiPosition) - { - if (uiPosition < uiCurPos) - { - flmAssert( pField->uiPrev); - pField = pRecord->prevField( pField); - --uiCurPos; - } - else - { - flmAssert( pField->uiNext); - pField = pRecord->nextField( pField); - uiCurPos++; - } - } - - if (uiChangeType != RFL_DELETE_FIELD) - { - - // Get the data type ... not supplied in the modify field - // packet. - - uiDataType = pRecord->getFieldDataType( pField); - uiTagNum = pField->ui16FieldID; - } - break; - } - - case RFL_INSERT_FIELD: - case RFL_INSERT_ENC_FIELD: - case RFL_INSERT_LARGE_FIELD: - case RFL_INSERT_ENC_LARGE_FIELD: - { - FlmField * pNewField; - - // On insert, we may be trying to position to a field that - // does not exist yet. Therefore we need to position to the - // field prior to the field position we want to insert. - - flmAssert( uiPosition > 1); // cannot insert at the root - ///position. - - while (uiCurPos != uiPosition - 1) - { - if (uiPosition - 1 < uiCurPos) - { - flmAssert( pField->uiPrev); - pField = pRecord->prevField( pField); - --uiCurPos; - } - else - { - flmAssert( pField->uiNext); - pField = pRecord->nextField( pField); - uiCurPos++; - } - } - - // Insert the new field at the specified position and get - // back a new field to use later. - - if (RC_BAD( rc = pRecord->createField( pField, &pNewField))) - { - goto Exit; - } - - if (RC_BAD( rc = pRecord->setFieldLevel( pNewField, uiLevel))) - { - goto Exit; - } - - pField = pNewField; - pField->ui16FieldID = (FLMUINT16) uiTagNum; - uiCurPos++; // Bump the position as we have - ///just added a new field and - - // we are positioned on it. - - break; - } - } - - if (uiChangeType == RFL_DELETE_FIELD) - { - - // Remove the specified field or subtree - - pvField = (void *) ((FLMUINT) (pField->uiPrev)); - --uiCurPos; - if (!pvField) - { - pvField = pRecord->root(); - uiCurPos = 1; - } - - // For versions 4.60 and greater, the interpretation for - // RFL_DELETE_FIELD is to delete the entire sub-tree. Prior to - // that, it is to delete only a single field. - - if (m_pFile->FileHdr.uiVersionNum >= FLM_FILE_FORMAT_VER_4_60) - { - if (RC_BAD( rc = pRecord->remove( pField))) - { - goto Exit; - } - } - else - { - - // Passing in the same pointer to removeFields will - // effectively delete just pField. - - if (RC_BAD( rc = pRecord->removeFields( pField, pField))) - { - goto Exit; - } - } - - // We need to reset our pField. - - pField = pRecord->getFieldPointer( pvField); - - continue; // Next field... - } - - // Both insert & modify need to have space allocated. - - if (bEncrypted) - { - uiFlags = FLD_HAVE_ENCRYPTED_DATA; - } - else - { - uiFlags = 0; - } - - flmAssert( pField); - - // Allocate space for the data. We call this even if uiDataLen is - // zero so that the appropriate data type will be set in the node as - // well. ; - // Before we allocate storage space, save the field offset. The - // field buffer may get reallocated, resulting in a nwe address for - // pField. - pvField = pRecord->getFieldVoid( pField); - if (RC_BAD( rc = pRecord->getNewDataPtr( pField, uiDataType, uiDataLen, - uiEncDataLen, uiEncId, uiFlags, &pucData, &pucEncData))) - { - goto Exit; - } - - pField = pRecord->getFieldPointer( pvField); - - // Get the data for insert or modify, if any - - if (bEncrypted) - { - while (uiEncDataLen) - { - if (uiEncDataLen > uiPacketBodyLen) - { - f_memcpy( pucEncData, pucPacketBody, uiPacketBodyLen); - pucEncData += uiPacketBodyLen; - uiEncDataLen -= uiPacketBodyLen; - uiPacketBodyLen = 0; - - // Get the next packet. Packet type had better be - // RFL_CHANGE_FIELDS_PACKET. - - if (RC_BAD( rc = getPacket( FALSE, &uiPacketType, &pucPacketBody, - &uiPacketBodyLen, NULL))) - { - goto Exit; - } - - if (uiPacketType != RFL_CHANGE_FIELDS_PACKET) - { - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - } - else - { - f_memcpy( pucEncData, pucPacketBody, uiEncDataLen); - pucEncData += uiEncDataLen; - uiPacketBodyLen -= uiEncDataLen; - pucPacketBody += uiEncDataLen; - uiEncDataLen = 0; - } - } - } - else // Not encrypted - { - while (uiDataLen) - { - if (uiDataLen > uiPacketBodyLen) - { - f_memcpy( pucData, pucPacketBody, uiPacketBodyLen); - pucData += uiPacketBodyLen; - uiDataLen -= uiPacketBodyLen; - uiPacketBodyLen = 0; - - // Get the next packet. Packet type had better be - // RFL_CHANGE_FIELDS_PACKET. - - if (RC_BAD( rc = getPacket( FALSE, &uiPacketType, &pucPacketBody, - &uiPacketBodyLen, NULL))) - { - goto Exit; - } - - if (uiPacketType != RFL_CHANGE_FIELDS_PACKET) - { - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - } - else - { - f_memcpy( pucData, pucPacketBody, uiDataLen); - pucData += uiDataLen; - uiPacketBodyLen -= uiDataLen; - pucPacketBody += uiDataLen; - uiDataLen = 0; - } - } - } - - // If this field is involved in an index, and it is encrypted, we - // need to make sure we decrypt it too. If it is not encrypted, we - // don't care if it involved in an index. - - if (bEncrypted && !(pDb->pFile->bInLimitedMode)) - { - IFD * pIfd; - - if (RC_BAD( rc = fdictGetField( pDb->pDict, uiTagNum, NULL, &pIfd, NULL))) - { - goto Exit; - } - - if (pIfd) - { - if (RC_BAD( rc = flmDecryptField( pDb->pDict, pRecord, - pRecord->getFieldVoid( pField), uiEncId, &pDb->TempPool))) - { - goto Exit; - } - } - } - } - -Exit: - - return (rc); -} - -/******************************************************************** -Desc: Read the next operation from the roll-forward log. -*********************************************************************/ -RCODE F_Rfl::readOp( - FDB * pDb, - FLMBOOL bForceNextFile, - RFL_OP_INFO * pOpInfo, - FlmRecord * pRecord) -{ - RCODE rc = FERR_OK; - FLMBYTE * pucPacketBody; - FLMUINT uiPacketBodyLen; - FLMUINT uiExpectedBodyLen; - FLMBOOL bLoggedTimes; - - f_memset( pOpInfo, 0, sizeof( RFL_OP_INFO)); - - // Get the next packet. - - if (RC_BAD( rc = getPacket( bForceNextFile, &pOpInfo->uiPacketType, - &pucPacketBody, &uiPacketBodyLen, &bLoggedTimes))) - { - goto Exit; - } - - // Must be one of our packet types that represents an operation. - - switch (pOpInfo->uiPacketType) - { - case RFL_TRNS_BEGIN_PACKET: - { - uiExpectedBodyLen = 8; - if (bLoggedTimes) - { - uiExpectedBodyLen += 4; - } - - if (uiExpectedBodyLen != uiPacketBodyLen) - { - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - - pOpInfo->uiTransId = (FLMUINT) FB2UD( pucPacketBody); - pucPacketBody += 4; - pOpInfo->uiStartTime = (FLMUINT) FB2UD( pucPacketBody); - pucPacketBody += 4; - break; - } - - case RFL_TRNS_BEGIN_EX_PACKET: - { - uiExpectedBodyLen = 12; - if (uiExpectedBodyLen != uiPacketBodyLen) - { - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - - pOpInfo->uiTransId = (FLMUINT) FB2UD( pucPacketBody); - pucPacketBody += 4; - pOpInfo->uiStartTime = (FLMUINT) FB2UD( pucPacketBody); - pucPacketBody += 4; - pOpInfo->uiLastLoggedCommitTransId = (FLMUINT) FB2UD( pucPacketBody); - pucPacketBody += 4; - break; - } - - case RFL_TRNS_COMMIT_PACKET: - case RFL_TRNS_ABORT_PACKET: - { - uiExpectedBodyLen = 8; - if (bLoggedTimes) - { - uiExpectedBodyLen += 8; - } - - if (uiExpectedBodyLen != uiPacketBodyLen) - { - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - - pOpInfo->uiTransId = (FLMUINT) FB2UD( pucPacketBody); - pucPacketBody += 8; - break; - } - - case RFL_ADD_RECORD_PACKET: - case RFL_MODIFY_RECORD_PACKET: - case RFL_DELETE_RECORD_PACKET: - case RFL_RESERVE_DRN_PACKET: - { - uiExpectedBodyLen = 10; - if (bLoggedTimes) - { - uiExpectedBodyLen += 16; - } - - if (uiExpectedBodyLen != uiPacketBodyLen) - { - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - - if ((pOpInfo->uiTransId = - (FLMUINT) FB2UD( pucPacketBody)) != m_uiCurrTransID) - { - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - pucPacketBody += 4; - - pOpInfo->uiContainer = (FLMUINT) FB2UW( pucPacketBody); - pucPacketBody += 2; - - pOpInfo->uiDrn = (FLMUINT) FB2UD( pucPacketBody); - pucPacketBody += 4; - - if (pOpInfo->uiPacketType == RFL_ADD_RECORD_PACKET) - { - if (RC_BAD( rc = getRecord( pDb, 0, NULL, 0, pRecord))) - { - goto Exit; - } - } - break; - } - - case RFL_ADD_RECORD_PACKET_VER_2: - case RFL_MODIFY_RECORD_PACKET_VER_2: - case RFL_DELETE_RECORD_PACKET_VER_2: - { - uiExpectedBodyLen = 11; - if (bLoggedTimes) - { - uiExpectedBodyLen += 16; - } - - if (uiExpectedBodyLen != uiPacketBodyLen) - { - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - - if ((pOpInfo->uiTransId = - (FLMUINT) FB2UD( pucPacketBody)) != m_uiCurrTransID) - { - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - pucPacketBody += 4; - - pOpInfo->uiContainer = (FLMUINT) FB2UW( pucPacketBody); - pucPacketBody += 2; - - pOpInfo->uiDrn = (FLMUINT) FB2UD( pucPacketBody); - pucPacketBody += 4; - - pOpInfo->uiFlags = *pucPacketBody; - pucPacketBody++; - - // Translate the flags - - if (pOpInfo->uiFlags) - { - FLMUINT uiTmp = 0; - - if (pOpInfo->uiFlags & RFL_UPDATE_BACKGROUND) - { - uiTmp |= FLM_DO_IN_BACKGROUND; - } - - if (pOpInfo->uiFlags & RFL_UPDATE_SUSPENDED) - { - uiTmp |= FLM_SUSPENDED; - } - - pOpInfo->uiFlags = uiTmp; - } - - if (pOpInfo->uiPacketType == RFL_ADD_RECORD_PACKET_VER_2) - { - if (RC_BAD( rc = getRecord( pDb, 0, NULL, 0, pRecord))) - { - goto Exit; - } - } - break; - } - - case RFL_INDEX_SET_PACKET: - case RFL_INDEX_SET_PACKET_VER_2: - { - uiExpectedBodyLen = - (FLMUINT) ((pOpInfo->uiPacketType == RFL_INDEX_SET_PACKET_VER_2) - ? (FLMUINT) 16 - : (FLMUINT) 14); - - if (bLoggedTimes) - { - uiExpectedBodyLen += 16; - } - - if (uiExpectedBodyLen != uiPacketBodyLen) - { - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - - if ((pOpInfo->uiTransId = - (FLMUINT) FB2UD( pucPacketBody)) != m_uiCurrTransID) - { - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - pucPacketBody += 4; - - if (pOpInfo->uiPacketType == RFL_INDEX_SET_PACKET_VER_2) - { - pOpInfo->uiContainer = (FLMUINT) FB2UW( pucPacketBody); - pucPacketBody += 2; - } - - pOpInfo->uiIndex = (FLMUINT) FB2UW( pucPacketBody); - pucPacketBody += 2; - - pOpInfo->uiDrn = (FLMUINT) FB2UD( pucPacketBody); - pucPacketBody += 4; - - pOpInfo->uiEndDrn = (FLMUINT) FB2UD( pucPacketBody); - pucPacketBody += 4; - - break; - } - - case RFL_START_UNKNOWN_PACKET: - { - uiExpectedBodyLen = 4; - if (uiExpectedBodyLen != uiPacketBodyLen) - { - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - - if ((pOpInfo->uiTransId = - (FLMUINT) FB2UD( pucPacketBody)) != m_uiCurrTransID) - { - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - - pucPacketBody += 4; - break; - } - - case RFL_REDUCE_PACKET: - { - uiExpectedBodyLen = 8; - if (uiExpectedBodyLen != uiPacketBodyLen) - { - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - - pOpInfo->uiTransId = (FLMUINT) FB2UD( pucPacketBody); - pOpInfo->uiLastLoggedCommitTransId = pOpInfo->uiTransId; - pucPacketBody += 4; - - pOpInfo->uiCount = (FLMUINT) FB2UD( pucPacketBody); - pucPacketBody += 4; - - break; - } - - case RFL_BLK_CHAIN_FREE_PACKET: - { - uiExpectedBodyLen = 16; - - if (bLoggedTimes) - { - uiExpectedBodyLen += 16; - } - - if (uiExpectedBodyLen != uiPacketBodyLen) - { - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - - if ((pOpInfo->uiTransId = - (FLMUINT) FB2UD( pucPacketBody)) != m_uiCurrTransID) - { - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - - pucPacketBody += 4; - - // Tracker record ID - - pOpInfo->uiDrn = (FLMUINT) FB2UD( pucPacketBody); - pucPacketBody += 4; - - // Count - - pOpInfo->uiCount = (FLMUINT) FB2UD( pucPacketBody); - pucPacketBody += 4; - - // Ending block address - - pOpInfo->uiEndBlock = (FLMUINT) FB2UD( pucPacketBody); - pucPacketBody += 4; - - break; - } - - case RFL_INDEX_SUSPEND_PACKET: - case RFL_INDEX_RESUME_PACKET: - { - uiExpectedBodyLen = 6; - if (uiExpectedBodyLen != uiPacketBodyLen) - { - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - - if ((pOpInfo->uiTransId = - (FLMUINT) FB2UD( pucPacketBody)) != m_uiCurrTransID) - { - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - - pucPacketBody += 4; - - pOpInfo->uiIndex = (FLMUINT) FB2UW( pucPacketBody); - pucPacketBody += 2; - break; - } - - case RFL_UPGRADE_PACKET: - { - FLMUINT uiDBKeyLen; - - uiExpectedBodyLen = 12; - if (uiExpectedBodyLen > uiPacketBodyLen) - { - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - - pOpInfo->uiTransId = (FLMUINT) FB2UD( pucPacketBody); - pucPacketBody += 4; - uiPacketBodyLen -= 4; - - pOpInfo->uiOldVersion = (FLMUINT) FB2UD( pucPacketBody); - pucPacketBody += 4; - uiPacketBodyLen -= 4; - - pOpInfo->uiNewVersion = (FLMUINT) FB2UD( pucPacketBody); - pucPacketBody += 4; - uiPacketBodyLen -= 4; - - // Only look for the wrapping key if the new database version is - // greater than 4.60 and there isn't already a key. - - if (pOpInfo->uiEndDrn >= FLM_FILE_FORMAT_VER_4_60 && - !m_pFile->pDbWrappingKey) - { - if (uiPacketBodyLen < 2) - { - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - - uiDBKeyLen = FB2UW( pucPacketBody); - pucPacketBody += 2; - uiPacketBodyLen -= 2; - - if (uiDBKeyLen) - { - if (uiPacketBodyLen != uiDBKeyLen) - { - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - - if ((m_pFile->pDbWrappingKey = f_new F_CCS) == NULL) - { - rc = RC_SET( FERR_MEM); - goto Exit; - } - - if (RC_BAD( rc = m_pFile->pDbWrappingKey->init( TRUE, - FLM_NICI_AES))) - { - goto Exit; - } - - if (RC_BAD( rc = m_pFile->pDbWrappingKey->setKeyFromStore( - pucPacketBody, (FLMUINT32) uiDBKeyLen, NULL, NULL, - FALSE))) - { - goto Exit; - } - - pucPacketBody += uiDBKeyLen; - uiPacketBodyLen -= uiDBKeyLen; - flmAssert( !uiPacketBodyLen); - } - } - break; - } - - case RFL_CONFIG_SIZE_EVENT_PACKET: - { - uiExpectedBodyLen = 16; - if (uiExpectedBodyLen != uiPacketBodyLen) - { - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - - pOpInfo->uiTransId = (FLMUINT) FB2UD( pucPacketBody); - pOpInfo->uiLastLoggedCommitTransId = pOpInfo->uiTransId; - pucPacketBody += 4; - - pOpInfo->uiSizeThreshold = (FLMUINT) FB2UD( pucPacketBody); - pucPacketBody += 4; - - pOpInfo->uiTimeInterval = (FLMUINT) FB2UD( pucPacketBody); - pucPacketBody += 4; - - pOpInfo->uiSizeInterval = (FLMUINT) FB2UD( pucPacketBody); - pucPacketBody += 4; - - break; - } - - case RFL_WRAP_KEY_PACKET: - case RFL_ENABLE_ENCRYPTION_PACKET: - { - FLMUINT uiDBKeyLen; - FLMBYTE * pucUncommittedLogHdr = &m_pFile->ucUncommittedLogHdr[0]; - eRestoreActionType eRestoreAction; - - uiExpectedBodyLen = 6; - if (uiExpectedBodyLen >= uiPacketBodyLen) - { - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - - pOpInfo->uiTransId = (FLMUINT) FB2UD( pucPacketBody); - pOpInfo->uiLastLoggedCommitTransId = pOpInfo->uiTransId; - pucPacketBody += 4; - uiPacketBodyLen -= 4; - - if (uiPacketBodyLen < 2) - { - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - - uiDBKeyLen = FB2UW( pucPacketBody); - pucPacketBody += 2; - uiPacketBodyLen -= 2; - - if (m_pRestore) - { - if (RC_BAD( rc = m_pRestore->status( - pOpInfo->uiPacketType == RFL_WRAP_KEY_PACKET - ? RESTORE_WRAP_KEY - : RESTORE_ENABLE_ENCRYPTION, - pOpInfo->uiTransId, (void *) uiDBKeyLen, (void *) 0, - (void *) 0, &eRestoreAction))) - { - goto Exit; - } - - if (eRestoreAction == RESTORE_ACTION_STOP) - { - m_uiCurrTransID = 0; - break; - } - } - - if (uiDBKeyLen) - { - if (uiPacketBodyLen != uiDBKeyLen) - { - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - - // We cannot directly set the key at this point as it may be - // encrypted using a password, which we do not have here. We - // will write the key out to the log header and trust the user - // to know whether or not a password is needed to open the - // database. - - if (RC_BAD( rc = flmBeginDbTrans( pDb, FLM_UPDATE_TRANS, 0, 0))) - { - goto Exit; - } - - f_memcpy( &pucUncommittedLogHdr[ LOG_DATABASE_KEY], pucPacketBody, - uiDBKeyLen); - - UW2FBA( uiDBKeyLen, &pucUncommittedLogHdr[ LOG_DATABASE_KEY_LEN]); - - if (RC_BAD( rc = flmCommitDbTrans( pDb, 0, TRUE))) - { - goto Exit; - } - - pucPacketBody += uiDBKeyLen; - uiPacketBodyLen -= uiDBKeyLen; - flmAssert( !uiPacketBodyLen); - } - - m_uiCurrTransID = 0; - break; - } - - default: - { - flmAssert( 0); - rc = RC_SET( FERR_BAD_RFL_PACKET); - break; - } - } - -Exit: - - return (rc); -} - -/******************************************************************** -Desc: Reads through unknown packets. -*********************************************************************/ -RCODE F_Rfl::readUnknown( - FLMUINT uiLenToRead, - FLMBYTE * pucBuffer, - FLMUINT * puiBytesRead) -{ - RCODE rc = FERR_OK; - FLMUINT uiPacketType; - FLMUINT uiBytesRead = 0; - FLMUINT uiBytesToCopy; - - // If we have read through all of the unknown packets, return - // FERR_EOF_HIT. - - if (!m_bReadingUnknown) - { - rc = RC_SET( FERR_EOF_HIT); - goto Exit; - } - - // Process packets until we have satisfied the read request or until - // we run out of unknown packets. - - while (uiLenToRead) - { - - // Get a packet, if we don't have one. - - if (!m_uiUnknownPacketBodyLen) - { - if (RC_BAD( rc = getPacket( FALSE, &uiPacketType, - &m_pucUnknownPacketBody, &m_uiUnknownPacketBodyLen, NULL))) - { - m_bReadingUnknown = FALSE; - m_uiUnknownPacketRc = rc; - goto Exit; - } - - if (uiPacketType != RFL_UNKNOWN_PACKET) - { - if (!uiBytesRead) - { - rc = RC_SET( FERR_EOF_HIT); - } - - m_bReadingUnknown = FALSE; - - // At this point, we know that the entire packet is inside - // our memory buffer, so it is safe to reset m_uiRflReadOffset - // back to the beginning of the packet. The call to readOp() - // will call getPacket again, which will get this exact same - // packet for processing. - - m_uiRflReadOffset -= (RFL_PACKET_OVERHEAD + m_uiUnknownPacketBodyLen); - goto Exit; - } - - m_uiUnknownBodyLenProcessed = 0; - } - - uiBytesToCopy = uiLenToRead; - if (uiBytesToCopy > m_uiUnknownPacketBodyLen - - m_uiUnknownBodyLenProcessed) - { - uiBytesToCopy = m_uiUnknownPacketBodyLen - m_uiUnknownBodyLenProcessed; - } - - f_memcpy( pucBuffer, m_pucUnknownPacketBody + m_uiUnknownBodyLenProcessed, - uiBytesToCopy); - pucBuffer += uiBytesToCopy; - uiLenToRead -= uiBytesToCopy; - uiBytesRead += uiBytesToCopy; - m_uiUnknownBodyLenProcessed += uiBytesToCopy; - - // If we have exhausted the current packet, reset things so that we - // will get a new packet the next time around. - - if (m_uiUnknownBodyLenProcessed == m_uiUnknownPacketBodyLen) - { - m_uiUnknownPacketBodyLen = 0; - m_uiUnknownBodyLenProcessed = 0; - m_pucUnknownPacketBody = NULL; - } - } - -Exit: - - *puiBytesRead = uiBytesRead; - return (rc); -} - -/******************************************************************** -Desc: Restore transactions from the roll-forward log to the - database. -*********************************************************************/ -RCODE F_Rfl::recover( - FDB * pDb, - F_Restore * pRestore) -{ - RCODE rc = FERR_OK; - HFDB hDb = (HFDB) pDb; - FLMUINT uiStartFileNum; - FLMUINT uiStartOffset; - FLMUINT uiOffset; - FLMUINT uiReadLen; - FLMUINT uiBytesRead; - FLMBYTE ucHdr[ 512]; - FLMUINT uiCount; - RFL_OP_INFO opInfo; - FlmRecord * pRecord = NULL; - FlmRecord * pTmpRecord = NULL; - eRestoreActionType eRestoreAction; - FLMBOOL bTransActive = FALSE; - FLMBOOL bHadOperations = FALSE; - FLMBOOL bLastTransEndedAtFileEOF = FALSE; - FLMBOOL bForceNextFile; - - flmAssert( m_pFile); - - m_pCurrentBuf = &m_Buf1; - m_uiLastLoggedCommitTransID = 0; - - // We need to allow all updates logged in the RFL (including - // dictionary updates). - - pDb->bFldStateUpdOk = TRUE; - - // If we are less than version 4.3, we cannot do restore. - - if (pRestore && m_pFile->FileHdr.uiVersionNum < FLM_FILE_FORMAT_VER_4_3) - { - goto Exit; - } - - // Turn off logging. - - m_bLoggingOff = TRUE; - - // Set the replay flag on the database. - - pDb->uiFlags |= FDB_REPLAYING_RFL; - - // Set the flag as to whether or not we are using multiple RFL files. - - if (m_pFile->FileHdr.uiVersionNum < FLM_FILE_FORMAT_VER_4_3) - { - m_bKeepRflFiles = FALSE; - } - else - { - m_bKeepRflFiles = m_pFile->ucLastCommittedLogHdr[LOG_KEEP_RFL_FILES] - ? TRUE - : FALSE; - } - - // Determine the current, on-disk size of the RFL - - if( m_bKeepRflFiles) - { - FLMUINT64 ui64RflDiskUsage; - - if( RC_BAD( rc = flmRflCalcDiskUsage( m_szRflDir, m_szDbPrefix, - m_pFile->FileHdr.uiVersionNum, &ui64RflDiskUsage))) - { - goto Exit; - } - - f_mutexLock( gv_FlmSysData.hShareMutex); - m_pFile->ui64RflDiskUsage = ui64RflDiskUsage; - f_mutexUnlock( gv_FlmSysData.hShareMutex); - } - - // If pRestore is NULL, we are doing a database recovery after open. - // In that case, we start from the last checkpoint offset and only run - // until the last transaction offset. - - if ((m_pRestore = pRestore) == NULL) - { - FLMBYTE * pucCheckSerialNum; - FLMUINT uiEndOffset; - - uiStartFileNum = (FLMUINT) FB2UD( - &m_pFile->ucLastCommittedLogHdr[ LOG_RFL_LAST_CP_FILE_NUM]); - - m_uiLastRecoverFileNum = (FLMUINT) FB2UD( - &m_pFile->ucLastCommittedLogHdr[ LOG_RFL_FILE_NUM]); - - uiStartOffset = (FLMUINT) FB2UD( - &m_pFile->ucLastCommittedLogHdr[ LOG_RFL_LAST_CP_OFFSET]); - - uiEndOffset = (FLMUINT) FB2UD( - &m_pFile->ucLastCommittedLogHdr[LOG_RFL_LAST_TRANS_OFFSET]); - - // Could be zero if the file was created, but no transactions were - // ever committed to it. - - if (!uiEndOffset) - { - uiEndOffset = 512; - } - - // Start offset better not be less than 512. - - flmAssert( uiStartOffset >= 512); - flmAssert( uiEndOffset >= 512); - - // If start and end are at the same place, there is nothing to - // recover. - - if (uiStartFileNum == m_uiLastRecoverFileNum && - uiStartOffset == uiEndOffset) - { - goto Finish_Recovery; - } - - // We have not recorded the serial number of the last checkpoint - // file number, so we pass in NULL, unless it happens to be the same - // as the last transaction file number, in which case we can pass in - // the serial number we have stored in the log header. - - pucCheckSerialNum = (uiStartFileNum == m_uiLastRecoverFileNum) - ? &m_pFile->ucLastCommittedLogHdr[LOG_LAST_TRANS_RFL_SERIAL_NUM] - : NULL; - - if (RC_BAD( rc = openFile( uiStartFileNum, pucCheckSerialNum))) - { - goto Exit; - } - - // If this is the last RFL file, the EOF is contained in the log - // header. Otherwise, it will be in the RFL file's header, and - // openFile will already have retrieved it. - - if (uiStartFileNum == m_uiLastRecoverFileNum) - { - m_uiFileEOF = uiEndOffset; - } - - // At this point, file EOF better be greater than or equal to 512. - - flmAssert( m_uiFileEOF >= 512); - } - else if (!m_bKeepRflFiles) - { - - // FlmDbRestore should be checking the "keep" flag and not - // attempting to do a restore of the RFL. - - flmAssert( 0); - rc = RC_SET( FERR_CANNOT_RESTORE_RFL_FILES); - goto Exit; - } - else - { - uiStartFileNum = (FLMUINT) FB2UD( - &m_pFile->ucLastCommittedLogHdr[LOG_RFL_FILE_NUM]); - - uiStartOffset = (FLMUINT) FB2UD( - &m_pFile->ucLastCommittedLogHdr[LOG_RFL_LAST_TRANS_OFFSET]); - - // Could be zero if the RFL file had never been created. - - if (!uiStartOffset) - { - uiStartOffset = 512; - } - - // Ask the recovery object to open the file. - -Retry_Open: - - flmAssert( uiStartFileNum); - if (RC_BAD( rc = m_pRestore->openRflFile( uiStartFileNum))) - { - if (rc == FERR_IO_PATH_NOT_FOUND) - { - - // Need to set m_pCurrentBuf->uiCurrFileNum in case the first - // call to openRflFile fails. This will cause the code at the - // Finish_Recovery label to correctly set up the log header. - - if (!uiStartOffset) - { - m_pCurrentBuf->uiCurrFileNum = uiStartFileNum - 1; - } - else - { - m_pCurrentBuf->uiCurrFileNum = uiStartFileNum; - } - - rc = FERR_OK; - goto Finish_Recovery; - } - else - { - goto Exit; - } - } - - // Get the first 512 bytes from the file and verify the header. - - if (RC_BAD( rc = m_pRestore->read( 512, ucHdr, &uiBytesRead))) - { - goto Exit; - } - - if (uiBytesRead < 512) - { - rc = RC_SET( FERR_NOT_RFL); - goto Exit; - } - - if (RC_BAD( rc = verifyHeader( ucHdr, uiStartFileNum, - &m_pFile->ucLastCommittedLogHdr[LOG_LAST_TRANS_RFL_SERIAL_NUM]))) - { - RCODE tmpRc; - - if (RC_BAD( tmpRc = m_pRestore->status( RESTORE_ERROR, 0, - (void *) ((FLMUINT) rc), (void *) 0, (void *) 0, - &eRestoreAction))) - { - rc = tmpRc; - goto Exit; - } - - if (eRestoreAction == RESTORE_ACTION_RETRY) - { - if (RC_BAD( rc = m_pRestore->close())) - { - goto Exit; - } - - goto Retry_Open; - } - - goto Exit; - } - - // We may not know the actual EOF of files during restore - // operations. - - if ((m_uiFileEOF = (FLMUINT) FB2UD( &ucHdr[RFL_EOF_POS])) == 0) - { - bLastTransEndedAtFileEOF = TRUE; - } - else - { - bLastTransEndedAtFileEOF = (m_uiFileEOF == uiStartOffset) - ? TRUE - : FALSE; - } - - // Position to the start offset. Unfortunately, this means reading - // through the data and discarding it. - - uiOffset = 512; - while (uiOffset < uiStartOffset) - { - uiReadLen = (uiStartOffset - uiOffset); - if (uiReadLen > m_uiBufferSize) - { - uiReadLen = m_uiBufferSize; - } - - if (RC_BAD( rc = m_pRestore->read( uiReadLen, - m_pCurrentBuf->pIOBuffer->m_pucBuffer, &uiBytesRead))) - { - goto Exit; - } - - // RFL file is incomplete if we could not read up to the last - // committed transaction. - - if (uiBytesRead < uiReadLen) - { - rc = RC_SET( FERR_RFL_INCOMPLETE); - goto Exit; - } - - uiOffset += uiBytesRead; - } - - // Need to set current file number - - m_pCurrentBuf->uiCurrFileNum = uiStartFileNum; - - // Better not be any transactions to recover - last database state - // needs to be a completed checkpoint. - - flmAssert( - FB2UD( &m_pFile->ucLastCommittedLogHdr [LOG_LAST_CP_TRANS_ID]) == - FB2UD( &m_pFile->ucLastCommittedLogHdr [LOG_CURR_TRANS_ID])); - - // Use uiStartOffset here instead of LOG_RFL_LAST_TRANS_OFFSET, - // because LOG_RFL_LAST_TRANS_OFFSET may be zero, but we in that - // case we should be comparing to 512, and uiStartOffset will have - // been adjusted to 512 if that is the case. - - flmAssert( FB2UD( &m_pFile->ucLastCommittedLogHdr [ - LOG_RFL_LAST_CP_OFFSET]) == uiStartOffset); - - flmAssert( FB2UD( &m_pFile->ucLastCommittedLogHdr[ - LOG_RFL_LAST_CP_FILE_NUM]) == uiStartFileNum); - } - - // Set last transaction ID to the last transaction that was - // checkpointed - transaction numbers should ascend from here. - - m_uiLastTransID = (FLMUINT) FB2UD( - &m_pFile->ucLastCommittedLogHdr[LOG_LAST_CP_TRANS_ID]); - - // Set the last committed trans ID if this is a 4.31+ database - - if (m_pFile->FileHdr.uiVersionNum >= FLM_FILE_FORMAT_VER_4_31) - { - m_uiLastLoggedCommitTransID = (FLMUINT) FB2UD( - &m_pFile->ucLastCommittedLogHdr[LOG_LAST_RFL_COMMIT_ID]); - } - - m_pCurrentBuf->uiRflFileOffset = uiStartOffset; - m_uiRflReadOffset = 0; - m_pCurrentBuf->uiRflBufBytes = 0; - - // Now, read until we are done. - - bForceNextFile = FALSE; - for (;;) - { - if (!pRecord) - { - if ((pRecord = f_new FlmRecord) == NULL) - { - rc = RC_SET( FERR_MEM); - goto Exit; - } - } - - // Get the next operation from the file. - - rc = readOp( pDb, bForceNextFile, &opInfo, pRecord); - bForceNextFile = FALSE; - - if (RC_BAD( rc)) - { -Handle_Packet_Error: - - if (rc == FERR_END) - { - if (!m_pRestore) - { - - // If we didn't end exactly where we should have, we have - // an incomplete log. The same is true if we are in the - // middle of a transaction. - - if (m_pCurrentBuf->uiCurrFileNum != m_uiLastRecoverFileNum || - bTransActive) - { - rc = RC_SET( FERR_RFL_INCOMPLETE); - } - else - { - rc = FERR_OK; - goto Finish_Recovery; - } - } - else - { - - // If we are doing a restore, and we get to the end of the - // log, it is OK - even if we are in the middle of a - // transaction - the transaction will simply be aborted. - - rc = FERR_OK; - goto Finish_Recovery; - } - } - else if (rc == FERR_BAD_RFL_PACKET) - { - - // If we don't know the current file size, and we are doing a - // restore, it is OK to end on a bad packet - we will simply - // abort the current transaction, if any. Then, try to go to - // the next file, because we really don't know where this file - // ends. - - if (m_pRestore && !m_uiFileEOF) - { - if (bTransActive) - { - FlmDbTransAbort( hDb); - bTransActive = FALSE; - } - - // Set current transaction ID to zero - as if we had - // encountered an abort packet. - - m_uiCurrTransID = 0; - bLastTransEndedAtFileEOF = TRUE; - - // Force to go to the next file - - bForceNextFile = TRUE; - rc = FERR_OK; - continue; - } - } - - goto Exit; - } - - // At this point, we know we have a good packet, see what it is and - // handle it. - - bHadOperations = TRUE; - switch (opInfo.uiPacketType) - { - case RFL_TRNS_BEGIN_EX_PACKET: - case RFL_TRNS_BEGIN_PACKET: - { - if (m_pRestore) - { - if (RC_BAD( rc = m_pRestore->status( RESTORE_BEGIN_TRANS, - opInfo.uiTransId, (void *) opInfo.uiStartTime, - (void *) 0, (void *) 0, &eRestoreAction))) - { - goto Exit; - } - - if (eRestoreAction == RESTORE_ACTION_STOP) - { - - // Need to set m_uiCurrTransID to 0 since it was set by - // getPacket(). We are not going to start a transaction - // because of the user's request to exit. - - m_uiCurrTransID = 0; - bLastTransEndedAtFileEOF = FALSE; - goto Finish_Recovery; - } - } - - // If we already have a transaction active, we have a problem. - - flmAssert( !bTransActive); - - if (RC_BAD( rc = FlmDbTransBegin( hDb, FLM_UPDATE_TRANS, 0))) - { - goto Exit; - } - - bTransActive = TRUE; - break; - } - - case RFL_TRNS_COMMIT_PACKET: - { - - // Commit the current transaction. - - if (m_pRestore) - { - if (RC_BAD( rc = m_pRestore->status( RESTORE_COMMIT_TRANS, - opInfo.uiTransId, (void *) 0, (void *) 0, (void *) 0, - &eRestoreAction))) - { - goto Exit; - } - - if (eRestoreAction == RESTORE_ACTION_STOP) - { - bLastTransEndedAtFileEOF = FALSE; - goto Finish_Recovery; - } - } - - flmAssert( bTransActive); - pDb->uiFlags |= FDB_REPLAYING_COMMIT; - rc = FlmDbTransCommit( hDb); - pDb->uiFlags &= ~FDB_REPLAYING_COMMIT; - bTransActive = FALSE; - - if (RC_BAD( rc)) - { - goto Exit; - } - - m_uiLastLoggedCommitTransID = opInfo.uiTransId; - -Finish_Transaction: - - if (!m_uiFileEOF) - { - bLastTransEndedAtFileEOF = TRUE; - } - else - { - bLastTransEndedAtFileEOF = - (m_uiRflReadOffset == m_pCurrentBuf->uiRflBufBytes && - m_pCurrentBuf->uiRflFileOffset + - m_pCurrentBuf->uiRflBufBytes == m_uiFileEOF) - ? TRUE - : FALSE; - } - - m_uiLastTransID = opInfo.uiTransId; - m_uiCurrTransID = 0; - break; - } - - case RFL_TRNS_ABORT_PACKET: - { - - // Abort the current transaction. - - if (m_pRestore) - { - if (RC_BAD( rc = m_pRestore->status( RESTORE_ABORT_TRANS, - opInfo.uiTransId, (void *) 0, (void *) 0, (void *) 0, - &eRestoreAction))) - { - goto Exit; - } - - if (eRestoreAction == RESTORE_ACTION_STOP) - { - bLastTransEndedAtFileEOF = FALSE; - goto Finish_Recovery; - } - } - - flmAssert( bTransActive); - rc = FlmDbTransAbort( hDb); - bTransActive = FALSE; - - if (RC_BAD( rc)) - { - goto Exit; - } - - goto Finish_Transaction; - } - - case RFL_ADD_RECORD_PACKET: - case RFL_ADD_RECORD_PACKET_VER_2: - { - if (m_pRestore) - { - if (RC_BAD( rc = m_pRestore->status( RESTORE_ADD_REC, - opInfo.uiTransId, (void *) opInfo.uiContainer, - (void *) opInfo.uiDrn, - (void *) pRecord, &eRestoreAction))) - { - goto Exit; - } - - if (eRestoreAction == RESTORE_ACTION_STOP) - { - bLastTransEndedAtFileEOF = FALSE; - goto Finish_Recovery; - } - } - - rc = FlmRecordAdd( hDb, opInfo.uiContainer, &opInfo.uiDrn, - pRecord, opInfo.uiFlags); - pRecord->Release(); - pRecord = NULL; - if (RC_BAD( rc)) - { - goto Exit; - } - break; - } - - case RFL_MODIFY_RECORD_PACKET: - case RFL_MODIFY_RECORD_PACKET_VER_2: - { - - // Must retrieve the record and then get the modify packet(s) - // to alter it. - - if (RC_BAD( rc = FlmRecordRetrieve( hDb, opInfo.uiContainer, - opInfo.uiDrn, FO_EXACT, &pRecord, NULL))) - { - goto Exit; - } - - if ((pTmpRecord = pRecord->copy()) == NULL) - { - rc = RC_SET( FERR_MEM); - goto Exit; - } - - pRecord->Release(); - pRecord = NULL; - - if (RC_BAD( rc = modifyRecord( hDb, pTmpRecord))) - { - goto Handle_Packet_Error; - } - - // Finally, modify the record in the database. - - if (m_pRestore) - { - if (RC_BAD( rc = m_pRestore->status( RESTORE_MOD_REC, - opInfo.uiTransId, - (void *) opInfo.uiContainer, (void *) opInfo.uiDrn, - (void *) pTmpRecord, &eRestoreAction))) - { - goto Exit; - } - - if (eRestoreAction == RESTORE_ACTION_STOP) - { - bLastTransEndedAtFileEOF = FALSE; - goto Finish_Recovery; - } - } - - rc = FlmRecordModify( hDb, opInfo.uiContainer, opInfo.uiDrn, - pTmpRecord, opInfo.uiFlags); - - pTmpRecord->Release(); - pTmpRecord = NULL; - - if (RC_BAD( rc)) - { - goto Exit; - } - - break; - } - - case RFL_DELETE_RECORD_PACKET: - case RFL_DELETE_RECORD_PACKET_VER_2: - { - if (m_pRestore) - { - if (RC_BAD( rc = m_pRestore->status( RESTORE_DEL_REC, - opInfo.uiTransId, - (void *) opInfo.uiContainer, (void *) opInfo.uiDrn, - (void *) 0, &eRestoreAction))) - { - goto Exit; - } - - if (eRestoreAction == RESTORE_ACTION_STOP) - { - bLastTransEndedAtFileEOF = FALSE; - goto Finish_Recovery; - } - } - - if (RC_BAD( rc = FlmRecordDelete( hDb, opInfo.uiContainer, - opInfo.uiDrn, opInfo.uiFlags))) - { - goto Exit; - } - break; - } - - case RFL_RESERVE_DRN_PACKET: - { - if (m_pRestore) - { - if (RC_BAD( rc = m_pRestore->status( RESTORE_RESERVE_DRN, - opInfo.uiTransId, (void *) opInfo.uiContainer, - (void *) opInfo.uiDrn, (void *) 0, &eRestoreAction))) - { - goto Exit; - } - - if (eRestoreAction == RESTORE_ACTION_STOP) - { - bLastTransEndedAtFileEOF = FALSE; - goto Finish_Recovery; - } - } - - if (RC_BAD( rc = FlmReserveNextDrn( hDb, opInfo.uiContainer, - &opInfo.uiDrn))) - { - goto Exit; - } - - break; - } - - case RFL_INDEX_SUSPEND_PACKET: - { - - if (m_pRestore) - { - if (RC_BAD( rc = m_pRestore->status( RESTORE_INDEX_SUSPEND, - opInfo.uiTransId, (void *) opInfo.uiIndex, - (void *) 0, (void *) 0, &eRestoreAction))) - { - goto Exit; - } - - if (eRestoreAction == RESTORE_ACTION_STOP) - { - bLastTransEndedAtFileEOF = FALSE; - goto Finish_Recovery; - } - } - - if (RC_BAD( rc = FlmIndexSuspend( hDb, opInfo.uiIndex))) - { - goto Exit; - } - - break; - } - - case RFL_INDEX_RESUME_PACKET: - { - - if (m_pRestore) - { - if (RC_BAD( rc = m_pRestore->status( RESTORE_INDEX_RESUME, - opInfo.uiTransId, (void *) opInfo.uiIndex, (void *) 0, - (void *) 0, &eRestoreAction))) - { - goto Exit; - } - - if (eRestoreAction == RESTORE_ACTION_STOP) - { - bLastTransEndedAtFileEOF = FALSE; - goto Finish_Recovery; - } - } - - if (RC_BAD( rc = FlmIndexResume( hDb, opInfo.uiIndex))) - { - goto Exit; - } - break; - } - - case RFL_INDEX_SET_PACKET: - case RFL_INDEX_SET_PACKET_VER_2: - { - if (m_pRestore) - { - if (RC_BAD( rc = m_pRestore->status( RESTORE_INDEX_SET, - opInfo.uiTransId, (void *) opInfo.uiIndex, - (void *) opInfo.uiDrn, (void *) opInfo.uiEndDrn, - &eRestoreAction))) - { - goto Exit; - } - - if (eRestoreAction == RESTORE_ACTION_STOP) - { - bLastTransEndedAtFileEOF = FALSE; - goto Finish_Recovery; - } - } - - if (m_pFile->FileHdr.uiVersionNum < FLM_FILE_FORMAT_VER_4_50 && - opInfo.uiPacketType != RFL_INDEX_SET_PACKET) - { - rc = RC_SET( FERR_BAD_RFL_PACKET); - goto Exit; - } - - if (RC_BAD( rc = flmDbIndexSetOfRecords( hDb, opInfo.uiIndex, - opInfo.uiContainer, opInfo.uiDrn, opInfo.uiEndDrn))) - { - goto Exit; - } - - break; - } - - case RFL_BLK_CHAIN_FREE_PACKET: - { - if (m_pRestore) - { - if (RC_BAD( rc = m_pRestore->status( RESTORE_BLK_CHAIN_DELETE, - opInfo.uiTransId, (void *) opInfo.uiDrn, - (void *) opInfo.uiCount, - (void *) opInfo.uiEndBlock, &eRestoreAction))) - { - goto Exit; - } - - if (eRestoreAction == RESTORE_ACTION_STOP) - { - bLastTransEndedAtFileEOF = FALSE; - goto Finish_Recovery; - } - } - - if (RC_BAD( rc = flmMaintFreeBlockChain( pDb, opInfo.uiDrn, - opInfo.uiCount, opInfo.uiEndBlock, NULL))) - { - goto Exit; - } - break; - } - - case RFL_START_UNKNOWN_PACKET: - { - if (m_pRestore) - { - F_RflUnknownStream unkStrm; - - unkStrm.setup( this, TRUE); - m_bReadingUnknown = TRUE; - m_uiUnknownPacketBodyLen = 0; - m_pucUnknownPacketBody = NULL; - m_uiUnknownBodyLenProcessed = 0; - m_uiUnknownPacketRc = FERR_OK; - - if (RC_BAD( rc = m_pRestore->processUnknown( - (F_UnknownStream*) &unkStrm))) - { - if (m_uiUnknownPacketRc != FERR_OK) - { - rc = m_uiUnknownPacketRc; - goto Handle_Packet_Error; - } - - goto Exit; - } - - // If we did not read through all of the unknown packets, - // skip them at this time. - - if (m_bReadingUnknown) - { - goto Skip_Unknown_Packets; - } - } - else - { -Skip_Unknown_Packets: - - // Skip all unknown packets. - - for (;;) - { - FLMUINT uiPacketType; - FLMBYTE * pucPacketBody; - FLMUINT uiPacketBodyLen; - - if (RC_BAD( rc = getPacket( FALSE, &uiPacketType, - &pucPacketBody, &uiPacketBodyLen, NULL))) - { - goto Handle_Packet_Error; - } - - // If we hit something other than an unknown packet, - // "push" it back into the pipe so it will be processed - // by readOp() up above. - - if (uiPacketType != RFL_UNKNOWN_PACKET) - { - - // At this point, we know that the entire packet is - // inside our memory buffer, so it is safe to reset - // m_uiRflReadOffset back to the beginning of the - // packet. The call to readOp() above will call - // getPacket again, which will get this exact same - // packet for processing. - - m_uiRflReadOffset -= - (RFL_PACKET_OVERHEAD + uiPacketBodyLen); - break; - } - } - } - break; - } - - case RFL_REDUCE_PACKET: - { - - if (m_pRestore) - { - if (RC_BAD( rc = m_pRestore->status( RESTORE_REDUCE, - opInfo.uiTransId, (void *) opInfo.uiCount, - (void *) 0, (void *) 0, &eRestoreAction))) - { - goto Exit; - } - - if (eRestoreAction == RESTORE_ACTION_STOP) - { - - // Need to set m_uiCurrTransID to 0 since it was set by - // getPacket(). We are not going to start a transaction - // because of the user's request to exit. - - m_uiCurrTransID = 0; - bLastTransEndedAtFileEOF = FALSE; - goto Finish_Recovery; - } - } - - if (RC_BAD( rc = FlmDbReduceSize( hDb, opInfo.uiCount, &uiCount))) - { - goto Exit; - } - - goto Finish_Transaction; - } - - case RFL_UPGRADE_PACKET: - { - if (m_pRestore) - { - if (RC_BAD( rc = m_pRestore->status( RESTORE_UPGRADE, - opInfo.uiTransId, - (void *) opInfo.uiOldVersion, - (void *) opInfo.uiNewVersion, - (void *) 0, &eRestoreAction))) - { - goto Exit; - } - - if (eRestoreAction == RESTORE_ACTION_STOP) - { - - // Need to set m_uiCurrTransID to 0 since it was set by - // getPacket(). We are not going to start a transaction - // because of the user's request to exit. - - m_uiCurrTransID = 0; - bLastTransEndedAtFileEOF = FALSE; - goto Finish_Recovery; - } - } - - // Attempt the conversion if the current version is less than - // the target version and the target version is less than or - // equal to the highest version supported by this code. - - if (opInfo.uiNewVersion > FLM_CUR_FILE_FORMAT_VER_NUM) - { - rc = RC_SET( FERR_UNALLOWED_UPGRADE); - goto Exit; - } - else - { - flmAssert( m_pFile->FileHdr.uiVersionNum < opInfo.uiNewVersion); - - // The logged "new" version may be a lesser version than - // FLM_CURRENT_FILE_FORMAT_VERSION, which is what FlmDbUpgrade - // upgrades to. This is OK because the current version - // should support all packets in the RFL for versions that - // are less than it. Otherwise, the RFL chain would have - // been broken by the original upgrade and it would not - // have logged an upgrade packet. - - if (RC_BAD( rc = FlmDbUpgrade( hDb, opInfo.uiNewVersion, - NULL, NULL))) - { - goto Exit; - } - } - - goto Finish_Transaction; - } - - case RFL_WRAP_KEY_PACKET: - case RFL_ENABLE_ENCRYPTION_PACKET: - { - goto Finish_Transaction; - } - - case RFL_CONFIG_SIZE_EVENT_PACKET: // here - { - if (m_pRestore) - { - if (RC_BAD( rc = m_pRestore->status( RESTORE_CONFIG_SIZE_EVENT, - opInfo.uiTransId, - (void *) opInfo.uiSizeThreshold, - (void *) opInfo.uiTimeInterval, - (void *) opInfo.uiSizeInterval, &eRestoreAction))) - { - goto Exit; - } - - if (eRestoreAction == RESTORE_ACTION_STOP) - { - - // Need to set m_uiCurrTransID to 0 since it was set by - // getPacket(). We are not going to start a transaction - // because of the user's request to exit. - - m_uiCurrTransID = 0; - bLastTransEndedAtFileEOF = FALSE; - goto Finish_Recovery; - } - } - - if (RC_BAD( rc = flmSetRflSizeThreshold( - hDb, opInfo.uiSizeThreshold, opInfo.uiTimeInterval, - opInfo.uiSizeInterval))) - { - goto Exit; - } - - goto Finish_Transaction; - } - - default: - { - - // Should not be getting other packet types at this point. ; - // If we don't know the current file size, and we are doing a - // restore, it is OK to end on a bad packet - we will simply - // abort the current transaction, if any. - - if (m_pRestore && !m_uiFileEOF) - { - rc = FERR_OK; - goto Finish_Recovery; - } - else - { - rc = RC_SET( FERR_BAD_RFL_PACKET); - } - - goto Exit; - } - } - } - -Finish_Recovery: - - if (bTransActive) - { - FlmDbTransAbort( hDb); - bTransActive = FALSE; - } - - if (m_pRestore) - { - FLMUINT uiNextRflFileNum = m_pCurrentBuf->uiCurrFileNum + 1; - - // At the end of the restore operation, we need to set things up so - // that the next transaction will begin a new RFL file. If we ended - // the restore in the middle of an RFL file, we need to set it up so - // that the new RFL file will have a new serial number. If we ended - // at the end of an RFL file, we can set it up so that the new RFL - // file will have the next serial number. ; - // Set up the next RFL file number and offset. - - UD2FBA( uiNextRflFileNum, - &m_pFile->ucLastCommittedLogHdr[LOG_RFL_FILE_NUM]); - - // Set a zero into the offset, this is a special case which tells - // us that we should create the file no matter what - even if it - // already exists - it should be overwritten. - - UD2FBA( 0, &m_pFile->ucLastCommittedLogHdr[LOG_RFL_LAST_TRANS_OFFSET]); - - if (bLastTransEndedAtFileEOF) - { - - // Move the next serial number of the last RFL file processed - // into into the current RFL serial number so that the log header - // will be correct - - f_memcpy( &m_pFile->ucLastCommittedLogHdr[ - LOG_LAST_TRANS_RFL_SERIAL_NUM], m_ucNextSerialNum, - F_SERIAL_NUM_SIZE); - } - else - { - - // Must create a new serial number so that when the new RFL file - // is created, it will have that next serial number. - - if (RC_BAD( rc = f_createSerialNumber( &m_pFile->ucLastCommittedLogHdr[ - LOG_LAST_TRANS_RFL_SERIAL_NUM]))) - { - goto Exit; - } - } - - // Save the last logged commit transaction ID. - - if (m_pFile->FileHdr.uiVersionNum >= FLM_FILE_FORMAT_VER_4_31 && - m_uiLastLoggedCommitTransID) - { - UD2FBA( m_uiLastLoggedCommitTransID, - &m_pFile->ucLastCommittedLogHdr[LOG_LAST_RFL_COMMIT_ID]); - } - - // No matter what, we must generate a new next serial number. This - // is what will be written to the new RFL file's header when it is - // created. - - if (RC_BAD( rc = f_createSerialNumber( - &m_pFile->ucLastCommittedLogHdr[LOG_RFL_NEXT_SERIAL_NUM]))) - { - goto Exit; - } - } - - if (!bHadOperations) - { - - // No transactions were recovered, but still need to setup a few - // things. - - m_pFile->uiFirstLogCPBlkAddress = 0; - m_pFile->uiLastCheckpointTime = (FLMUINT) FLM_GET_TIMER(); - - // Save the state of the log header into the ucCheckpointLogHdr - // buffer. - - f_memcpy( m_pFile->ucCheckpointLogHdr, m_pFile->ucLastCommittedLogHdr, - LOG_HEADER_SIZE); - } - - // Force a checkpoint to force the log files to be truncated and - // everything to be reset. This is done because during recovery the - // checkpoints that are executed do NOT truncate the RFL file - because - // it is using the log file to recover! - - closeFile(); - m_pRestore = NULL; - m_bLoggingOff = FALSE; - pDb->uiFlags &= ~FDB_REPLAYING_RFL; - - if (RC_BAD( rc = FlmDbCheckpoint( hDb, 0))) - { - goto Exit; - } - -Exit: - - if (pRecord) - { - pRecord->Release(); - } - - if (pTmpRecord) - { - pTmpRecord->Release(); - } - - if (bTransActive) - { - FlmDbTransAbort( hDb); - } - - pDb->bFldStateUpdOk = FALSE; - pDb->uiFlags &= ~FDB_REPLAYING_RFL; - - return (rc); -} - -/******************************************************************** -Desc: -********************************************************************/ -RCODE flmRflCalcDiskUsage( - const char * pszRflDir, - const char * pszRflPrefix, - FLMUINT uiDbVersionNum, - FLMUINT64 * pui64DiskUsage) -{ - RCODE rc = FERR_OK; - F_DirHdl * pDirHdl = NULL; - FLMUINT uiFileNumber; - FLMUINT64 ui64DiskUsage; - - ui64DiskUsage = 0; - - if( RC_BAD( rc = gv_FlmSysData.pFileSystem->OpenDir( pszRflDir, - (char *) "*", &pDirHdl))) - { - if( rc == FERR_IO_PATH_NOT_FOUND) - { - rc = FERR_OK; - } - - goto Exit; - } - - for( ;;) - { - if( RC_BAD( rc = pDirHdl->Next())) - { - if( rc != FERR_IO_NO_MORE_FILES && rc != FERR_IO_PATH_NOT_FOUND) - { - goto Exit; - } - - rc = FERR_OK; - break; - } - - // If the current file is an RFL file, increment the disk usage - - if( rflGetFileNum( uiDbVersionNum, pszRflPrefix, - pDirHdl->CurrentItemName(), &uiFileNumber)) - { - ui64DiskUsage += pDirHdl->CurrentItemSize(); - } - } - -Exit: - - *pui64DiskUsage = ui64DiskUsage; - - if( pDirHdl) - { - pDirHdl->Release(); - } - - return( rc); -} - -/******************************************************************** -Desc: Returns the name of an RFL file given its number -********************************************************************/ -FLMEXP RCODE FLMAPI FlmDbGetRflFileName( - HFDB hDb, - FLMUINT uiFileNum, - char * pszFileName) -{ - ((FDB *) hDb)->pFile->pRfl->getBaseRflFileName( uiFileNum, pszFileName); - return (FERR_OK); -} - -/**************************************************************************** -Desc: -****************************************************************************/ -F_RflUnknownStream::F_RflUnknownStream() -{ - m_pRfl = NULL; - m_bStartedWriting = FALSE; - m_bInputStream = FALSE; - m_bSetupCalled = FALSE; -} - -/**************************************************************************** -Desc: -****************************************************************************/ -F_RflUnknownStream::~F_RflUnknownStream() -{ - if (m_bSetupCalled) - { - (void)close(); - } -} - -/**************************************************************************** -Desc: -****************************************************************************/ -RCODE F_RflUnknownStream::setup( - F_Rfl * pRfl, - FLMBOOL bInputStream) -{ - RCODE rc = FERR_OK; - - flmAssert( !m_bSetupCalled); - - if (!pRfl) - { - flmAssert( 0); - rc = RC_SET( FERR_INVALID_PARM); - goto Exit; - } - m_pRfl = pRfl; - m_bInputStream = bInputStream; - m_bSetupCalled = TRUE; - m_bStartedWriting = FALSE; - -Exit: - return( rc); -} - -/**************************************************************************** -Desc: -****************************************************************************/ -RCODE F_RflUnknownStream::close( void) -{ - RCODE rc = FERR_OK; - - flmAssert( m_bSetupCalled); - - // There is nothing to do for input streams, because the RFL - // code handles skipping over any unknown data that may not have - // been read yet. - // For output streams, we need to call the endLoggingUnknown - // routine so that the last packet gets written out. - - if (!m_bInputStream) - { - if (m_bStartedWriting) - { - m_bStartedWriting = FALSE; - if (RC_BAD( rc = m_pRfl->endLoggingUnknown())) - { - goto Exit; - } - } - } -Exit: - return( rc); -} - -/**************************************************************************** -Desc: -****************************************************************************/ -RCODE F_RflUnknownStream::read( - FLMUINT uiLength, - void * pvBuffer, - FLMUINT * puiBytesRead) -{ - RCODE rc = FERR_OK; - - flmAssert( m_bSetupCalled); - - if (!m_bInputStream) - { - - // Cannot read from an output stream. - - flmAssert( 0); - rc = RC_SET( FERR_ILLEGAL_OP); - goto Exit; - } - - if (RC_BAD( rc = m_pRfl->readUnknown( uiLength, (FLMBYTE *)pvBuffer, - puiBytesRead))) - { - goto Exit; - } - -Exit: - return( rc); -} - -/**************************************************************************** -Desc: -****************************************************************************/ -RCODE F_RflUnknownStream::write( - FLMUINT uiLength, - void * pvBuffer) -{ - RCODE rc = FERR_OK; - - flmAssert( m_bSetupCalled); - flmAssert( m_pRfl); - - if (m_bInputStream) - { - - // Cannot write to an input stream. - - flmAssert( 0); - rc = RC_SET( FERR_ILLEGAL_OP); - goto Exit; - } - - // Need to start logging on the first write. - - if (!m_bStartedWriting) - { - if (RC_BAD( rc = m_pRfl->startLoggingUnknown())) - { - goto Exit; - } - m_bStartedWriting = TRUE; - } - - // Log the data. - - if (RC_BAD( rc = m_pRfl->logUnknown( (FLMBYTE *)pvBuffer, uiLength))) - { - goto Exit; - } -Exit: - return( rc); -} - -/**************************************************************************** -Desc: Returns an unknown stream object - suitable for writing unknown - streams into the roll-forward log. -****************************************************************************/ -FLMEXP RCODE FLMAPI FlmDbGetUnknownStreamObj( - HFDB hDb, - F_UnknownStream ** ppUnknownStream) -{ - RCODE rc = FERR_OK; - FDB * pDb = (FDB *)hDb; - F_RflUnknownStream * pUnkStream = NULL; - - flmAssert( pDb); - flmAssert( ppUnknownStream); - - // See if the database is being forced to close - - if( RC_BAD( rc = flmCheckDatabaseState( pDb))) - { - goto Exit; - } - - // This is only valid on 4.3 and greater. - - if (pDb->pFile->FileHdr.uiVersionNum < FLM_FILE_FORMAT_VER_4_3) - { - goto Exit; // Will return FERR_OK and a NULL pointer. - } - - // Must be in an update transaction. - - if (pDb->uiTransType == FLM_NO_TRANS) - { - rc = RC_SET( FERR_NO_TRANS_ACTIVE); - goto Exit; - } - if (pDb->uiTransType != FLM_UPDATE_TRANS) - { - rc = RC_SET( FERR_ILLEGAL_TRANS_OP); - goto Exit; - } - - // Allocate the stream object we want. - - if ((pUnkStream = f_new F_RflUnknownStream) == NULL) - { - rc = RC_SET( FERR_MEM); - goto Exit; - } - - // Setup the unknown stream object. - - if (RC_BAD( rc = pUnkStream->setup( pDb->pFile->pRfl, FALSE))) - { - goto Exit; - } - -Exit: - - if (RC_BAD( rc) && pUnkStream) - { - pUnkStream->Release(); - pUnkStream = NULL; - } - *ppUnknownStream = (F_UnknownStream *)pUnkStream; - return( rc); -} +//------------------------------------------------------------------------- +// Desc: Routines for roll-forward logging. +// Tabs: 3 +// +// Copyright (c) 1998-2006 Novell, Inc. All Rights Reserved. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of version 2 of the GNU General Public +// License as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, contact Novell, Inc. +// +// To contact Novell about this file by physical or electronic mail, +// you may find current contact information at www.novell.com +// +// $Id: rfl.cpp 12334 2006-01-23 12:45:35 -0700 (Mon, 23 Jan 2006) dsanders $ +//------------------------------------------------------------------------- + +#include "flaimsys.h" + +#define MOD_512(uiNum) \ + (FLMUINT) ((uiNum) & 511) + +#define ON_512_BYTE_BOUNDARY(uiNum) \ + (!MOD_512( uiNum)) + +#define ROUND_DOWN_TO_NEAREST_512(uiNum) \ + (FLMUINT) ((uiNum) & (~((FLMUINT) 511))) + +FSTATIC RCODE RflCheckMaxLogged( + FLMUINT * puiMaxBytesNeededRV, + FLMUINT uiPacketsLogged, + FLMUINT * puiCurrTotalLoggedRV, + FLMUINT uiBytesToLog); + +FSTATIC void RflChangeCallback( + GRD_DifferenceData & DiffData, + void * CallbackData); + +/******************************************************************** +Desc: +*********************************************************************/ +F_Rfl::F_Rfl() +{ + m_pFile = NULL; + m_hBufMutex = F_MUTEX_NULL; + m_pCommitBuf = NULL; + m_pCurrentBuf = NULL; + m_uiRflWriteBufs = DEFAULT_RFL_WRITE_BUFFERS; + m_uiBufferSize = DEFAULT_RFL_BUFFER_SIZE; + f_memset( &m_Buf1, 0, sizeof(m_Buf1)); + f_memset( &m_Buf2, 0, sizeof(m_Buf2)); + m_bKeepRflFiles = FALSE; + m_uiRflMinFileSize = DEFAULT_MIN_RFL_FILE_SIZE; + m_uiRflMaxFileSize = DEFAULT_MAX_RFL_FILE_SIZE; + m_pFileHdl = NULL; + m_uiLastRecoverFileNum = 0; + f_memset( m_ucCurrSerialNum, 0, sizeof(m_ucCurrSerialNum)); + m_bLoggingOff = FALSE; + m_bLoggingUnknown = FALSE; + m_uiUnknownPacketLen = 0; + m_bReadingUnknown = FALSE; + m_uiUnknownPacketBodyLen = 0; + m_pucUnknownPacketBody = NULL; + m_uiUnknownBodyLenProcessed = 0; + m_uiUnknownPacketRc = FERR_OK; + m_uiTransStartFile = 0; + m_uiTransStartAddr = 0; + m_uiCurrTransID = 0; + m_uiLastTransID = 0; + m_uiLastLoggedCommitTransID = 0; + m_uiOperCount = 0; + m_uiRflReadOffset = 0; + m_uiFileEOF = 0; + m_pRestore = NULL; + f_memset( m_szDbPrefix, 0, sizeof(m_szDbPrefix)); + f_memset( m_szRflDir, 0, sizeof(m_szRflDir)); + m_bRflDirSameAsDb = FALSE; + m_bCreateRflDir = FALSE; + f_memset( m_ucNextSerialNum, 0, sizeof(m_ucNextSerialNum)); + m_bRflVolumeOk = TRUE; + m_bRflVolumeFull = FALSE; +} + +/******************************************************************** +Desc: +*********************************************************************/ +F_Rfl::~F_Rfl() +{ + + // Better not be in the middle of logging unknown packets for the + // application. + + flmAssert( !m_bLoggingUnknown); + + if (m_Buf1.pIOBuffer) + { + m_Buf1.pIOBuffer->Release(); + m_Buf1.pIOBuffer = NULL; + } + + if (m_Buf2.pIOBuffer) + { + m_Buf2.pIOBuffer->Release(); + m_Buf2.pIOBuffer = NULL; + } + + if (m_Buf1.pBufferMgr) + { + flmAssert( !m_Buf1.pBufferMgr->havePendingIO() && + !m_Buf1.pBufferMgr->haveUsed()); + + m_Buf1.pBufferMgr->Release(); + m_Buf1.pBufferMgr = NULL; + } + + if (m_Buf2.pBufferMgr) + { + flmAssert( !m_Buf2.pBufferMgr->havePendingIO() && + !m_Buf2.pBufferMgr->haveUsed()); + + m_Buf2.pBufferMgr->Release(); + m_Buf2.pBufferMgr = NULL; + } + + if (m_hBufMutex != F_MUTEX_NULL) + { + f_mutexDestroy( &m_hBufMutex); + } + + if (m_pFileHdl) + { + m_pFileHdl->Close(); + m_pFileHdl->Release(); + m_pFileHdl = NULL; + m_pFile = NULL; + } +} + +/******************************************************************** +Desc: Returns a boolean indicating whether or not we are at + the end of the RFL log - will only be TRUE when we are + doing recovery. +*********************************************************************/ +FLMBOOL F_Rfl::atEndOfLog(void) +{ + return( (!m_pRestore && m_uiFileEOF && + m_pCurrentBuf->uiRflFileOffset + + m_pCurrentBuf->uiRflBufBytes >= m_uiFileEOF && + m_uiRflReadOffset == m_pCurrentBuf->uiRflBufBytes && + m_pCurrentBuf->uiCurrFileNum == m_uiLastRecoverFileNum) + ? TRUE + : FALSE); +} + +/******************************************************************** +Desc: Gets the base RFL file name - does not have directory part. + This needs to be separate from the F_Rfl object so it can + be called without having to instantiate and set up an F_Rfl + object. +*********************************************************************/ +void rflGetBaseFileName( + FLMUINT uiDbVersion, + const char * pszDbPrefix, + FLMUINT uiFileNum, + char * pszBaseNameOut) +{ + FLMINT iCnt = 0; + FLMUINT uiDigit; + char * pszTmp = pszBaseNameOut; + + if (uiDbVersion < FLM_FILE_FORMAT_VER_4_3) + { + + // Output the database name prefix (up to three characters). + + f_strcpy( pszTmp, pszDbPrefix); + while (*pszTmp) + { + pszTmp++; + } + + // Output as five digit base 36 number. + + pszTmp += 4; + while (iCnt < 5) + { + uiDigit = (FLMUINT) (uiFileNum % 36); + uiFileNum /= 36; + if (uiDigit <= 9) + { + uiDigit += NATIVE_ZERO; + } + else + { + uiDigit += (NATIVE_LOWER_A - 10); + } + + *pszTmp = (FLMBYTE) uiDigit; + pszTmp--; + iCnt++; + } + + // Skip to end of digits and append ".log" to name + + pszTmp += 6; + f_strcpy( pszTmp, ".log"); + } + else + { + + // Output as eight digit hex number. + + pszTmp += 7; + while (iCnt < 8) + { + uiDigit = (FLMUINT) (uiFileNum & 0xF); + uiFileNum >>= 4; + if (uiDigit <= 9) + { + uiDigit += NATIVE_ZERO; + } + else + { + uiDigit += (NATIVE_LOWER_A - 10); + } + + *pszTmp = (FLMBYTE) uiDigit; + pszTmp--; + iCnt++; + } + + // Skip to end of digits and append ".log" to name + + pszTmp += 9; + f_strcpy( pszTmp, ".log"); + } +} + +/******************************************************************** +Desc: Gets the base RFL file name - does not have directory part. +*********************************************************************/ +void F_Rfl::getBaseRflFileName( + FLMUINT uiFileNum, + char * pszBaseName) +{ + rflGetBaseFileName( m_pFile->FileHdr.uiVersionNum, + m_szDbPrefix, uiFileNum, pszBaseName); +} + +/******************************************************************** +Desc: Generates the full roll forward log file name. Name is based + on the sequence number and the first three characters of the + database if the DB is less than version 4.3. + Otherwise, it is a hex number. +*********************************************************************/ +RCODE F_Rfl::getFullRflFileName( + FLMUINT uiFileNum, + char * pszRflFileName) +{ + RCODE rc = FERR_OK; + char szBaseName[F_FILENAME_SIZE]; + + // Get the directory name. + + f_strcpy( pszRflFileName, m_szRflDir); + + // Get the base RFL file name. + + getBaseRflFileName( uiFileNum, szBaseName); + + // Append the two together. + + if (RC_BAD( rc = f_pathAppend( pszRflFileName, szBaseName))) + { + goto Exit; + } + +Exit: + + return (rc); +} + +/******************************************************************** +Desc: Positions to the offset specified in the RFL file. +*********************************************************************/ +RCODE F_Rfl::positionTo( + FLMUINT uiFileOffset) +{ + RCODE rc = FERR_OK; + FLMUINT uiBytesToRead; + FLMUINT uiBytesRead; + + // Should never be attempting to position to something less than 512 - + // the header is stored in the first 512 bytes. + + flmAssert( uiFileOffset >= 512); + + // If the position is within our current buffer, see if we can adjust + // things without having to go back and re-read the buffer from disk. + + if (m_pCurrentBuf->uiRflBufBytes && + uiFileOffset >= m_pCurrentBuf->uiRflFileOffset && + uiFileOffset <= m_pCurrentBuf->uiRflFileOffset + + m_pCurrentBuf->uiRflBufBytes) + { + + // Whatever is in the buffer beyond uiFileOffset is irrelevant and + // can be discarded. + + m_pCurrentBuf->uiRflBufBytes = uiFileOffset - + m_pCurrentBuf->uiRflFileOffset; + } + else + { + + // Populate the buffer from the 512 byte boundary that is just + // before the offset we are trying to position to. + + uiBytesToRead = MOD_512( uiFileOffset); + m_pCurrentBuf->uiRflFileOffset = ROUND_DOWN_TO_NEAREST_512( uiFileOffset); + m_pCurrentBuf->uiRflBufBytes = MOD_512( uiFileOffset); + if (m_pCurrentBuf->uiRflBufBytes) + { + if (RC_BAD( rc = m_pFileHdl->SectorRead( + m_pCurrentBuf->uiRflFileOffset, + m_pCurrentBuf->uiRflBufBytes, + m_pCurrentBuf->pIOBuffer->m_pucBuffer, &uiBytesRead))) + { + if (rc == FERR_IO_END_OF_FILE) + { + rc = RC_SET( FERR_NOT_RFL); + } + else + { + m_bRflVolumeOk = FALSE; + } + + goto Exit; + } + else if (uiBytesRead < m_pCurrentBuf->uiRflBufBytes) + { + rc = RC_SET( FERR_NOT_RFL); + goto Exit; + } + } + } + +Exit: + + return (rc); +} + +/******************************************************************** +Desc: Get the ACTUAL RFL directory, using as input parameters the + database version, the name of the database, and the + user specified RFL directory. Also return the database + prefix. +*********************************************************************/ +RCODE rflGetDirAndPrefix( + FLMUINT uiDbVersionNum, + const char * pszDbFileName, + const char * pszRflDirIn, + char * pszRflDirOut, + char * pszDbPrefixOut) +{ + RCODE rc = FERR_OK; + char szDbPath[F_PATH_MAX_SIZE]; + char szBaseName[F_FILENAME_SIZE]; + + // Parse the database name into directory and base name + + if (RC_BAD( rc = f_pathReduce( pszDbFileName, szDbPath, szBaseName))) + { + goto Exit; + } + + // Get the base path + + flmGetDbBasePath( pszDbPrefixOut, szBaseName, NULL); + + if (uiDbVersionNum >= FLM_FILE_FORMAT_VER_4_3) + { + + // Determine the RFL directory. If one was specified, it is + // whatever was specified. Otherwise, it is relative to the database + // directory. + + if (pszRflDirIn && *pszRflDirIn) + { + f_strcpy( pszRflDirOut, pszRflDirIn); + } + else + { + f_strcpy( pszRflDirOut, szDbPath); + } + + // For 4.3 and above, the RFL files go in a subdirectory underneath + // the directory where the database is located or the specified + // directory. + + f_strcpy( szBaseName, pszDbPrefixOut); + f_strcat( szBaseName, ".rfl"); + f_pathAppend( pszRflDirOut, szBaseName); + } + else + { + f_strcpy( pszRflDirOut, szDbPath); + } + +Exit: + + return (rc); +} + +/******************************************************************** +Desc: Set the RFL directory. If pszRflDir is NULL or empty string, + the RFL directory is set to the same directory as the + database. +*********************************************************************/ +RCODE F_Rfl::setRflDir( + const char * pszRflDir) +{ + + // Better have set up the FFILE pointer. + + flmAssert( m_pFile != NULL); + + m_bRflDirSameAsDb = (!pszRflDir || !(*pszRflDir)) ? TRUE : FALSE; + + flmAssert( m_pFile->FileHdr.uiVersionNum); + + if (m_pFile->FileHdr.uiVersionNum < FLM_FILE_FORMAT_VER_4_3) + { + + // Don't allow RFL directory to be specified for versions less than + // 4.3 + + pszRflDir = NULL; + m_bRflDirSameAsDb = TRUE; + } + + m_bCreateRflDir = + (m_pFile->FileHdr.uiVersionNum >= FLM_FILE_FORMAT_VER_4_3) + ? TRUE + : FALSE; + return( rflGetDirAndPrefix( + m_pFile->FileHdr.uiVersionNum, m_pFile->pszDbPath, + pszRflDir, m_szRflDir, m_szDbPrefix)); +} + +/******************************************************************** +Desc: Gets an RFL file name - based on DB name and RFL directory. +*********************************************************************/ +RCODE rflGetFileName( + FLMUINT uiDbVersion, + const char * pszDbName, + const char * pszRflDir, + FLMUINT uiFileNum, + char * pszRflFileName) +{ + RCODE rc = FERR_OK; + char szDbPrefix[ F_FILENAME_SIZE]; + char szBaseName[ F_FILENAME_SIZE]; + + // Get the full RFL file name. + + if (RC_BAD( rc = rflGetDirAndPrefix( uiDbVersion, pszDbName, pszRflDir, + pszRflFileName, szDbPrefix))) + { + goto Exit; + } + + rflGetBaseFileName( uiDbVersion, szDbPrefix, uiFileNum, szBaseName); + if (RC_BAD( rc = f_pathAppend( pszRflFileName, szBaseName))) + { + goto Exit; + } + +Exit: + + return (rc); +} + +/******************************************************************** +Desc: Gets an RFL file number from the RFL file name. +*********************************************************************/ +FLMBOOL rflGetFileNum( + FLMUINT uiDbVersion, + const char * pszDbPrefix, + const char * pszRflFileName, + FLMUINT * puiFileNum) +{ + FLMBOOL bGotNum = FALSE; + char szDir[F_PATH_MAX_SIZE]; + char szBaseName[F_FILENAME_SIZE]; + char * pszTmp; + FLMUINT uiCharCnt; + + if (RC_BAD( f_pathReduce( pszRflFileName, szDir, szBaseName))) + { + goto Exit; + } + + // See if it has a .log extension. + + pszTmp = &szBaseName[0]; + while (*pszTmp && *pszTmp != '.') + { + pszTmp++; + } + + // If we do not have a .log extension, it is not a legitimate RFL file. + + if (f_stricmp( pszTmp, ".log") != 0) + { + goto Exit; + } + + // Parse out the name according to the rules for this DB version. + + *pszTmp = 0; // Set period to zero + pszTmp = &szBaseName[0]; + *puiFileNum = 0; + uiCharCnt = 0; + if (uiDbVersion >= FLM_FILE_FORMAT_VER_4_3) + { + + // Name up to the period should be a hex number + + while (*pszTmp) + { + (*puiFileNum) <<= 4; + if (*pszTmp >= NATIVE_ZERO && *pszTmp <= NATIVE_NINE) + { + *puiFileNum += (FLMUINT) (*pszTmp - NATIVE_ZERO); + } + else if (*pszTmp >= NATIVE_LOWER_A && *pszTmp <= NATIVE_LOWER_F) + { + *puiFileNum += ((FLMUINT) (*pszTmp - NATIVE_LOWER_A) + 10); + } + else if (*pszTmp >= NATIVE_UPPER_A && *pszTmp <= NATIVE_UPPER_F) + { + *puiFileNum += ((FLMUINT) (*pszTmp - NATIVE_UPPER_A) + 10); + } + else + { + goto Exit; // Not a hex number + } + + uiCharCnt++; + pszTmp++; + } + + // Better have been exactly 8 hex digits. + + bGotNum = (FLMBOOL) ((uiCharCnt == 8) ? TRUE : FALSE); + } + else + { + FLMUINT uiLen = f_strlen( pszTmp); + FLMUINT uiPrefixLen = f_strlen( pszDbPrefix); + + // Length of base name without the .log extension better be exactly + // 5 more characters than the length of the prefix. + + if (uiLen != uiPrefixLen + 5) + { + flmAssert( 0); + goto Exit; + } + + // Prefix better match. + + while (uiPrefixLen) + { + if (f_toupper( *pszTmp) != f_toupper( *pszDbPrefix)) + { + goto Exit; + } + + uiPrefixLen--; + pszTmp++; + pszDbPrefix++; + } + + // Rest of the name is the five digits that are a base 36 number. + + while (*pszTmp) + { + (*puiFileNum) *= 36; + if (*pszTmp >= NATIVE_ZERO && *pszTmp <= NATIVE_NINE) + { + *puiFileNum += (FLMUINT) (*pszTmp - NATIVE_ZERO); + } + else if (*pszTmp >= NATIVE_LOWER_A && *pszTmp <= NATIVE_LOWER_Z) + { + *puiFileNum += ((FLMUINT) (*pszTmp - NATIVE_LOWER_A) + 10); + } + else if (*pszTmp >= NATIVE_UPPER_A && *pszTmp <= NATIVE_UPPER_Z) + { + *puiFileNum += ((FLMUINT) (*pszTmp - NATIVE_UPPER_A) + 10); + } + else + { + goto Exit; // Not a base 36 number + } + + pszTmp++; + } + + bGotNum = TRUE; + } + +Exit: + + return (bGotNum); +} + +/******************************************************************** +Desc: Sets up the RFL object - associating with a file, etc. +*********************************************************************/ +RCODE F_Rfl::setup( + FFILE * pFile, + const char * pszRflDir) +{ + RCODE rc = FERR_OK; + + // Better not already be associated with an FFILE + + flmAssert( m_pFile == NULL); + m_pFile = pFile; + + // Allocate memory for the RFL buffers + + if (!gv_FlmSysData.bOkToDoAsyncWrites) + { + m_uiRflWriteBufs = 1; + m_uiBufferSize = DEFAULT_RFL_WRITE_BUFFERS * DEFAULT_RFL_BUFFER_SIZE; + } + + if (RC_BAD( rc = f_mutexCreate( &m_hBufMutex))) + { + goto Exit; + } + + if ((m_Buf1.pBufferMgr = f_new F_IOBufferMgr) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if ((m_Buf2.pBufferMgr = f_new F_IOBufferMgr) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + m_Buf1.pBufferMgr->enableKeepBuffer(); + m_Buf1.pBufferMgr->setMaxBuffers( m_uiRflWriteBufs); + m_Buf1.pBufferMgr->setMaxBytes( m_uiRflWriteBufs * m_uiBufferSize); + + if (RC_BAD( rc = m_Buf1.pBufferMgr->getBuffer( &m_Buf1.pIOBuffer, + m_uiBufferSize, m_uiBufferSize))) + { + goto Exit; + } + + m_Buf2.pBufferMgr->enableKeepBuffer(); + m_Buf2.pBufferMgr->setMaxBuffers( m_uiRflWriteBufs); + m_Buf2.pBufferMgr->setMaxBytes( m_uiRflWriteBufs * m_uiBufferSize); + + if (RC_BAD( rc = m_Buf2.pBufferMgr->getBuffer( &m_Buf2.pIOBuffer, + m_uiBufferSize, m_uiBufferSize))) + { + goto Exit; + } + + m_bLoggingOff = FALSE; + m_pCurrentBuf = &m_Buf1; + m_pCurrentBuf->uiRflBufBytes = 0; + + // Set the RFL directory and prefix if necessary. + + if (RC_BAD( rc = setRflDir( pszRflDir))) + { + goto Exit; + } + +Exit: + + return (rc); +} + +/******************************************************************** +Desc: Wait for the writes of a buffer to finish. This routine assumes + that the m_hBufMutex is locked when coming in. It will ALWAYS + unlock the mutex before exiting. +*********************************************************************/ +RCODE F_Rfl::waitForWrites( + RFL_BUFFER * pBuffer, + FLMBOOL bIsWriter) +{ + RCODE rc = FERR_OK; + RCODE TempRc; + RFL_WAITER Waiter; + FLMBOOL bMutexLocked = TRUE; + + // Put self on the wait queue for the buffer. + + Waiter.uiThreadId = f_threadId(); + Waiter.bIsWriter = bIsWriter; + Waiter.hESem = F_SEM_NULL; + if (RC_BAD( rc = f_semCreate( &Waiter.hESem))) + { + goto Exit; + } + + // Note: rc better be changed to success or write error by the process + // that signals us. + + rc = RC_SET( FERR_FAILURE); + Waiter.pRc = &rc; + Waiter.pNext = NULL; + if (pBuffer->pLastWaiter) + { + pBuffer->pLastWaiter->pNext = &Waiter; + } + else + { + pBuffer->pFirstWaiter = &Waiter; + } + + pBuffer->pLastWaiter = &Waiter; + f_mutexUnlock( m_hBufMutex); + bMutexLocked = FALSE; + + // Now just wait to be signaled. + + if (RC_BAD( TempRc = f_semWait( Waiter.hESem, F_SEM_WAITFOREVER))) + { +#ifdef FLM_NLM + EnterDebugger(); +#else + flmAssert( 0); +#endif + rc = TempRc; + } + else + { + + // Process that signaled us better set the rc to something besides + // FERR_FAILURE. + + if (rc == FERR_FAILURE) + { +#ifdef FLM_NLM + EnterDebugger(); +#else + flmAssert( 0); +#endif + } + } + +Exit: + + if (Waiter.hESem != F_SEM_NULL) + { + f_semDestroy( &Waiter.hESem); + } + + if (bMutexLocked) + { + f_mutexUnlock( m_hBufMutex); + } + + return (rc); +} + +/******************************************************************** +Desc: If a commit is in progress, wait for it to finish. +*********************************************************************/ +RCODE F_Rfl::waitForCommit(void) +{ + RCODE rc = FERR_OK; + FLMBOOL bMutexLocked = FALSE; + + // NOTE: If m_pCommitBuf is NULL it cannot be set to something + // non-NULL except by this thread when this thread ends the + // transaction. So, there is no need to lock the mutex and re-check if + // it is NULL. + + if (m_pCommitBuf) + { + f_mutexLock( m_hBufMutex); + bMutexLocked = TRUE; + + // Check m_pCommitBuf again after locking mutex - may have finished. + + if (m_pCommitBuf) + { + + // waitForWrites will unlock the mutex. + + bMutexLocked = FALSE; + rc = waitForWrites( m_pCommitBuf, FALSE); + } + } + + if (bMutexLocked) + { + f_mutexUnlock( m_hBufMutex); + } + + return (rc); +} + +/******************************************************************** +Desc: Write out the header information for an RFL file. +*********************************************************************/ +RCODE F_Rfl::writeHeader( + FLMUINT uiFileNum, + FLMUINT uiEof, + FLMBYTE * pucSerialNum, + FLMBYTE * pucNextSerialNum, + FLMBOOL bKeepSignature) +{ + RCODE rc = FERR_OK; + FLMBYTE ucBuf [512]; + FLMUINT uiBytesWritten; + + flmAssert( m_pFile); + flmAssert( m_pFileHdl); + + f_memset( ucBuf, 0, sizeof(ucBuf)); + f_memcpy( &ucBuf[RFL_NAME_POS], RFL_NAME, RFL_NAME_LEN); + f_memcpy( &ucBuf[RFL_VERSION_POS], RFL_VERSION, RFL_VERSION_LEN); + UD2FBA( (FLMUINT32) uiFileNum, &ucBuf[RFL_FILE_NUMBER_POS]); + UD2FBA( (FLMUINT32) uiEof, &ucBuf[RFL_EOF_POS]); + + if (m_pFile->FileHdr.uiVersionNum >= FLM_FILE_FORMAT_VER_4_3) + { + f_memcpy( &ucBuf[RFL_DB_SERIAL_NUM_POS], + &m_pFile->ucLastCommittedLogHdr[LOG_DB_SERIAL_NUM], + F_SERIAL_NUM_SIZE); + f_memcpy( &ucBuf[RFL_SERIAL_NUM_POS], pucSerialNum, F_SERIAL_NUM_SIZE); + f_memcpy( &ucBuf[RFL_NEXT_FILE_SERIAL_NUM_POS], pucNextSerialNum, + F_SERIAL_NUM_SIZE); + f_strcpy( (char *) &ucBuf[RFL_KEEP_SIGNATURE_POS], + ((bKeepSignature) ? RFL_KEEP_SIGNATURE : RFL_NOKEEP_SIGNATURE)); + } + + // Write out the header + + if (RC_BAD( rc = m_pFileHdl->SectorWrite( 0L, 512, ucBuf, sizeof(ucBuf), + NULL, &uiBytesWritten))) + { + + // Remap disk full error + + if (rc == FERR_IO_DISK_FULL) + { + rc = RC_SET( FERR_RFL_DEVICE_FULL); + m_bRflVolumeFull = TRUE; + } + + m_bRflVolumeOk = FALSE; + goto Exit; + } + + // Flush the file handle to ensure it is forced to disk. + + if (RC_BAD( rc = m_pFileHdl->Flush())) + { + + // Remap disk full error + + if (rc == FERR_IO_DISK_FULL) + { + rc = RC_SET( FERR_RFL_DEVICE_FULL); + m_bRflVolumeFull = TRUE; + } + + m_bRflVolumeOk = FALSE; + goto Exit; + } + +Exit: + + return (rc); +} + +/******************************************************************** +Desc: Verifies the header of an RFL file. +*********************************************************************/ +RCODE F_Rfl::verifyHeader( + FLMBYTE * pucHeader, + FLMUINT uiFileNum, + FLMBYTE * pucSerialNum) +{ + RCODE rc = FERR_OK; + + flmAssert( m_pFile); + + // Check the RFL name and version number + + if (f_memcmp( &pucHeader[RFL_NAME_POS], RFL_NAME, RFL_NAME_LEN) != 0) + { + rc = RC_SET( FERR_NOT_RFL); + goto Exit; + } + + if (f_memcmp( &pucHeader[RFL_VERSION_POS], + RFL_VERSION, RFL_VERSION_LEN) != 0) + { + rc = RC_SET( FERR_NOT_RFL); + goto Exit; + } + + if (m_pFile->FileHdr.uiVersionNum >= FLM_FILE_FORMAT_VER_4_3) + { + + // Verify the database serial number + + if (f_memcmp( &pucHeader[RFL_DB_SERIAL_NUM_POS], + &m_pFile->ucLastCommittedLogHdr[LOG_DB_SERIAL_NUM], + F_SERIAL_NUM_SIZE) != 0) + { + rc = RC_SET( FERR_BAD_RFL_DB_SERIAL_NUM); + goto Exit; + } + + // Verify the serial number that is expected to be on the RFL file. + // If pucSerialNum is NULL, we will not verify it. This is generally + // only done during recovery or restore when we are reading through + // multiple RFL files and we need to verify their serial numbers. + + if (pucSerialNum && + f_memcmp( &pucHeader[RFL_SERIAL_NUM_POS], pucSerialNum, + F_SERIAL_NUM_SIZE) != 0) + { + rc = RC_SET( FERR_BAD_RFL_SERIAL_NUM); + goto Exit; + } + + // Verify the file number. + + if (uiFileNum != (FLMUINT) FB2UD( &pucHeader[RFL_FILE_NUMBER_POS])) + { + rc = RC_SET( FERR_BAD_RFL_FILE_NUMBER); + goto Exit; + } + + // Save serial numbers from the header. + + f_memcpy( m_ucCurrSerialNum, &pucHeader[RFL_SERIAL_NUM_POS], + F_SERIAL_NUM_SIZE); + f_memcpy( m_ucNextSerialNum, &pucHeader[RFL_NEXT_FILE_SERIAL_NUM_POS], + F_SERIAL_NUM_SIZE); + } + + // Save some things from the header. + + m_uiFileEOF = (FLMUINT) FB2UD( &pucHeader[RFL_EOF_POS]); + +Exit: + + return (rc); +} + +/******************************************************************** +Desc: Opens an RFL file. Verifies the serial number for 4.3 dbs. +*********************************************************************/ +RCODE F_Rfl::openFile( + FLMUINT uiFileNum, + FLMBYTE * pucSerialNum) +{ + RCODE rc = FERR_OK; + char szRflFileName [F_PATH_MAX_SIZE]; + FLMBYTE ucBuf [512]; + FLMUINT uiBytesRead; + + flmAssert( m_pFile); + + // If we have a file open and it is not the file number passed in, + // close it. + + if (m_pFileHdl) + { + if (m_pCurrentBuf->uiCurrFileNum != uiFileNum) + { + if (RC_BAD( rc = waitForCommit())) + { + goto Exit; + } + + closeFile(); + } + else + { + goto Exit; // Will return FERR_OK + } + } + else + { + + // Should not be able to be in the middle of a commit if we don't + // have a file open! + + flmAssert( !m_pCommitBuf); + } + + // Generate the log file name. + + if (RC_BAD( rc = getFullRflFileName( uiFileNum, szRflFileName))) + { + goto Exit; + } + + // Open the file. + + if (RC_BAD( rc = gv_FlmSysData.pFileSystem->OpenBlockFile( szRflFileName, + F_IO_RDWR | F_IO_SH_DENYNONE | F_IO_DIRECT, 512, &m_pFileHdl))) + { + goto Exit; + } + + m_pFileHdl->setMaxAutoExtendSize( m_uiRflMaxFileSize); + m_pFileHdl->setExtendSize( m_pFile->uiFileExtendSize); + + // Read the header. + + if (RC_BAD( rc = m_pFileHdl->SectorRead( 0, 512, ucBuf, &uiBytesRead))) + { + if (rc == FERR_IO_END_OF_FILE) + { + rc = RC_SET( FERR_NOT_RFL); + } + else + { + m_bRflVolumeOk = FALSE; + } + + goto Exit; + } + + // If there is not enough data in the buffer, it is not an RFL file. + + if (uiBytesRead < 512) + { + rc = RC_SET( FERR_NOT_RFL); + goto Exit; + } + + // Verify the header information + + if (RC_BAD( rc = verifyHeader( ucBuf, uiFileNum, pucSerialNum))) + { + goto Exit; + } + + m_pCurrentBuf->uiRflBufBytes = 0; + m_pCurrentBuf->uiRflFileOffset = 0; + m_pCurrentBuf->uiCurrFileNum = uiFileNum; + +Exit: + + if (RC_BAD( rc)) + { + waitForCommit(); + closeFile(); + } + + return (rc); +} + +/******************************************************************** +Desc: Creates a new roll forward log file. +*********************************************************************/ +RCODE F_Rfl::createFile( + FLMUINT uiFileNum, + FLMBYTE * pucSerialNum, + FLMBYTE * pucNextSerialNum, + FLMBOOL bKeepSignature) +{ + RCODE rc = FERR_OK; + char szRflFileName [F_PATH_MAX_SIZE]; + + flmAssert( m_pFile); + + // Better not be trying to create the current file + + flmAssert( uiFileNum != m_pCurrentBuf->uiCurrFileNum); + + // If we have a file open close it. + + if (RC_BAD( rc = waitForCommit())) + { + goto Exit; + } + + closeFile(); + + // Generate the log file name. + + if (RC_BAD( rc = getFullRflFileName( uiFileNum, szRflFileName))) + { + goto Exit; + } + + // Delete the file if it already exists - don't care about return code + // here + + (void) gv_FlmSysData.pFileSystem->Delete( szRflFileName); + + // If DB is 4.3 or greater and we are in the same directory as our + // database files, see if we need to create the subdirectory. + // Otherwise, the RFL directory should already have been created. If + // the directory already exists, it is OK - we only try this the first + // time after setRflDir is called - to either verify that the directory + // exists, or if it doesn't, to create it. + + if (m_bCreateRflDir) + { + + // If it already exists, don't attempt to create it. + + if (RC_BAD( rc = gv_FlmSysData.pFileSystem->Exists( m_szRflDir))) + { + if (rc != FERR_IO_PATH_NOT_FOUND && rc != FERR_IO_INVALID_PATH) + { + goto Exit; + } + else + { + if (RC_BAD( rc = gv_FlmSysData.pFileSystem->CreateDir( m_szRflDir))) + { + goto Exit; + } + } + } + + m_bCreateRflDir = FALSE; + } + + // Create the file + + if (RC_BAD( rc = gv_FlmSysData.pFileSystem->CreateBlockFile( szRflFileName, + F_IO_RDWR | F_IO_EXCL | F_IO_SH_DENYNONE | F_IO_DIRECT, 512, + &m_pFileHdl))) + { + goto Exit; + } + + m_pFileHdl->setMaxAutoExtendSize( m_uiRflMaxFileSize); + m_pFileHdl->setExtendSize( m_pFile->uiFileExtendSize); + + // 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; + + // Update the size of the RFL + + if( m_bKeepRflFiles) + { + FLMUINT64 ui64RflDiskUsage; + + if( RC_BAD( rc = flmRflCalcDiskUsage( m_szRflDir, m_szDbPrefix, + m_pFile->FileHdr.uiVersionNum, &ui64RflDiskUsage))) + { + goto Exit; + } + + f_mutexLock( gv_FlmSysData.hShareMutex); + m_pFile->ui64RflDiskUsage = ui64RflDiskUsage; + f_mutexUnlock( gv_FlmSysData.hShareMutex); + } + + +Exit: + + // Close the RFL log file AND delete it if we were not successful. + + if (RC_BAD( rc)) + { + closeFile(); + (void) gv_FlmSysData.pFileSystem->Delete( szRflFileName); + } + + return (rc); +} + +/******************************************************************** +Desc: Copy last partial sector of last buffer written (or to be + written) into a new buffer. +*********************************************************************/ +void F_Rfl::copyLastSector( + RFL_BUFFER * pBuffer, + FLMBYTE * pucOldBuffer, + FLMBYTE * pucNewBuffer, + FLMUINT uiCurrPacketLen, + FLMBOOL bStartingNewFile) +{ + FLMUINT uiOldBufBytes = pBuffer->uiRflBufBytes; + + // If we will be starting a new file, no need to keep any of + // what is in the buffer. Only the current packet needs to + // be copied - at the beginning of the buffer. + + // OTHERWISE: + + // If there are fewer than 512 bytes in the buffer, we simply + // keep them and keep appending to the buffer the next time + // we output stuff. The beginning of the buffer must ALWAYS be + // a 512 byte boundary in the file, because we want to always + // do our writing on 512 byte boundaries - because of direct IO. + + // If the number of bytes in the buffer is over 512 and it is + // evenly divisible by 512, we can clear the buffer. Otherwise, + // we want to move the extra bytes over the last 512 byte boundary + // down to the beginning of the buffer and adjust the buffer bytes + // to reflect just these left-over bytes. + + if (bStartingNewFile) + { + pBuffer->uiRflBufBytes = 0; + pBuffer->uiRflFileOffset = 512; + } + else if (pBuffer->uiRflBufBytes >= 512) + { + + // See if the number of bytes in the buffer is an exact multiple of + // 512. + + if (pBuffer->uiRflBufBytes & 511) // Not exact multiple + { + + // Round m_uiRflBufBytes down to next 512 byte boundary + + FLMUINT ui512Offset = ROUND_DOWN_TO_NEAREST_512( + pBuffer->uiRflBufBytes); + + // Move all bytes above the nearest 512 byte boundary down to + // the beginning of the buffer and adjust pBuffer->uiRflBufBytes + // and pBuffer->uiRflFileOffset accordingly. + + f_memcpy( pucNewBuffer, &pucOldBuffer[ui512Offset], + pBuffer->uiRflBufBytes - ui512Offset); + pBuffer->uiRflBufBytes -= ui512Offset; + pBuffer->uiRflFileOffset += ui512Offset; + } + else + { + pBuffer->uiRflFileOffset += pBuffer->uiRflBufBytes; + pBuffer->uiRflBufBytes = 0; + } + } + else if (pucNewBuffer != pucOldBuffer) + { + f_memcpy( pucNewBuffer, pucOldBuffer, pBuffer->uiRflBufBytes); + } + + if (uiCurrPacketLen) + { + flmAssert( uiOldBufBytes + uiCurrPacketLen <= m_uiBufferSize); + f_memmove( &pucNewBuffer[pBuffer->uiRflBufBytes], + &pucOldBuffer[uiOldBufBytes], uiCurrPacketLen); + } +} + +/******************************************************************** +Desc: Flush the RFL data from the buffer to disk. +*********************************************************************/ +RCODE F_Rfl::flush( + RFL_BUFFER * pBuffer, + FLMBOOL bFinalWrite, + FLMUINT uiCurrPacketLen, + FLMBOOL bStartingNewFile) +{ + RCODE rc = FERR_OK; + FLMUINT uiBytesWritten; + F_IOBuffer * pNewBuffer; + F_IOBuffer * pAsyncBuf = NULL; + FLMBYTE * pucOldBuffer; + FLMUINT uiFileOffset; + FLMUINT uiBufBytes; + + if (m_pFileHdl && pBuffer->uiRflBufBytes) + { + + // Must wait for stuff in committing buffer, if any, before going + // ahead here. + + if (pBuffer != m_pCommitBuf) + { + if (RC_BAD( rc = waitForCommit())) + { + goto Exit; + } + } + + if (m_uiRflWriteBufs > 1 && m_pFileHdl->CanDoAsync()) + { + pAsyncBuf = pBuffer->pIOBuffer; + } + + if ((FLMUINT) (-1) - pBuffer->uiRflFileOffset <= pBuffer->uiRflBufBytes) + { + rc = RC_SET( FERR_DB_FULL); + goto Exit; + } + + pucOldBuffer = pBuffer->pIOBuffer->m_pucBuffer; + uiFileOffset = pBuffer->uiRflFileOffset; + uiBufBytes = pBuffer->uiRflBufBytes; + if (m_uiRflWriteBufs > 1) + { + if (RC_BAD( rc = pBuffer->pBufferMgr->getBuffer( &pNewBuffer, + m_uiBufferSize, m_uiBufferSize))) + { + goto Exit; + } + + // No need to copy data if it is the final write, because it + // won't be reused anyway - the data for the next transaction has + // already been copied to another buffer. + + if (!bFinalWrite) + { + copyLastSector( pBuffer, pucOldBuffer, pNewBuffer->m_pucBuffer, + uiCurrPacketLen, bStartingNewFile); + } + } + + if( RC_OK( rc = m_pFileHdl->SectorWrite( uiFileOffset, uiBufBytes, + pucOldBuffer, + m_uiBufferSize, pAsyncBuf, &uiBytesWritten, + FALSE))) + { + if( m_bKeepRflFiles) + { + f_mutexLock( gv_FlmSysData.hShareMutex); + + if( m_pFile->uiFileExtendSize) + { + FLMUINT uiTmpSize; + + uiTmpSize = (uiFileOffset % m_pFile->uiFileExtendSize) + + (FLMUINT)flmRoundUp( uiBufBytes, + m_pFileHdl->GetSectorSize()); + + if( uiTmpSize > m_pFile->uiFileExtendSize) + { + m_pFile->ui64RflDiskUsage += m_pFile->uiFileExtendSize; + } + } + else + { + m_pFile->ui64RflDiskUsage += uiBytesWritten; + } + + f_mutexUnlock( gv_FlmSysData.hShareMutex); + } + } + + if (m_uiRflWriteBufs == 1) + { + + // We are counting on the fact that the write completed. When we + // only have one buffer, we cannot do async writes. + + flmAssert( !pAsyncBuf); + if (RC_OK( rc) && !bFinalWrite) + { + copyLastSector( pBuffer, pucOldBuffer, pucOldBuffer, + uiCurrPacketLen, bStartingNewFile); + } + + // DO NOT call notifyComplete - that would put + // pBuffer->pIOBuffer into the avail list, and we don't want + // that. We simply want to keep reusing it. + + } + else + { + + // No need to call copyLastSector, because it was called above + // before calling SectorWrite. The part of the old buffer that + // needs to be transferred to the new buffer has already been + // transferred. + + if (!pAsyncBuf) + { + pBuffer->pIOBuffer->notifyComplete( rc); + } + + pBuffer->pIOBuffer = pNewBuffer; + } + + if (RC_BAD( rc)) + { + + // Remap disk full error + + if (rc == FERR_IO_DISK_FULL) + { + rc = RC_SET( FERR_RFL_DEVICE_FULL); + m_bRflVolumeFull = TRUE; + } + + m_bRflVolumeOk = FALSE; + goto Exit; + } + } + +Exit: + + return (rc); +} + +/******************************************************************** +Desc: Switch buffers. This routine assumes the m_hBufMutex is locked. +*********************************************************************/ +void F_Rfl::switchBuffers(void) +{ + RFL_BUFFER * pOldBuffer = m_pCurrentBuf; + + if (m_pCurrentBuf == &m_Buf1) + { + m_pCurrentBuf = &m_Buf2; + } + else + { + m_pCurrentBuf = &m_Buf1; + } + + m_pCurrentBuf->bTransInProgress = pOldBuffer->bTransInProgress; + m_pCurrentBuf->uiCurrFileNum = pOldBuffer->uiCurrFileNum; + m_pCurrentBuf->uiRflBufBytes = pOldBuffer->uiRflBufBytes; + m_pCurrentBuf->uiRflFileOffset = pOldBuffer->uiRflFileOffset; + if (pOldBuffer->uiRflBufBytes) + { + copyLastSector( m_pCurrentBuf, pOldBuffer->pIOBuffer->m_pucBuffer, + m_pCurrentBuf->pIOBuffer->m_pucBuffer, 0, FALSE); + } +} + +/******************************************************************** +Desc: Wait for all RFL transaction writes to be finished. The caller + has the write lock on the database, which will prevent further + writes to the RFL. +*********************************************************************/ +FLMBOOL F_Rfl::seeIfRflWritesDone( + FLMBOOL bForceWait) +{ + FLMBOOL bWritesDone; + + f_mutexLock( m_hBufMutex); + + if (!bForceWait) + { + bWritesDone = (FLMBOOL) ((m_pCurrentBuf->pFirstWaiter || m_pCommitBuf) + ? FALSE + : TRUE); + f_mutexUnlock( m_hBufMutex); + } + else + { + + // If the current buffer has a waiter, add self to that list to + // wait, because it will be notified after the commit buffer has + // been notified. Otherwise, if there is a commit in progress, add + // self to that list to wait. + + if (m_pCurrentBuf->pFirstWaiter) + { + + // If bTransInProgress is TRUE and m_pCommitBuf is NULL then + // this thread is the current transaction, and nobody is going to + // wake up the first waiter until we are done! Hence, we must + // wake him up. + + if (!m_pCommitBuf) + { + + // If m_pCommitBuf is NULL, this could only be possible if + // there is a transaction in progress. Otherwise, there would + // not have been a pFirstWaiter, because when the commit + // buffer finishes writing, if there is a waiter, it will set + // commitbuf=currentbuf if there is no transaction active. + + flmAssert( m_pCurrentBuf->bTransInProgress); + + m_pCommitBuf = m_pCurrentBuf; + switchBuffers(); + wakeUpWaiter( FERR_OK, TRUE); + (void) waitForWrites( m_pCommitBuf, FALSE); + } + else + { + FLMBOOL bSaveTransInProgress = m_pCurrentBuf->bTransInProgress; + + // Must set bTransInProgress to FALSE so that when the writer + // of m_pCommitBuf finishes, it will signal the first waiter + // on m_pCurrentBuf. If we don't do this, m_pCommitBuf will + // simply be set to NULL, and the first waiter will never be + // woke up. + + m_pCurrentBuf->bTransInProgress = FALSE; + (void) waitForWrites( m_pCurrentBuf, FALSE); + + // It is OK to restore the trans in progress flag to what it + // was before, because whoever called this routine has a lock + // on the database, and it is his trans-in-progress state that + // should be preserved. No other thread will have been able to + // change that state because the database is locked. + + f_mutexLock( m_hBufMutex); + m_pCurrentBuf->bTransInProgress = bSaveTransInProgress; + f_mutexUnlock( m_hBufMutex); + } + } + else if (m_pCommitBuf) + { + (void) waitForWrites( m_pCommitBuf, FALSE); + } + else + { + f_mutexUnlock( m_hBufMutex); + } + + bWritesDone = TRUE; + } + + return (bWritesDone); +} + +/******************************************************************** +Desc: Wake up the first thread that is waiting on the commit buffer. +*********************************************************************/ +void F_Rfl::wakeUpWaiter( + RCODE rc, + FLMBOOL bIsWriter // Only used for debug + ) +{ + F_SEM hESem; + +#ifndef FLM_DEBUG + F_UNREFERENCED_PARM( bIsWriter); +#else + if (bIsWriter) + { + flmAssert( m_pCommitBuf->pFirstWaiter->bIsWriter); + } + else + { + flmAssert( !m_pCommitBuf->pFirstWaiter->bIsWriter); + } +#endif + + *(m_pCommitBuf->pFirstWaiter->pRc) = rc; + hESem = m_pCommitBuf->pFirstWaiter->hESem; + if ((m_pCommitBuf->pFirstWaiter = m_pCommitBuf->pFirstWaiter->pNext) == NULL) + { + m_pCommitBuf->pLastWaiter = NULL; + } + + f_semSignal( hESem); +} + +/******************************************************************** +Desc: Wait for the transaction writes to be finished. +*********************************************************************/ +RCODE F_Rfl::completeTransWrites( + FDB * pDb, + FLMBOOL bCommitting, + FLMBOOL bOkToUnlock + ) +{ + RCODE rc = FERR_OK; + RCODE tmpRc; + FLMBOOL bMutexLocked = FALSE; + FLMBOOL bNotifyWaiters = FALSE; + FLMBOOL bDbUnlocked = FALSE; + DB_STATS * pDbStats = NULL; + F_TMSTAMP StartTime; + + f_mutexLock( m_hBufMutex); + bMutexLocked = TRUE; + m_pCurrentBuf->bTransInProgress = FALSE; + + flmAssert( pDb->uiFlags & FDB_HAS_WRITE_LOCK); + + // If we are not logging, we are probably recovering or restoring the + // database. All we need to do in this case is write out the log + // header. + + if (pDb->uiFlags & FDB_REPLAYING_RFL) + { + if (pDb->bHadUpdOper && m_pCurrentBuf->bOkToWriteHdrs) + { + f_mutexUnlock( m_hBufMutex); + bMutexLocked = FALSE; + if (RC_BAD( rc = flmWriteLogHdr( pDb->pDbStats, pDb->pSFileHdl, + pDb->pFile, m_pCurrentBuf->ucLogHdr, m_pCurrentBuf->ucCPHdr, + FALSE))) + { + flmSetMustCloseFlags( pDb->pFile, rc, FALSE); + } + } + + goto Exit; + } + + // Handle empty transactions differently. These transactions should + // not do any writing and do not need to wait for all writes to + // complete, unless the bOkToUnlock flag is set to FALSE. In that case + // they must wait for all writes to complete before unlocking. + + if (!pDb->bHadUpdOper) + { + + // If the current buffer has a waiter, add self to that list to + // wait, because it will be notified after the commit buffer has + // been notified. Otherwise, if there is a commit in progress, add + // self to that list to wait. + + if (m_pCurrentBuf->pFirstWaiter) + { + + // If m_pCommitBuf is NULL then nobody is going to wake up the + // first waiter - we must do it. + + if (!m_pCommitBuf) + { + if (bOkToUnlock) + { + flmUnlinkDbFromTrans( pDb, bCommitting); + bDbUnlocked = TRUE; + } + + m_pCommitBuf = m_pCurrentBuf; + switchBuffers(); + wakeUpWaiter( FERR_OK, TRUE); + + if (!bOkToUnlock) + { + bMutexLocked = FALSE; + (void) waitForWrites( m_pCommitBuf, FALSE); + } + } + else if (!bOkToUnlock) + { + bMutexLocked = FALSE; + (void) waitForWrites( m_pCurrentBuf, FALSE); + } + } + else if (m_pCommitBuf) + { + if (!bOkToUnlock) + { + bMutexLocked = FALSE; + rc = waitForWrites( m_pCommitBuf, FALSE); + } + } + + goto Exit; + } + + // If there is a transaction committing, put self into the wait list + // on the current buffer. When the committer finishes, he will wake up + // the first thread in the list and that thread will commit the buffer. + + if (m_pCommitBuf) + { + FLMBOOL bIsWriter; + + // Another thread has to be doing the writes to m_pCommitBuf, which + // means that m_pCurrentBuf better not be equal to m_pCommitBuf. + + flmAssert( m_pCommitBuf != m_pCurrentBuf); + + // If there are no waiters, we are the first one, so when we get + // signaled, we should proceed and do the write. + + bIsWriter = m_pCurrentBuf->pFirstWaiter ? FALSE : TRUE; + if (bOkToUnlock) + { + flmUnlinkDbFromTrans( pDb, bCommitting); + bDbUnlocked = TRUE; + } + + bMutexLocked = FALSE; + rc = waitForWrites( m_pCurrentBuf, bIsWriter); + + // If we were the first one in the queue, we must now do the write. + + if (!bIsWriter) + { + goto Exit; + } + + // First one in the queue, fall through to do the write. The thread + // that woke me up better have set m_pCommitBuf See below. + + flmAssert( m_pCommitBuf); + } + else if (m_pCurrentBuf->pFirstWaiter) + { + + // Another thread is ready to commit the next set of buffers, but + // just needs to be woke up. + + if (bOkToUnlock) + { + flmUnlinkDbFromTrans( pDb, bCommitting); + bDbUnlocked = TRUE; + } + + // Need to set things up for that first waiter and get him going. + + m_pCommitBuf = m_pCurrentBuf; + switchBuffers(); + wakeUpWaiter( rc, TRUE); + + // Wait for the write to be completed. + + bMutexLocked = FALSE; + rc = waitForWrites( m_pCommitBuf, FALSE); + goto Exit; + } + else + { + m_pCommitBuf = m_pCurrentBuf; + switchBuffers(); + if (bOkToUnlock) + { + flmUnlinkDbFromTrans( pDb, bCommitting); + bDbUnlocked = TRUE; + } + + f_mutexUnlock( m_hBufMutex); + bMutexLocked = FALSE; + } + + // NOTE: From this point on we use tmpRc because we don't want to lose + // the rc that may have been set above in the call to waitForWrites; + // At this point the mutex better not be locked. + flmAssert( !bMutexLocked); + bNotifyWaiters = TRUE; + + if ((pDbStats = pDb->pDbStats) != NULL) + { + f_timeGetTimeStamp( &StartTime); + } + + // Must write out whatever we have in the commit buffer before + // unlocking the database. + + if (RC_BAD( tmpRc = flush( m_pCommitBuf, TRUE))) + { + if (RC_OK( rc)) + { + rc = tmpRc; + } + + goto Exit; + } + + // Wait for any pending IO off of the log buffer + + if (RC_BAD( tmpRc = m_pCommitBuf->pBufferMgr->waitForAllPendingIO())) + { + if (RC_OK( rc)) + { + rc = tmpRc; + } + + goto Exit; + } + + // Force the RFL writes to disk if necessary. NOTE: It is possible for + // m_pFileHdl to be NULL at this point if there were no operations + // actually logged. This happens in FlmDbUpgrade (see flconvrt.cpp). + // Even though nothing was logged the transaction is not an empty + // transaction, because it still needs to write out the log header. + + if (m_pFileHdl) + { + if (RC_BAD( tmpRc = m_pFileHdl->Flush())) + { + + // Remap disk full error + + if (tmpRc == FERR_IO_DISK_FULL) + { + rc = RC_SET( FERR_RFL_DEVICE_FULL); + m_bRflVolumeFull = TRUE; + } + else if (RC_OK( rc)) + { + rc = tmpRc; + } + + m_bRflVolumeOk = FALSE; + goto Exit; + } + } + + // Write the log header + + if (m_pCommitBuf->bOkToWriteHdrs) + { + if (RC_BAD( tmpRc = flmWriteLogHdr( pDb->pDbStats, pDb->pSFileHdl, + pDb->pFile, m_pCommitBuf->ucLogHdr, m_pCommitBuf->ucCPHdr, + FALSE))) + { + if (RC_OK( rc)) + { + rc = tmpRc; + } + + flmSetMustCloseFlags( pDb->pFile, tmpRc, FALSE); + goto Exit; + } + } + +Exit: + + if (!bDbUnlocked && bOkToUnlock) + { + flmUnlinkDbFromTrans( pDb, bCommitting); + } + + if (bNotifyWaiters) + { + FLMUINT uiNumFinished = 1; // For self + + flmAssert( !bMutexLocked); + f_mutexLock( m_hBufMutex); + bMutexLocked = TRUE; + + // Wake up any waiters + + while (m_pCommitBuf->pFirstWaiter) + { + uiNumFinished++; + wakeUpWaiter( rc, FALSE); + } + + // If there are waiters on the current buffer, the first one should + // be woke up so it can start the next set of writes. + + if (m_pCurrentBuf->pFirstWaiter && !m_pCurrentBuf->bTransInProgress) + { + flmAssert( m_pCurrentBuf != m_pCommitBuf); + m_pCommitBuf = m_pCurrentBuf; + switchBuffers(); + wakeUpWaiter( rc, TRUE); + } + else + { + m_pCommitBuf = NULL; + } + + if (pDbStats) + { + flmAddElapTime( &StartTime, + &pDbStats->UpdateTransStats.GroupCompletes.ui64ElapMilli); + pDbStats->UpdateTransStats.GroupCompletes.ui64Count++; + pDbStats->bHaveStats = TRUE; + pDbStats->UpdateTransStats.ui64GroupFinished += uiNumFinished; + } + } + + if (bMutexLocked) + { + f_mutexUnlock( m_hBufMutex); + } + + return (rc); +} + +/******************************************************************** +Desc: Calculate the checksum for a packet. +*********************************************************************/ +FLMBYTE RflCalcChecksum( + const FLMBYTE * pucPacket, + FLMUINT uiPacketBodyLen) +{ + FLMUINT uiBytesToChecksum; + FLMUINT uiChecksum = 0; + FLMBYTE ucTmp; + const FLMBYTE * pucStart; + const FLMBYTE * pucEnd; + const FLMBYTE * pucSectionEnd; + const FLMBYTE * pucCur; + + // Checksum is calculated for every byte in the packet that comes + // after the checksum byte. + + pucStart = &pucPacket[RFL_PACKET_CHECKSUM_OFFSET + 1]; + uiBytesToChecksum = (FLMUINT)(uiPacketBodyLen + + RFL_PACKET_OVERHEAD - + (RFL_PACKET_CHECKSUM_OFFSET + 1)); + + pucCur = pucStart; + pucEnd = pucStart + uiBytesToChecksum; + +#ifdef FLM_64BIT + pucSectionEnd = pucStart + (sizeof( FLMUINT) - ((FLMUINT)pucStart & 0x7)); +#else + pucSectionEnd = pucStart + (sizeof( FLMUINT) - ((FLMUINT)pucStart & 0x3)); +#endif + + if (pucSectionEnd > pucEnd) + { + pucSectionEnd = pucEnd; + } + + while (pucCur < pucSectionEnd) + { + uiChecksum = (uiChecksum << 8) +*pucCur++; + } + +#ifdef FLM_64BIT + pucSectionEnd = (FLMBYTE *)((FLMUINT)pucEnd & 0xFFFFFFFFFFFFFFF8); +#else + pucSectionEnd = (FLMBYTE *)((FLMUINT)pucEnd & 0xFFFFFFFC); +#endif + + while (pucCur < pucSectionEnd) + { + uiChecksum ^= *((FLMUINT *) pucCur); + pucCur += sizeof(FLMUINT); + } + + while (pucCur < pucEnd) + { + uiChecksum ^= *pucCur++; + } + + ucTmp = (FLMBYTE) uiChecksum; + + uiChecksum >>= 8; + ucTmp ^= (FLMBYTE) uiChecksum; + + uiChecksum >>= 8; + ucTmp ^= (FLMBYTE) uiChecksum; + +#ifdef FLM_64BIT + uiChecksum >>= 8; + ucTmp ^= (FLMBYTE)uiChecksum; + + uiChecksum >>= 8; + ucTmp ^= (FLMBYTE)uiChecksum; + + uiChecksum >>= 8; + ucTmp ^= (FLMBYTE)uiChecksum; + + uiChecksum >>= 8; + ucTmp ^= (FLMBYTE)uiChecksum; +#endif + ucTmp ^= (FLMBYTE) (uiChecksum >> 8); + uiChecksum = ucTmp; + + if ((uiChecksum = ucTmp) == 0) + { + uiChecksum = 1; + } + + return ((FLMBYTE) uiChecksum); +} + +/******************************************************************** +Desc: Flush all completed packets out of the RFL buffer, and shift + the new partial packet down. This guarantees that there is + now room in the buffer for the maximum packet size. +*********************************************************************/ +RCODE F_Rfl::shiftPacketsDown( + FLMUINT uiCurrPacketLen, + FLMBOOL bStartingNewFile) +{ + RCODE rc = FERR_OK; + + // The call to flush will move whatever needs to be moved from the + // current buffer into a new buffer if multiple buffers are being used. + // If only one buffer is being used, it will move the part of the + // packet that needs to be moved down to the beginning of the buffer - + // AFTER writing out the buffer. + + if (RC_BAD( rc = flush( m_pCurrentBuf, FALSE, uiCurrPacketLen, + bStartingNewFile))) + { + goto Exit; + } + + // NOTE: If multiple buffers are being used, whatever was moved to the + // new buffer has not yet been written out + + if (bStartingNewFile) + { + if (RC_BAD( rc = waitPendingWrites())) + { + goto Exit; + } + } + +Exit: + + return (rc); +} + +/******************************************************************** +Desc: Determine if we should start a new file. If we are over the + low limit, and the bDoNewIfOverLowLimit flag is set, we + will start a new log file. Or, if this packet size would + put us over the upper limit, we will start a new log file. +*********************************************************************/ +RCODE F_Rfl::seeIfNeedNewFile( + FLMUINT uiPacketLen, + FLMBOOL bDoNewIfOverLowLimit) +{ + RCODE rc = FERR_OK; + FLMBYTE ucNextSerialNum[F_SERIAL_NUM_SIZE]; + + flmAssert( m_pFile); + + // If the keep files flag is FALSE, we won't start a new file. NOTE: + // This should ALWAYS be false for pre 4.3 databases. + + if (!m_bKeepRflFiles) + { + goto Exit; // Should return FERR_OK; + } + + // VERY IMPORTANT NOTE: It is preferrable that we keep transactions + // entirely contained in the same RFL file if at all possible. Note + // that it is NOT a hard and fast requirement. The system will work + // just fine if we don't. However, it would be nice if RFL files always + // ended with a commit or abort packet. This preferences is due to what + // happens after a restore operation. After a restore operation, we + // always need to start a new RFL file, but if possible, we would like + // that new RFL file to be the next one in the sequence after the last + // RFL file that was restored. We can only do this if we were able to + // restore EVERY transaction that was in the last restored RFL file - + // which we can only do if the last restored RFL file ended with a + // commit or abort packet. To accomplish this end, we try to roll to + // new files on the first transaction begin packet that occurs after we + // have exceeded our low threshold - which is why bDoNewIfOverLowLimit + // is only set to TRUE on transaction begin packets. It is set to FALSE + // on other packets so that we will continue logging the transaction in + // the same file that we started the transaction in - if possible. The + // only thing that will cause a non-transaction-begin packet to roll to + // a new file is if we would exceed the high limit. + + if ((bDoNewIfOverLowLimit && + m_pCurrentBuf->uiRflFileOffset + m_pCurrentBuf->uiRflBufBytes >= + m_uiRflMinFileSize) || + (m_pCurrentBuf->uiRflFileOffset + m_pCurrentBuf->uiRflBufBytes + + uiPacketLen >= m_uiRflMaxFileSize)) + { + FLMUINT uiCurrFileEOF = m_pCurrentBuf->uiRflFileOffset + + m_pCurrentBuf->uiRflBufBytes; + + // Shift the current packet to the beginning of the buffer. Any + // packets in the buffer before that one will be written out to the + // current file. + + if (RC_BAD( rc = shiftPacketsDown( uiPacketLen, TRUE))) + { + goto Exit; + } + + // Update the header of the current file and close it. + + if (RC_BAD( rc = writeHeader( m_pCurrentBuf->uiCurrFileNum, uiCurrFileEOF, + m_ucCurrSerialNum, m_ucNextSerialNum, TRUE))) + { + goto Exit; + } + + // Truncate the file. + + if (!ON_512_BYTE_BOUNDARY( uiCurrFileEOF)) + { + uiCurrFileEOF = ROUND_DOWN_TO_NEAREST_512( uiCurrFileEOF) + 512; + } + + if (RC_BAD( rc = m_pFileHdl->Truncate( uiCurrFileEOF))) + { + goto Exit; + } + + // Close the file handle. + + m_pFileHdl->Close(); + m_pFileHdl->Release(); + m_pFileHdl = NULL; + + // Get the next serial number that will be used for the RFL file + // after this one. + + if (RC_BAD( rc = f_createSerialNumber( ucNextSerialNum))) + { + goto Exit; + } + + // Create next file in the sequence. Use the next serial number + // stored in the FDB's log header for the serial number on this RFL + // file. Use the serial number we just generated as the next RFL + // serial number. + + if (RC_BAD( rc = createFile( m_pCurrentBuf->uiCurrFileNum + 1, + m_ucNextSerialNum, ucNextSerialNum, TRUE))) + { + goto Exit; + } + + // Move the next serial number to the current serial number and the + // serial number we generated above into the next serial number. + + f_memcpy( m_ucCurrSerialNum, m_ucNextSerialNum, F_SERIAL_NUM_SIZE); + f_memcpy( m_ucNextSerialNum, ucNextSerialNum, F_SERIAL_NUM_SIZE); + } + +Exit: + + return (rc); +} + +/******************************************************************** +Desc: Finish the current RFL file - set up so that next transaction + will begin a new RFL file. +********************************************************************/ +RCODE F_Rfl::finishCurrFile( + FDB * pDb, + FLMBOOL bNewKeepState) +{ + RCODE rc = FERR_OK; + FLMBOOL bDbLocked = FALSE; + FLMUINT uiTransFileNum; + FLMUINT uiTransOffset; + FLMUINT uiTruncateSize; + FLMBYTE * pucUncommittedLogHdr; + FLMBYTE ucCheckpointLogHdr[ LOG_HEADER_SIZE]; + + // Make sure we don't have a transaction going + + if (pDb->uiTransType != FLM_NO_TRANS) + { + rc = RC_SET( FERR_TRANS_ACTIVE); + goto Exit; + } + + // Make sure there is no active backup running + + f_mutexLock( gv_FlmSysData.hShareMutex); + if (m_pFile->bBackupActive) + { + f_mutexUnlock( gv_FlmSysData.hShareMutex); + rc = RC_SET( FERR_BACKUP_ACTIVE); + goto Exit; + } + + f_mutexUnlock( gv_FlmSysData.hShareMutex); + + // Lock the database - need to prevent update transactions and + // checkpoint thread from running. + + if (RC_BAD( rc = dbLock( pDb, FLM_NO_TIMEOUT))) + { + goto Exit; + } + + bDbLocked = TRUE; + + // Must wait for all RFL writes before switching files. + + (void) seeIfRflWritesDone( TRUE); + + // Better not be in the middle of logging unknown stuff for the + // application + + flmAssert( !m_bLoggingUnknown); + + // Better not be in the middle of a transaction. + + flmAssert( !m_uiCurrTransID); + + // If DB version is less than 4.3 we cannot do this. + + if (m_pFile->FileHdr.uiVersionNum < FLM_FILE_FORMAT_VER_4_3) + { + goto Exit; // Will return FERR_OK + } + + pucUncommittedLogHdr = &m_pFile->ucUncommittedLogHdr[0]; + + // Don't want to copy last committed log header into uncommitted log + // header if bNewKeepState is TRUE because the caller has already done + // it, and has made modifications to the uncommitted log header that we + // don't want to lose. + + if (!bNewKeepState) + { + f_memcpy( pucUncommittedLogHdr, m_pFile->ucLastCommittedLogHdr, + LOG_HEADER_SIZE); + + // If we are in a no-keep state, but we were not told that we have + // a new keep state, we cannot roll to the next RFL file, because a + // checkpoint has not been done. This is not an error - it is just + // the case where FlmDbConfig was asked to roll to the next RFL file + // when the keep flag was still FALSE. + + if (!pucUncommittedLogHdr[LOG_KEEP_RFL_FILES]) + { + goto Exit; // Will return FERR_OK + } + } + + // Get the last committed serial numbers from the file's log header + // buffer. + + f_memcpy( m_ucCurrSerialNum, + &pucUncommittedLogHdr[LOG_LAST_TRANS_RFL_SERIAL_NUM], + F_SERIAL_NUM_SIZE); + + f_memcpy( m_ucNextSerialNum, &pucUncommittedLogHdr[LOG_RFL_NEXT_SERIAL_NUM], + F_SERIAL_NUM_SIZE); + + uiTransFileNum = (FLMUINT) FB2UD( &pucUncommittedLogHdr[LOG_RFL_FILE_NUM]); + uiTransOffset = (FLMUINT) FB2UD( &pucUncommittedLogHdr[LOG_RFL_LAST_TRANS_OFFSET]); + + // If the LOG_RFL_LAST_TRANS_OFFSET is zero, there is no need to go + // set up to go to the next file, because we are already poised to do + // so at the beginning of the next transaction. Just return if this is + // the case. Same for if the file does not exist. + + if (!uiTransOffset) + { + if (!bNewKeepState) + { + goto Exit; // Will return FERR_OK + } + } + else if (RC_BAD( rc = openFile( uiTransFileNum, m_ucCurrSerialNum))) + { + if (rc == FERR_IO_PATH_NOT_FOUND || rc == FERR_IO_INVALID_PATH) + { + rc = FERR_OK; + if (!bNewKeepState) + { + goto Exit; + } + } + else + { + goto Exit; + } + } + else + { + + // At this point, we know the file exists, so we will update its + // header and then update the log header. Note that we use the keep + // RFL state from the last committed log header, not the uncommitted + // log header - because it will contain the correct keep-state for + // the current RFL file. + + if (RC_BAD( rc = writeHeader( m_pCurrentBuf->uiCurrFileNum, uiTransOffset, + m_ucCurrSerialNum, m_ucNextSerialNum, + m_pFile->ucLastCommittedLogHdr [LOG_KEEP_RFL_FILES] + ? TRUE + : FALSE))) + { + goto Exit; + } + + // Truncate the file down to its EOF size - the nearest 512 byte + // boundary. + + uiTruncateSize = uiTransOffset; + if (!ON_512_BYTE_BOUNDARY( uiTruncateSize)) + { + uiTruncateSize = ROUND_DOWN_TO_NEAREST_512( uiTruncateSize) + 512; + } + + if (RC_BAD( rc = m_pFileHdl->Truncate( uiTruncateSize))) + { + goto Exit; + } + + // Close the file handle. + + m_pFileHdl->Close(); + m_pFileHdl->Release(); + m_pFileHdl = NULL; + + // Set things up in the log header to go to the next file when we + // begin the next transaction. NOTE: NO need to lock the mutex, + // because nobody but an update transaction looks at the uncommitted + // log header. + + uiTransFileNum++; + UD2FBA( (FLMUINT32) uiTransFileNum, + &pucUncommittedLogHdr[LOG_RFL_FILE_NUM]); + } + + // Generate a new current serial number if bNewKeepState is TRUE. + // Otherwise, move the next serial number into the current serial + // number. + + if (bNewKeepState) + { + if (RC_BAD( rc = f_createSerialNumber( m_ucCurrSerialNum))) + { + goto Exit; + } + } + else + { + f_memcpy( m_ucCurrSerialNum, m_ucNextSerialNum, F_SERIAL_NUM_SIZE); + } + + // Always generate a new next serial number. + + if (RC_BAD( rc = f_createSerialNumber( m_ucNextSerialNum))) + { + goto Exit; + } + + // Set transaction offset to zero. This will force the next RFL file + // to be created on the next transaction begin. It will be created even + // if it is already there. + + UD2FBA( (FLMUINT32) 0, &pucUncommittedLogHdr[LOG_RFL_LAST_TRANS_OFFSET]); + + f_memcpy( &pucUncommittedLogHdr[LOG_LAST_TRANS_RFL_SERIAL_NUM], + m_ucCurrSerialNum, F_SERIAL_NUM_SIZE); + + f_memcpy( &pucUncommittedLogHdr[LOG_RFL_NEXT_SERIAL_NUM], m_ucNextSerialNum, + F_SERIAL_NUM_SIZE); + + // Set the CP file number and CP offset to point into the new file. + // The outer code (FlmDbConfig) has done a checkpoint and the database + // is still locked. We need to set these values here, otherwise if we + // crash before the next checkpoint, recovery will start in the old RFL + // file, causing an FERR_BAD_RFL_SERIAL_NUM to be returned when + // traversing from the old RFL file to the new RFL file. NOTE: These + // changes must be made to the uncommitted log header AND the CP log + // header (so that they will be written out even though we are not + // forcing a checkpoint). + + if (bNewKeepState) + { +#ifdef FLM_DEBUG + // Do a quick check to see if it looks like we are in a + // checkpointed state + + if (!m_pFile->ucLastCommittedLogHdr[LOG_KEEP_RFL_FILES] && + (FLMUINT) FB2UD( &m_pFile->ucLastCommittedLogHdr[ + LOG_RFL_LAST_TRANS_OFFSET]) > 512) + { + flmAssert( 0); + } +#endif + + f_memcpy( ucCheckpointLogHdr, m_pFile->ucCheckpointLogHdr, + LOG_HEADER_SIZE); + + UD2FBA( (FLMUINT32) uiTransFileNum, + &ucCheckpointLogHdr[LOG_RFL_LAST_CP_FILE_NUM]); + + UD2FBA( (FLMUINT32) uiTransFileNum, + &pucUncommittedLogHdr[LOG_RFL_LAST_CP_FILE_NUM]); + + UD2FBA( (FLMUINT32) 512, &ucCheckpointLogHdr[LOG_RFL_LAST_CP_OFFSET]); + + UD2FBA( (FLMUINT32) 512, &pucUncommittedLogHdr[LOG_RFL_LAST_CP_OFFSET]); + } + + // Write out the log header to disk. + + if (RC_BAD( rc = flmWriteLogHdr( pDb->pDbStats, pDb->pSFileHdl, + m_pFile, pucUncommittedLogHdr, + bNewKeepState + ? ucCheckpointLogHdr + : m_pFile->ucCheckpointLogHdr, FALSE))) + { + goto Exit; + } + + // Copy the uncommitted log header back to the committed log header + // and copy the CP log header (if changed above). + + f_mutexLock( gv_FlmSysData.hShareMutex); + f_memcpy( m_pFile->ucLastCommittedLogHdr, pucUncommittedLogHdr, + LOG_HEADER_SIZE); + + if (bNewKeepState) + { + f_memcpy( m_pFile->ucCheckpointLogHdr, ucCheckpointLogHdr, + LOG_HEADER_SIZE); + } + + f_mutexUnlock( gv_FlmSysData.hShareMutex); + +Exit: + + if (bDbLocked) + { + (void) dbUnlock( pDb); + } + + return (rc); +} + +/******************************************************************** +Desc: Finish packet by outputting header information for it. +*********************************************************************/ +RCODE F_Rfl::finishPacket( + FLMUINT uiPacketType, + FLMUINT uiPacketBodyLen, + FLMBOOL bDoNewIfOverLowLimit) +{ + RCODE rc = FERR_OK; + FLMUINT uiEncryptPacketBodyLen; + FLMUINT uiPacketLen; + FLMBYTE * pucPacket; + + // Encrypt the packet body, if requested. + + uiEncryptPacketBodyLen = getEncryptPacketBodyLen( uiPacketType, + uiPacketBodyLen); + uiPacketLen = uiEncryptPacketBodyLen + RFL_PACKET_OVERHEAD; + + // See if this packet will cause us to overflow the limits on the + // current file. If so, create a new file. + + if (RC_BAD( rc = seeIfNeedNewFile( uiPacketLen, bDoNewIfOverLowLimit))) + { + goto Exit; + } + + // Get a pointer to packet header. + + pucPacket = &(m_pCurrentBuf->pIOBuffer->m_pucBuffer[ + m_pCurrentBuf->uiRflBufBytes]); + + // Set the packet address in the packet header. + + m_uiPacketAddress = m_pCurrentBuf->uiRflFileOffset + + m_pCurrentBuf->uiRflBufBytes; + + UD2FBA( (FLMUINT32) m_uiPacketAddress, &pucPacket[ + RFL_PACKET_ADDRESS_OFFSET]); + + // Set the packet type and packet body length. + + pucPacket[RFL_PACKET_TYPE_OFFSET] = (FLMBYTE) uiPacketType; + + UW2FBA( (FLMUINT16) uiPacketBodyLen, + &pucPacket[RFL_PACKET_BODY_LENGTH_OFFSET]); + + // Set the checksum for the packet. + + pucPacket[RFL_PACKET_CHECKSUM_OFFSET] = + RflCalcChecksum( pucPacket, uiEncryptPacketBodyLen); + + // Increment bytes in the buffer to reflect the fact that this packet + // is now complete. + + m_pCurrentBuf->uiRflBufBytes += uiPacketLen; + +Exit: + + return (rc); +} + +/******************************************************************** +Desc: Truncate roll-forward log file to a certain size - only do if + not keeping RFL files. +*********************************************************************/ +RCODE F_Rfl::truncate( + FLMUINT uiTruncateSize) +{ + RCODE rc = FERR_OK; + FLMUINT uiFileNum; + + flmAssert( uiTruncateSize >= 512); + + // Keeping of log files better not be enabled. + + flmAssert( !m_pFile->ucLastCommittedLogHdr[LOG_KEEP_RFL_FILES]); + + // Better not be in the middle of logging unknown stuff for the + // application + + flmAssert( !m_bLoggingUnknown); + + // Better not be in the middle of a transaction. + + flmAssert( !m_uiCurrTransID); + + // Open the current RFL file. If it does not exist, it is OK - there + // is nothing to truncate. + + uiFileNum = (FLMUINT) FB2UD( &m_pFile->ucLastCommittedLogHdr[ + LOG_RFL_FILE_NUM]); + + if (RC_BAD( rc = openFile( uiFileNum, &m_pFile->ucLastCommittedLogHdr[ + LOG_LAST_TRANS_RFL_SERIAL_NUM]))) + { + if (rc == FERR_IO_PATH_NOT_FOUND || rc == FERR_IO_INVALID_PATH) + { + rc = FERR_OK; + } + + goto Exit; + } + + if (RC_BAD( rc = m_pFileHdl->Truncate( uiTruncateSize))) + { + m_bRflVolumeOk = FALSE; + goto Exit; + } + +Exit: + + return (rc); +} + +/******************************************************************** +Desc: Setup to begin a transaction +*********************************************************************/ +RCODE F_Rfl::setupTransaction(void) +{ + RCODE rc = FERR_OK; + FLMUINT uiFileNum; + FLMUINT uiLastTransOffset; + FLMBOOL bCreateFile; + + f_mutexLock( m_hBufMutex); + m_pCurrentBuf->bTransInProgress = TRUE; + f_mutexUnlock( m_hBufMutex); + + // Get the last committed serial numbers from the file's log header + // buffer. + + f_memcpy( m_ucCurrSerialNum, + &m_pFile->ucLastCommittedLogHdr[LOG_LAST_TRANS_RFL_SERIAL_NUM], + F_SERIAL_NUM_SIZE); + + f_memcpy( m_ucNextSerialNum, + &m_pFile->ucLastCommittedLogHdr[LOG_RFL_NEXT_SERIAL_NUM], + F_SERIAL_NUM_SIZE); + + uiFileNum = (FLMUINT) FB2UD( &m_pFile->ucLastCommittedLogHdr[ + LOG_RFL_FILE_NUM]); + + uiLastTransOffset = (FLMUINT) FB2UD( &m_pFile->ucLastCommittedLogHdr[ + LOG_RFL_LAST_TRANS_OFFSET]); + + // If the LOG_RFL_LAST_TRANS_OFFSET is zero, we need to create the + // next RFL file number no matter what. There are two cases where this + // happens: 1) when the database is first created, and 2) after a + // restore operation. + + if (!uiLastTransOffset) + { + bCreateFile = TRUE; + + // Close the current file, just in case we had opened it before. At + // this point, it doesn't matter because we are going to overwrite + // it. + + if (RC_BAD( rc = waitForCommit())) + { + goto Exit; + } + + closeFile(); + } + else if (RC_BAD( rc = openFile( uiFileNum, m_ucCurrSerialNum))) + { + if (rc != FERR_IO_PATH_NOT_FOUND && rc != FERR_IO_INVALID_PATH) + { + goto Exit; + } + + bCreateFile = TRUE; + } + else + { + bCreateFile = FALSE; + } + + if (bCreateFile) + { + + // If the log header indicates that data has already been logged to + // the file, we need to return the I/O error rather than just + // re-creating the file. This may mean that someone changed the RFL + // directory without moving the RFL files properly. + + if (uiLastTransOffset > 512) + { + rc = RC_SET( FERR_RFL_FILE_NOT_FOUND); + goto Exit; + } + + // Create the RFL file if not found. Use the next serial number + // stored in the FDB's log header for the serial number on this RFL + // file. Use the serial number we just generated as the next RFL + // serial number. + + if (RC_BAD( rc = createFile( uiFileNum, + m_ucCurrSerialNum, m_ucNextSerialNum, + m_pFile->ucLastCommittedLogHdr [LOG_KEEP_RFL_FILES] + ? TRUE + : FALSE))) + { + goto Exit; + } + } + else + { + + // Read in enough of the buffer from the RFL file so that we are + // positioned on a 512 byte boundary. + + if (RC_BAD( positionTo( uiLastTransOffset))) + { + goto Exit; + } + } + + // These can only be changed when starting a transaction. + + if (m_pFile->FileHdr.uiVersionNum >= FLM_FILE_FORMAT_VER_4_3) + { + m_bKeepRflFiles = m_pFile->ucLastCommittedLogHdr[LOG_KEEP_RFL_FILES] + ? TRUE + : FALSE; + + m_uiRflMaxFileSize = (FLMUINT) FB2UD( &m_pFile->ucLastCommittedLogHdr[ + LOG_RFL_MAX_FILE_SIZE]); + + // Round maximum down to nearest 512 boundary. This is necessary + // because we always write a minimum of 512 byte units in direct IO + // mode. If we did not round the maximum down, our last packet could + // end at an offset that is less than the maximum, but greater than + // the nearest 512 byte boundary - technically within the + // user-specified size limit. However, because we always write a + // full 512 bytes of data to fill out the last sector when we are in + // direct IO mode, we would end up with a file that was slightly + // larger than the user-specified limit. The EOF in the header of + // the file would be below the limit, but the actual file size would + // not be. Thus, the need to round down. + + m_uiRflMaxFileSize = ROUND_DOWN_TO_NEAREST_512( m_uiRflMaxFileSize); + + // The maximum cannot go below a certain threshold - must have room + // for least one packet plus the header. + + if (m_uiRflMaxFileSize < RFL_MAX_PACKET_SIZE + 512) + { + m_uiRflMaxFileSize = RFL_MAX_PACKET_SIZE + 512; + } + else if (m_uiRflMaxFileSize > gv_FlmSysData.uiMaxFileSize) + { + m_uiRflMaxFileSize = gv_FlmSysData.uiMaxFileSize; + } + } + else + { + m_bKeepRflFiles = FALSE; + m_uiRflMaxFileSize = gv_FlmSysData.uiMaxFileSize; + } + + m_uiRflMinFileSize = (FLMUINT) FB2UD( &m_pFile->ucLastCommittedLogHdr[ + LOG_RFL_MIN_FILE_SIZE]); + + // Minimum RFL file size should not be allowed to be larger than + // maximum! + + if (m_uiRflMinFileSize > m_uiRflMaxFileSize) + { + m_uiRflMinFileSize = m_uiRflMaxFileSize; + } + + // Set the operation count to zero. + + m_uiOperCount = 0; + + // Set file extend sizes + + m_pFileHdl->setMaxAutoExtendSize( m_uiRflMaxFileSize); + m_pFileHdl->setExtendSize( m_pFile->uiFileExtendSize); + +Exit: + + return (rc); +} + +/******************************************************************** +Desc: Log transaction begin. This routine will also make sure + we have opened an RFL file. + NOTE: The prior version of FLAIM (before 4.3) would log + a time and set the RFL_TIME_LOGGED_FLAG bit in the packet + type. This is no longer done. Old code should be + compatible because it reads the flag. +*********************************************************************/ +RCODE F_Rfl::logBeginTransaction( + FDB * pDb) +{ + RCODE rc = FERR_OK; + FLMUINT uiDbVersion = pDb->pFile->FileHdr.uiVersionNum; + FLMBYTE * pucPacketBody; + FLMUINT uiPacketBodyLen; + FLMUINT uiGMTTime; + + // Do nothing if logging is disabled. + + if (m_bLoggingOff) + { + goto Exit; + } + + // Better not be in the middle of logging unknown stuff for the + // application + + flmAssert( !m_bLoggingUnknown); + + // Better not be in the middle of a transaction. + + flmAssert( !m_uiCurrTransID); + + if (RC_BAD( rc = setupTransaction())) + { + goto Exit; + } + + uiPacketBodyLen = uiDbVersion >= FLM_FILE_FORMAT_VER_4_31 ? 12 : 8; + + // Make sure we have space in the RFL buffer for a complete packet. + + if (!haveBuffSpace( uiPacketBodyLen + RFL_PACKET_OVERHEAD)) + { + if (RC_BAD( rc = flush( m_pCurrentBuf))) + { + goto Exit; + } + } + + // Get a pointer to where we will be laying down the packet body. + + pucPacketBody = getPacketBodyPtr(); + + // Output the transaction ID. + + UD2FBA( (FLMUINT32) pDb->LogHdr.uiCurrTransID, pucPacketBody); + pucPacketBody += 4; + + // This used to be a FLM_GET_TIMER() value in pre-4.3 code, but it was + // never really used. Set it to GMT time now. + + f_timeGetSeconds( &uiGMTTime); + UD2FBA( (FLMUINT32) uiGMTTime, pucPacketBody); + pucPacketBody += 4; + + // NOTE: In the pre-4.3 code the next four bytes would be zero, but + // that is really unnecessary. We will simply no longer set the + // RFL_TIME_LOGGED_FLAG bit in the packet type. Pre-4.3 code should be + // compatible. + + if (uiDbVersion >= FLM_FILE_FORMAT_VER_4_31) + { + FLMUINT uiLastLoggedCommitID; + + uiLastLoggedCommitID = FB2UD( + &m_pFile->ucLastCommittedLogHdr[LOG_LAST_RFL_COMMIT_ID]); + + UD2FBA( (FLMUINT32) uiLastLoggedCommitID, pucPacketBody); + pucPacketBody += 4; + + if (RC_BAD( rc = finishPacket( RFL_TRNS_BEGIN_EX_PACKET, uiPacketBodyLen, + TRUE))) + { + goto Exit; + } + } + else + { + if (RC_BAD( rc = finishPacket( RFL_TRNS_BEGIN_PACKET, uiPacketBodyLen, + TRUE))) + { + goto Exit; + } + } + + // Save the file offset for the start transaction packet. + + m_uiTransStartFile = m_pCurrentBuf->uiCurrFileNum; + m_uiTransStartAddr = m_pCurrentBuf->uiRflFileOffset + + m_pCurrentBuf->uiRflBufBytes - + uiPacketBodyLen - + RFL_PACKET_OVERHEAD; + m_uiCurrTransID = pDb->LogHdr.uiCurrTransID; + +Exit: + + return (rc); +} + +/******************************************************************** +Desc: Flushes the RFL and sets some things in the log header. +*********************************************************************/ +void F_Rfl::finalizeTransaction(void) +{ + FLMUINT uiRflTransEndOffset; + FLMBYTE * pucLogHdr = &m_pFile->ucUncommittedLogHdr[0]; + + // Save the serial numbers and file numbers into the file's + // uncommitted log header. + + UD2FBA( (FLMUINT32) m_pCurrentBuf->uiCurrFileNum, + &pucLogHdr[LOG_RFL_FILE_NUM]); + + uiRflTransEndOffset = getCurrWriteOffset(); + UD2FBA( (FLMUINT32) uiRflTransEndOffset, + &pucLogHdr[LOG_RFL_LAST_TRANS_OFFSET]); + + f_memcpy( &pucLogHdr[LOG_LAST_TRANS_RFL_SERIAL_NUM], m_ucCurrSerialNum, + F_SERIAL_NUM_SIZE); + + f_memcpy( &pucLogHdr[LOG_RFL_NEXT_SERIAL_NUM], m_ucNextSerialNum, + F_SERIAL_NUM_SIZE); +} + +/******************************************************************** +Desc: Handles the commit and abort log operations. If aborting + the transaction, or if the transaction was empty, we will + simply throw away the entire transaction and not bother + to log it. In that case we will reset transaction pointers, + etc. back to the file and offset where the transaction began. + We will also delete RFL files that were created during the + transaction if necessary. NOTE: It is not essential that + the RFL files be deleted. If they are not successfully + deleted, they will be overwritten if need be when creating + new ones. +Note: The prior version of FLAIM (before 4.3) would log + a time and set the RFL_TIME_LOGGED_FLAG bit in the packet + type. This is no longer done. Old code should be + compatible because it reads the flag. +*********************************************************************/ +RCODE F_Rfl::logEndTransaction( + FLMUINT uiPacketType, + FLMBOOL bThrowLogAway, + FLMBOOL * pbLoggedTransEnd) +{ + RCODE rc = FERR_OK; + RCODE rc2 = FERR_OK; + FLMUINT uiLowFileNum; + FLMUINT uiHighFileNum; + char szRflFileName[F_PATH_MAX_SIZE]; + FLMBYTE * pucPacketBody; + FLMUINT uiPacketBodyLen; + + // Better not be in the middle of logging unknown stuff for the + // application + + flmAssert( !m_bLoggingUnknown); + + // Initialize the "logged trans end" flag + + if (pbLoggedTransEnd) + { + *pbLoggedTransEnd = FALSE; + } + + // Do nothing if logging is disabled. + + if (m_bLoggingOff) + { + goto Exit; + } + + flmAssert( m_pFileHdl); + flmAssert( m_pFile); + + // If the transaction had no operations, throw it away - don't even + // log the packet. An abort operation may also elect to throw the log + // away even if there were operations. That is determined by the + // bThrowLogAway flag. The bThrowLogAway flag may be TRUE when doing a + // commit if the caller knows that nothing happened during the + // transction. + + if (bThrowLogAway || !m_uiOperCount) + { +Throw_Away_Transaction: + + // If we have switched files, delete all but the file we started in. + + if (m_pCurrentBuf->uiCurrFileNum != m_uiTransStartFile) + { + flmAssert( m_pCurrentBuf->uiCurrFileNum > m_uiTransStartFile); + + // File number in uncommitted log header better not have been + // changed yet. It is only supposed to be changed when the + // transaction finishes - i.e., in this routine. Up until this + // point, it should only be changed in + // m_pCurrentBuf->uiCurrFileNum. + + flmAssert( m_uiTransStartFile == (FLMUINT) FB2UD( + &m_pFile->ucUncommittedLogHdr[LOG_RFL_FILE_NUM])); + + uiLowFileNum = m_uiTransStartFile + 1; + uiHighFileNum = m_pCurrentBuf->uiCurrFileNum; + + // Close the current file so it can be deleted. + + if (RC_BAD( rc = waitForCommit())) + { + goto Exit; + } + + closeFile(); + + // Delete as many of the files as possible. Don't worry about + // errors here. + + while (uiLowFileNum <= uiHighFileNum) + { + if (RC_OK( getFullRflFileName( uiLowFileNum, szRflFileName))) + { + (void) gv_FlmSysData.pFileSystem->Delete( szRflFileName); + } + + uiLowFileNum++; + } + } + else + { + + // If we are in the file the transaction started in, simply + // reset to where the transaction started. + + if (RC_BAD( rc2 = positionTo( m_uiTransStartAddr))) + { + + // If we got to this point because of a "goto + // Throw_Away_Transaction", we don't want to clobber the + // original error code. So, we use rc2 temporarily and then + // determine if its value should be set into rc. + + if (RC_OK( rc)) + { + rc = rc2; + } + + rc2 = FERR_OK; + goto Exit; + } + } + } + else + { + + // Log a commit or abort packet. + + uiPacketBodyLen = 8; + + // Make sure we have space in the RFL buffer for a complete packet. + + if (!haveBuffSpace( uiPacketBodyLen + RFL_PACKET_OVERHEAD)) + { + if (RC_BAD( rc = flush( m_pCurrentBuf))) + { + goto Throw_Away_Transaction; + } + } + + // Get a pointer to where we will be laying down the packet body. + + pucPacketBody = getPacketBodyPtr(); + + // Output the transaction ID. + + UD2FBA( (FLMUINT32) m_uiCurrTransID, pucPacketBody); + pucPacketBody += 4; + UD2FBA( (FLMUINT32) m_uiTransStartAddr, pucPacketBody); + pucPacketBody += 4; + if (RC_BAD( rc = finishPacket( uiPacketType, uiPacketBodyLen, FALSE))) + { + goto Throw_Away_Transaction; + } + + finalizeTransaction(); + + if (pbLoggedTransEnd) + { + *pbLoggedTransEnd = TRUE; + } + } + +Exit: + + if (!m_bLoggingOff) + { + m_uiCurrTransID = 0; + } + + return (RC_BAD( rc) ? rc : rc2); +} + +/******************************************************************** +Desc: Log add, modify, delete, and reserve DRN packets +*********************************************************************/ +RCODE F_Rfl::logUpdatePacket( + FLMUINT uiPacketType, + FLMUINT uiContainer, + FLMUINT uiDrn, + FLMUINT uiAutoTrans) +{ + RCODE rc = FERR_OK; + FLMBYTE * pucPacketBody; + FLMUINT uiPacketBodyLen; + + // Do nothing if logging is disabled. + + if (m_bLoggingOff) + { + goto Exit; + } + + // Better not be in the middle of logging unknown stuff for the + // application + + flmAssert( !m_bLoggingUnknown); + + // Better be in the middle of a transaction. + + flmAssert( m_uiCurrTransID); + + m_uiOperCount++; + + if (uiPacketType == RFL_ADD_RECORD_PACKET_VER_2 || + uiPacketType == RFL_MODIFY_RECORD_PACKET_VER_2 || + uiPacketType == RFL_DELETE_RECORD_PACKET_VER_2) + { + uiPacketBodyLen = 11; + } + else + { + uiPacketBodyLen = 10; + } + + // Make sure we have space in the RFL buffer for a complete packet. + + if (!haveBuffSpace( uiPacketBodyLen + RFL_PACKET_OVERHEAD)) + { + if (RC_BAD( rc = flush( m_pCurrentBuf))) + { + goto Exit; + } + } + + // Get a pointer to where we will be laying down the packet body. + + pucPacketBody = getPacketBodyPtr(); + + // Output the transaction ID. + + UD2FBA( (FLMUINT32) m_uiCurrTransID, pucPacketBody); + pucPacketBody += 4; + + // Output the container number. + + UW2FBA( (FLMUINT16) uiContainer, pucPacketBody); + pucPacketBody += 2; + + // Output the DRN. + + UD2FBA( (FLMUINT32) uiDrn, pucPacketBody); + pucPacketBody += 4; + + // Output the flags + + if (uiPacketType == RFL_ADD_RECORD_PACKET_VER_2 || + uiPacketType == RFL_MODIFY_RECORD_PACKET_VER_2 || + uiPacketType == RFL_DELETE_RECORD_PACKET_VER_2) + { + FLMUINT uiFlags = 0; + + // For now, these are the only flags we log + + if (uiAutoTrans & FLM_DO_IN_BACKGROUND) + { + uiFlags |= RFL_UPDATE_BACKGROUND; + } + + if (uiAutoTrans & FLM_SUSPENDED) + { + uiFlags |= RFL_UPDATE_SUSPENDED; + } + + *pucPacketBody++ = (FLMBYTE) uiFlags; + } + + // Finish the packet + + if (RC_BAD( rc = finishPacket( uiPacketType, uiPacketBodyLen, FALSE))) + { + goto Exit; + } + +Exit: + + return (rc); +} + +/******************************************************************** +Desc: Log index suspend and resume packets +*********************************************************************/ +RCODE F_Rfl::logIndexSuspendOrResume( + FLMUINT uiIndexNum, + FLMUINT uiPacketType) +{ + RCODE rc = FERR_OK; + FLMBYTE * pucPacketBody; + FLMUINT uiPacketBodyLen; + + // This call is new with 4.51 databases - not supported in older + // versions, so don't log it. + + if (m_pFile->FileHdr.uiVersionNum < FLM_FILE_FORMAT_VER_4_51) + { + goto Exit; + } + + // Do nothing if logging is disabled. + + if (m_bLoggingOff) + { + goto Exit; + } + + // Better not be in the middle of logging unknown stuff for the + // application + + flmAssert( !m_bLoggingUnknown); + + // Better be in the middle of a transaction. + + flmAssert( m_uiCurrTransID); + + m_uiOperCount++; + uiPacketBodyLen = 6; + + // Make sure we have space in the RFL buffer for a complete packet. + + if (!haveBuffSpace( uiPacketBodyLen + RFL_PACKET_OVERHEAD)) + { + if (RC_BAD( rc = flush( m_pCurrentBuf))) + { + goto Exit; + } + } + + // Get a pointer to where we will be laying down the packet body. + + pucPacketBody = getPacketBodyPtr(); + + // Output the transaction ID. + + UD2FBA( (FLMUINT32) m_uiCurrTransID, pucPacketBody); + pucPacketBody += 4; + + // Output the index number. + + UW2FBA( (FLMUINT16) uiIndexNum, pucPacketBody); + pucPacketBody += 2; + + // Finish the packet + + if (RC_BAD( rc = finishPacket( uiPacketType, uiPacketBodyLen, FALSE))) + { + goto Exit; + } + +Exit: + + return (rc); +} + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE F_Rfl::logSizeEventConfig( + FLMUINT uiTransID, + FLMUINT uiSizeThreshold, + FLMUINT uiSecondsBetweenEvents, + FLMUINT uiBytesBetweenEvents) +{ + RCODE rc = FERR_OK; + FLMBYTE * pucPacketBody; + FLMUINT uiPacketBodyLen; + + // Don't log the operation if it isn't supported by the current database + // version + + if (m_pFile->FileHdr.uiVersionNum < FLM_FILE_FORMAT_VER_4_61) + { + goto Exit; + } + + // Do nothing if logging is disabled. + + if (m_bLoggingOff) + { + goto Exit; + } + + // Better not be in the middle of logging unknown stuff for the + // application + + flmAssert( !m_bLoggingUnknown); + + // We need to set up to log this packet as if we were logging a + // transaction. The only difference is that we don't log the begin + // transaction packet. + + if (RC_BAD( rc = setupTransaction())) + { + goto Exit; + } + + uiPacketBodyLen = 16; + + // Make sure we have space in the RFL buffer for a complete packet. + + if( !haveBuffSpace( uiPacketBodyLen + RFL_PACKET_OVERHEAD)) + { + if (RC_BAD( rc = flush( m_pCurrentBuf))) + { + goto Exit; + } + } + + // Get a pointer to where we will be laying down the packet body. + + pucPacketBody = getPacketBodyPtr(); + + // Output the transaction ID. + + UD2FBA( (FLMUINT32) uiTransID, pucPacketBody); + pucPacketBody += 4; + + // Output the size threshold + + UD2FBA( (FLMUINT32) uiSizeThreshold, pucPacketBody); + pucPacketBody += 4; + + // Output the time frequency + + UD2FBA( (FLMUINT32) uiSecondsBetweenEvents, pucPacketBody); + pucPacketBody += 4; + + // Output the size frequency + + UD2FBA( (FLMUINT32) uiBytesBetweenEvents, pucPacketBody); + pucPacketBody += 4; + + // Finish the packet + + if (RC_BAD( rc = finishPacket( RFL_CONFIG_SIZE_EVENT_PACKET, + uiPacketBodyLen, TRUE))) + { + goto Exit; + } + +Exit: + + return (rc); +} + +/******************************************************************** +Desc: Make room in the RFL buffer for the additional bytes. + This is done by flushing the log buffer and shifting down + the bytes already used in the current packet. If that doesn't + make room, the current packet will be finished and a new one + started. +*********************************************************************/ +RCODE F_Rfl::makeRoom( + FLMUINT uiAdditionalBytesNeeded, + FLMUINT * puiCurrPacketLenRV, + FLMUINT uiPacketType, + FLMUINT * puiBytesAvailableRV, + FLMUINT * puiPacketCountRV) +{ + RCODE rc = FERR_OK; + FLMUINT uiBytesNeeded; + + // Must account for encryption, so round bytes needed to nearest four + // byte boundary. + + uiBytesNeeded = *puiCurrPacketLenRV + uiAdditionalBytesNeeded; + if (uiBytesNeeded & 0x3) + { + uiBytesNeeded += (4 - (uiBytesNeeded & 0x3)); + } + + if (uiBytesNeeded <= (FLMUINT) RFL_MAX_PACKET_SIZE) + { + FLMUINT uiTmp = uiBytesNeeded; + + if (haveBuffSpace( uiTmp)) + { + if (puiBytesAvailableRV) + { + *puiBytesAvailableRV = uiAdditionalBytesNeeded; + } + } + else + { + + // Bytes requested will fit into a packet, but not the buffer, + // so we need to shift the packets in the buffer down. The + // shiftPacketsDown guarantees that there is room in the buffer + // for a full size packet. + + if (RC_BAD( rc = shiftPacketsDown( *puiCurrPacketLenRV, FALSE))) + { + goto Exit; + } + + // If a non-NULL puwBytesAvailableRV is passed in it means that + // we are to return the number of bytes that we can actually + // output. Since we know there is enough for the bytes needed, we + // simply return the number of bytes that were requested. + + if (puiBytesAvailableRV) + { + *puiBytesAvailableRV = uiAdditionalBytesNeeded; + } + } + } + else // (uiBytesNeeded > RFL_MAX_PACKET_SIZE) + { + + // This is the case where the bytes needed would overflow the + // maximum packet size. If puwBytesAvailableRV is NULL, it means + // that all of the requested additional bytes must fit into the + // packet. In that case, since the requested bytes would put us over + // the packet size limit, we must finish the current packet and then + // flush the packets out of the buffer so we can start a new packet. + + if (!puiBytesAvailableRV) + { + + // Finish the current packet and start a new one. + + if (puiPacketCountRV) + { + (*puiPacketCountRV)++; + } + + if (RC_BAD( rc = finishPacket( uiPacketType, + *puiCurrPacketLenRV - RFL_PACKET_OVERHEAD, FALSE))) + { + goto Exit; + } + + if (RC_BAD( rc = flush( m_pCurrentBuf))) + { + goto Exit; + } + + *puiCurrPacketLenRV = RFL_PACKET_OVERHEAD; + } + else + { + + // When puiBytesAvailableRV is non-NULL, it means we can fill up + // the rest of the packet with part of the bytes. In this case we + // return the number of bytes available and then shift the + // packets down in the buffer to make sure there is room for a + // full-size packet. + + *puiBytesAvailableRV = RFL_MAX_PACKET_SIZE -*puiCurrPacketLenRV; + if (RC_BAD( rc = shiftPacketsDown( *puiCurrPacketLenRV, FALSE))) + { + goto Exit; + } + } + } + +Exit: + + return (rc); +} + +/******************************************************************** +Desc: Log a chunk of data to the RFL log - typically used to log + field data. Will spill over into multiple packets if + necessary. +*********************************************************************/ +RCODE F_Rfl::logData( + FLMUINT uiDataLen, + const FLMBYTE * pucData, + FLMUINT uiPacketType, + FLMUINT * puiPacketLenRV, + FLMUINT * puiPacketCountRV, + FLMUINT * puiMaxLogBytesNeededRV, + FLMUINT * puiTotalBytesLoggedRV) +{ + RCODE rc = FERR_OK; + FLMUINT uiBytesAvail; + FLMBYTE * pucDest; + + while (uiDataLen) + { + if (RC_BAD( rc = makeRoom( uiDataLen, puiPacketLenRV, uiPacketType, + &uiBytesAvail, puiPacketCountRV))) + { + goto Exit; + } + + if (uiBytesAvail) + { + if (puiMaxLogBytesNeededRV) + { + if (RC_BAD( rc = RflCheckMaxLogged( puiMaxLogBytesNeededRV, + *puiPacketCountRV, puiTotalBytesLoggedRV, uiBytesAvail))) + { + goto Exit; + } + } + + pucDest = getPacketPtr() + (*puiPacketLenRV); + f_memcpy( pucDest, pucData, uiBytesAvail); + uiDataLen -= uiBytesAvail; + pucData += uiBytesAvail; + (*puiPacketLenRV) += uiBytesAvail; + } + + // If we didn't get all of the data into the RFL buffer, finish and + // flush the current packet. + + if (uiDataLen) + { + if (puiPacketCountRV) + { + (*puiPacketCountRV)++; + } + + if (RC_BAD( rc = finishPacket( uiPacketType, + *puiPacketLenRV - RFL_PACKET_OVERHEAD, FALSE))) + { + goto Exit; + } + + if (RC_BAD( rc = flush( m_pCurrentBuf))) + { + goto Exit; + } + + *puiPacketLenRV = RFL_PACKET_OVERHEAD; + if (puiMaxLogBytesNeededRV) + { + if (RC_BAD( rc = RflCheckMaxLogged( puiMaxLogBytesNeededRV, + *puiPacketCountRV, puiTotalBytesLoggedRV, + RFL_PACKET_OVERHEAD))) + { + goto Exit; + } + } + } + } + +Exit: + + return (rc); +} + +/******************************************************************** +Desc: Check to see if by logging the requested number of bytes we + will end up exceeding the maximum bytes needed. If so, and + we have not yet actually logged a packet, return + FERR_FAILURE so that we will discard this packet that is + being built. If we have already logged a packet, it is + too late to discard what has been done. +*********************************************************************/ +FSTATIC RCODE RflCheckMaxLogged( + FLMUINT * puiMaxBytesNeededRV, + FLMUINT uiPacketsLogged, + FLMUINT * puiCurrTotalLoggedRV, + FLMUINT uiBytesToLog) +{ + RCODE rc = FERR_OK; + + *puiCurrTotalLoggedRV += uiBytesToLog; + + if ((!uiPacketsLogged) && (*puiCurrTotalLoggedRV > *puiMaxBytesNeededRV)) + { + rc = RC_SET( FERR_FAILURE); + goto Exit; + } + +Exit: + + return (rc); +} + +/******************************************************************** +Desc: Callback function that captures the changes being logged by + the call to flmRecordDifference. +*********************************************************************/ +FSTATIC void RflChangeCallback( + GRD_DifferenceData& DiffData, + void * CallbackData) +{ + RFL_CHANGE_DATA * pRflChangeData = (RFL_CHANGE_DATA*) CallbackData; + F_Rfl * pRfl = pRflChangeData->pRfl; + void * pvField; + const FLMBYTE * pucExportPtr; + FLMBYTE * pucTmp; + FLMUINT uiOverhead = 0; + FLMUINT uiBytesToLog; + FLMUINT uiPos; + FLMUINT uiTagNum; + FLMUINT uiDataLen = 0; + FLMBOOL bEncrypted = FALSE; + FLMUINT uiEncId; + FLMBYTE ucChangeType = 0; + + // If we had an error before this callback, do nothing. + + if (RC_BAD( pRflChangeData->rc)) + { + goto Exit; + } + + switch (DiffData.type) + { + case GRD_Inserted: + { + bEncrypted = DiffData.pAfterRecord->isEncryptedField( DiffData.pvAfterField); + uiDataLen = DiffData.pAfterRecord->getDataLength( DiffData.pvAfterField); + if (pRflChangeData->uiVersionNum < FLM_FILE_FORMAT_VER_4_61) + { + if (bEncrypted) + { + uiOverhead = 13; + ucChangeType = RFL_INSERT_ENC_FIELD; + } + else + { + uiOverhead = 9; + ucChangeType = RFL_INSERT_FIELD; + } + } + else + { + if (bEncrypted) + { + uiOverhead = 17; + ucChangeType = RFL_INSERT_ENC_LARGE_FIELD; + } + else + { + uiOverhead = 11; + ucChangeType = RFL_INSERT_LARGE_FIELD; + } + } + break; + } + + case GRD_Deleted: + { + + // Ignore these for versions of the database >= 4.60 + + if (pRflChangeData->uiVersionNum >= FLM_FILE_FORMAT_VER_4_60) + { + goto Exit; + } + + uiOverhead = 3; + break; + } + + case GRD_DeletedSubtree: + { + + // Ignore these for versions of the database < 4.60 + + if (pRflChangeData->uiVersionNum < FLM_FILE_FORMAT_VER_4_60) + { + goto Exit; + } + + uiOverhead = 3; + break; + } + + case GRD_Modified: + { + bEncrypted = DiffData.pAfterRecord->isEncryptedField( DiffData.pvAfterField); + uiDataLen = DiffData.pAfterRecord->getDataLength( DiffData.pvAfterField); + if (pRflChangeData->uiVersionNum < FLM_FILE_FORMAT_VER_4_61) + { + if (bEncrypted) + { + uiOverhead = 10; + ucChangeType = RFL_MODIFY_ENC_FIELD; + } + else + { + uiOverhead = 6; + ucChangeType = RFL_MODIFY_FIELD; + } + } + else + { + if (bEncrypted) + { + uiOverhead = 14; + ucChangeType = RFL_MODIFY_ENC_LARGE_FIELD; + } + else + { + uiOverhead = 8; + ucChangeType = RFL_MODIFY_LARGE_FIELD; + } + } + break; + } + + default: + { + flmAssert( 0); + break; + } + } + + // Determine the number of bytes that will actually be logged with + // this overhead. If it won't fit in the current packet, we will have + // to create a new packet - hence, we add RFL_PACKET_OVERHEAD to the + // amount that will be logged. + + uiBytesToLog = uiOverhead; + if (RFL_MAX_PACKET_SIZE - uiOverhead < pRflChangeData->uiCurrPacketLen) + { + uiBytesToLog += RFL_PACKET_OVERHEAD; + } + + // See if the bytes we are going log will exceed the maximum bytes + // needed. + + if (RC_BAD( pRflChangeData->rc = RflCheckMaxLogged( + &pRflChangeData->uiMaxLogBytesNeeded, + pRflChangeData->uiPacketCount, + &pRflChangeData->uiTotalBytesLogged, uiBytesToLog))) + { + goto Exit; + } + + // Make room to log the overhead + + if (RC_BAD( pRflChangeData->rc = pRfl->makeRoom( uiOverhead, + &pRflChangeData->uiCurrPacketLen, RFL_CHANGE_FIELDS_PACKET, NULL, + &pRflChangeData->uiPacketCount))) + { + goto Exit; + } + + pucTmp = pRfl->getPacketPtr() + pRflChangeData->uiCurrPacketLen; + uiPos = DiffData.uiAbsolutePosition; + UW2FBA( (FLMUINT16) uiPos, &pucTmp[1]); + pRflChangeData->uiCurrPacketLen += uiOverhead; + pvField = DiffData.pvAfterField; + + switch (DiffData.type) + { + case GRD_Inserted: + { + *pucTmp = ucChangeType; + pucTmp += 3; + uiTagNum = DiffData.pAfterRecord->getFieldID( pvField); + UW2FBA( (FLMUINT16) uiTagNum, pucTmp); + pucTmp += 2; + *pucTmp++ = (FLMBYTE) DiffData.pAfterRecord->getDataType( pvField); + *pucTmp++ = (FLMBYTE) DiffData.pAfterRecord->getLevel( pvField); + if (pRflChangeData->uiVersionNum < FLM_FILE_FORMAT_VER_4_61) + { + UW2FBA( (FLMUINT16)uiDataLen, pucTmp); + pucTmp += 2; + } + else + { + UD2FBA( (FLMUINT32)uiDataLen, pucTmp); + pucTmp += 4; + } + + if (bEncrypted) + { + uiEncId = DiffData.pAfterRecord->getEncryptionID( pvField); + flmAssert( uiEncId); + UW2FBA( (FLMUINT16) uiEncId, pucTmp); + pucTmp += 2; + + uiDataLen = DiffData.pAfterRecord->getEncryptedDataLength( pvField); + if (pRflChangeData->uiVersionNum < FLM_FILE_FORMAT_VER_4_61) + { + UW2FBA( (FLMUINT16)uiDataLen, pucTmp); + pucTmp += 2; + } + else + { + UD2FBA( (FLMUINT32)uiDataLen, pucTmp); + pucTmp += 4; + } + } + + // Log the data, if any. + + if (uiDataLen) + { + if (bEncrypted) + { + pucExportPtr = DiffData.pAfterRecord->getEncryptionDataPtr( pvField); + } + else + { + pucExportPtr = DiffData.pAfterRecord->getDataPtr( pvField); + } + + if (!pucExportPtr) + { + pRflChangeData->rc = RC_SET( FERR_MEM); + goto Exit; + } + + if (RC_BAD( pRflChangeData->rc = pRfl->logData( uiDataLen, + pucExportPtr, RFL_CHANGE_FIELDS_PACKET, + &pRflChangeData->uiCurrPacketLen, + &pRflChangeData->uiPacketCount, + &pRflChangeData->uiMaxLogBytesNeeded, + &pRflChangeData->uiTotalBytesLogged))) + { + goto Exit; + } + } + + break; + } + + case GRD_Deleted: + case GRD_DeletedSubtree: + { + *pucTmp = RFL_DELETE_FIELD; + break; + } + + case GRD_Modified: + { + *pucTmp = ucChangeType; + pucTmp += 3; + + // For now, just log the new bytes using RFL_REPLACE_BYTES option + + *pucTmp++ = RFL_REPLACE_BYTES; + uiDataLen = DiffData.pAfterRecord->getDataLength( pvField); + if (pRflChangeData->uiVersionNum < FLM_FILE_FORMAT_VER_4_61) + { + UW2FBA( (FLMUINT16)uiDataLen, pucTmp); + pucTmp += 2; + } + else + { + UD2FBA( (FLMUINT32)uiDataLen, pucTmp); + pucTmp += 4; + } + + if (bEncrypted) + { + uiEncId = DiffData.pAfterRecord->getEncryptionID( pvField); + flmAssert( uiEncId); + UW2FBA( (FLMUINT16) uiEncId, pucTmp); + pucTmp += 2; + + uiDataLen = DiffData.pAfterRecord->getEncryptedDataLength( pvField); + if (pRflChangeData->uiVersionNum < FLM_FILE_FORMAT_VER_4_61) + { + UW2FBA( (FLMUINT16)uiDataLen, pucTmp); + pucTmp += 2; + } + else + { + UD2FBA( (FLMUINT32)uiDataLen, pucTmp); + pucTmp += 4; + } + } + + // Log the data, if any. + + if (uiDataLen) + { + if (bEncrypted) + { + pucExportPtr = DiffData.pAfterRecord->getEncryptionDataPtr( pvField); + } + else + { + pucExportPtr = DiffData.pAfterRecord->getDataPtr( pvField); + } + + if (pucExportPtr == NULL) + { + pRflChangeData->rc = RC_SET( FERR_MEM); + goto Exit; + } + + if (RC_BAD( pRflChangeData->rc = pRfl->logData( uiDataLen, + pucExportPtr, RFL_CHANGE_FIELDS_PACKET, + &pRflChangeData->uiCurrPacketLen, + &pRflChangeData->uiPacketCount, + &pRflChangeData->uiMaxLogBytesNeeded, + &pRflChangeData->uiTotalBytesLogged))) + { + goto Exit; + } + } + + break; + } + + default: + { + flmAssert( 0); + break; + } + } + +Exit: + + return; +} + +/******************************************************************** +Desc: Log change fields for a record modify operation. +*********************************************************************/ +RCODE F_Rfl::logChangeFields( + FlmRecord * pOldRecord, + FlmRecord * pNewRecord) +{ + RFL_CHANGE_DATA RflChangeData; + FLMUINT uiTmpBodyLen; + FLMUINT uiDataLen; + void * pvNewField; + FLMBOOL bEncrypted; + FLMUINT uiOverhead; + + RflChangeData.rc = FERR_OK; + RflChangeData.pRfl = this; + RflChangeData.uiVersionNum = m_pFile->FileHdr.uiVersionNum; + + // Determine the total amount that would have to be logged if we just + // logged the new record. + + RflChangeData.uiMaxLogBytesNeeded = RFL_PACKET_OVERHEAD; + uiTmpBodyLen = 0; + pvNewField = pNewRecord->root(); + for (; pvNewField; pvNewField = pNewRecord->next( pvNewField)) + { + bEncrypted = pNewRecord->isEncryptedField( pvNewField); + uiOverhead = (bEncrypted ? 10 : 6); + if (uiTmpBodyLen + uiOverhead <= RFL_MAX_PACKET_BODY_SIZE) + { + uiTmpBodyLen += uiOverhead; + } + else + { + uiTmpBodyLen = uiOverhead; + RflChangeData.uiMaxLogBytesNeeded += RFL_PACKET_OVERHEAD; + } + + RflChangeData.uiMaxLogBytesNeeded += uiOverhead; + if (bEncrypted) + { + uiDataLen = pNewRecord->getEncryptedDataLength( pvNewField); + } + else + { + uiDataLen = pNewRecord->getDataLength( pvNewField); + } + + while (uiDataLen) + { + FLMUINT uiTmp; + + uiTmp = RFL_MAX_PACKET_BODY_SIZE - uiTmpBodyLen; + if (uiTmp >= uiDataLen) + { + uiTmp = uiDataLen; + uiTmpBodyLen += uiDataLen; + } + else + { + uiTmpBodyLen = 0; + RflChangeData.uiMaxLogBytesNeeded += RFL_PACKET_OVERHEAD; + } + + RflChangeData.uiMaxLogBytesNeeded += uiTmp; + uiDataLen -= uiTmp; + } + } + + // Account for terminating 0 at the end. + + if (uiTmpBodyLen + 2 > RFL_MAX_PACKET_BODY_SIZE) + { + RflChangeData.uiMaxLogBytesNeeded += RFL_PACKET_OVERHEAD; + } + + RflChangeData.uiMaxLogBytesNeeded += 2; + + RflChangeData.uiPacketCount = 0; + RflChangeData.uiTotalBytesLogged = RFL_PACKET_OVERHEAD; + RflChangeData.uiCurrPacketLen = RFL_PACKET_OVERHEAD; + + if (!haveBuffSpace( RFL_PACKET_OVERHEAD)) + { + if (RC_BAD( RflChangeData.rc = flush( m_pCurrentBuf))) + { + goto Exit; + } + } + + flmRecordDifference( pOldRecord, pNewRecord, RflChangeCallback, + (void *) &RflChangeData); + + // See if we exceeded the maximum log bytes. If so, just log the + // changed record in its entirety. + + if (RC_BAD( RflChangeData.rc)) + { + if (RflChangeData.rc == FERR_FAILURE) + { + RflChangeData.rc = logRecord( pNewRecord); + } + + goto Exit; + } + else + { + FLMBYTE * pucTmp; + + // Make room to log the 3 bytes of terminator + + if (RC_BAD( RflChangeData.rc = makeRoom( 3, + &RflChangeData.uiCurrPacketLen, RFL_CHANGE_FIELDS_PACKET, NULL, + &RflChangeData.uiPacketCount))) + { + if (RflChangeData.rc == FERR_FAILURE) + { + RflChangeData.rc = logRecord( pNewRecord); + } + + goto Exit; + } + + pucTmp = getPacketPtr() + RflChangeData.uiCurrPacketLen; + *pucTmp++ = RFL_END_FIELD_CHANGES; + UW2FBA( (FLMUINT16) 0, pucTmp); + RflChangeData.uiCurrPacketLen += 3; + + if (RC_BAD( RflChangeData.rc = finishPacket( RFL_CHANGE_FIELDS_PACKET, + RflChangeData.uiCurrPacketLen - RFL_PACKET_OVERHEAD, FALSE))) + { + goto Exit; + } + } + +Exit: + + return (RflChangeData.rc); +} + +/******************************************************************** +Desc: Log a record for the record add or modify operations. +*********************************************************************/ +RCODE F_Rfl::logRecord( + FlmRecord * pRecord) +{ + RCODE rc = FERR_OK; + FLMUINT uiPacketLen = RFL_PACKET_OVERHEAD; + void * pvField; + FLMBYTE * pucTmp; + FLMUINT uiTagNum; + FLMUINT uiDataLen; + FLMBOOL bEncrypted; + FLMUINT uiEncId; + FLMUINT uiPacketType; + FLMUINT uiOverhead; + + if (!haveBuffSpace( RFL_PACKET_OVERHEAD)) + { + if (RC_BAD( rc = flush( m_pCurrentBuf))) + { + goto Exit; + } + } + + if (m_pFile->FileHdr.uiVersionNum < FLM_FILE_FORMAT_VER_4_60) + { + uiPacketType = RFL_DATA_RECORD_PACKET; + } + else if (m_pFile->FileHdr.uiVersionNum < FLM_FILE_FORMAT_VER_4_61) + { + uiPacketType = RFL_ENC_DATA_RECORD_PACKET; + } + else + { + uiPacketType = RFL_DATA_RECORD_PACKET_VER_3; + } + + pvField = pRecord->root(); + for (; pvField; pvField = pRecord->next( pvField)) + { + if (uiPacketType == RFL_DATA_RECORD_PACKET) + { + bEncrypted = FALSE; + uiOverhead = 6; + } + else if (uiPacketType == RFL_ENC_DATA_RECORD_PACKET) + { + bEncrypted = pRecord->isEncryptedField( pvField); + uiOverhead = (bEncrypted ? 11 : 7); + } + else + { + bEncrypted = pRecord->isEncryptedField( pvField); + uiOverhead = (bEncrypted ? 15 : 9); + } + + if (RC_BAD( rc = makeRoom( uiOverhead, &uiPacketLen, uiPacketType, NULL, + NULL))) + { + goto Exit; + } + + pucTmp = getPacketPtr() + uiPacketLen; + uiPacketLen += uiOverhead; + + uiTagNum = pRecord->getFieldID( pvField); + UW2FBA( (FLMUINT16) uiTagNum, pucTmp); + pucTmp += 2; + *pucTmp++ = (FLMBYTE) pRecord->getDataType( pvField); + *pucTmp++ = (FLMBYTE) pRecord->getLevel( pvField); + uiDataLen = pRecord->getDataLength( pvField); + if (uiPacketType != RFL_DATA_RECORD_PACKET_VER_3) + { + UW2FBA( (FLMUINT16) uiDataLen, pucTmp); + pucTmp += 2; + } + else + { + UD2FBA( (FLMUINT32) uiDataLen, pucTmp); + pucTmp += 4; + } + + // Record if this field is encrypted. If it is, then there will be + // more data to follow. + + if (uiPacketType != RFL_DATA_RECORD_PACKET) + { + *pucTmp = (bEncrypted ? (FLMBYTE) 1 : (FLMBYTE) 0); + pucTmp++; + + // Check for encrypted field and add the results. + + if (bEncrypted) + { + uiEncId = pRecord->getEncryptionID( pvField); + flmAssert( uiEncId); + UW2FBA( (FLMUINT16) uiEncId, pucTmp); + pucTmp += 2; + + uiDataLen = pRecord->getEncryptedDataLength( pvField); + if (uiPacketType == RFL_DATA_RECORD_PACKET_VER_3) + { + UD2FBA( (FLMUINT32)uiDataLen, pucTmp); + pucTmp += 4; + } + else + { + UW2FBA( (FLMUINT16)uiDataLen, pucTmp); + pucTmp += 2; + } + } + } + + // Log the data, if any. + + if (uiDataLen) + { + const FLMBYTE * pucExportPtr; + + if (bEncrypted) + { + pucExportPtr = pRecord->getEncryptionDataPtr( pvField); + } + else + { + pucExportPtr = pRecord->getDataPtr( pvField); + } + + if (pucExportPtr == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if (RC_BAD( rc = logData( uiDataLen, pucExportPtr, uiPacketType, + &uiPacketLen, NULL, NULL, NULL))) + { + goto Exit; + } + } + } + + // Add null to terminate the record. + + if (RC_BAD( rc = makeRoom( 2, &uiPacketLen, uiPacketType, NULL, NULL))) + { + goto Exit; + } + + pucTmp = getPacketPtr() + uiPacketLen; + uiPacketLen += 2; + UW2FBA( 0, pucTmp); + pucTmp += 2; + + // Finish the packet. + + if (RC_BAD( rc = finishPacket( uiPacketType, + uiPacketLen - RFL_PACKET_OVERHEAD, FALSE))) + { + goto Exit; + } + +Exit: + + return (rc); +} + +/******************************************************************** +Desc: Log record add, modify, or delete operation +*********************************************************************/ +RCODE F_Rfl::logUpdate( + FLMUINT uiContainer, + FLMUINT uiDrn, + FLMUINT uiAutoTrans, + FlmRecord * pOldRecord, + FlmRecord * pNewRecord) +{ + RCODE rc = FERR_OK; + FLMUINT uiPacketType; + + // Do nothing if logging is disabled. + + if (m_bLoggingOff) + { + goto Exit; + } + + // Better not be in the middle of logging unknown stuff for the + // application + + flmAssert( !m_bLoggingUnknown); + + // Better be in the middle of a transaction. + + flmAssert( m_uiCurrTransID); + + if (pOldRecord && pNewRecord) + { + if (m_pFile->FileHdr.uiVersionNum >= FLM_FILE_FORMAT_VER_4_60) + { + uiPacketType = RFL_MODIFY_RECORD_PACKET_VER_2; + } + else + { + uiPacketType = RFL_MODIFY_RECORD_PACKET; + } + } + else if (pNewRecord) + { + if (m_pFile->FileHdr.uiVersionNum >= FLM_FILE_FORMAT_VER_4_60) + { + uiPacketType = RFL_ADD_RECORD_PACKET_VER_2; + } + else + { + uiPacketType = RFL_ADD_RECORD_PACKET; + } + } + else + { + if (m_pFile->FileHdr.uiVersionNum >= FLM_FILE_FORMAT_VER_4_60) + { + uiPacketType = RFL_DELETE_RECORD_PACKET_VER_2; + } + else + { + uiPacketType = RFL_DELETE_RECORD_PACKET; + } + } + + if (RC_BAD( rc = logUpdatePacket( uiPacketType, uiContainer, uiDrn, + uiAutoTrans))) + { + goto Exit; + } + + // If it is a record modify, log the change fields. If it is a record + // add, log the new record. + + if (pOldRecord && pNewRecord) + { + if (RC_BAD( rc = logChangeFields( pOldRecord, pNewRecord))) + { + goto Exit; + } + } + else if (pNewRecord) + { + if (RC_BAD( rc = logRecord( pNewRecord))) + { + goto Exit; + } + } + +Exit: + + return (rc); +} + +/******************************************************************** +Desc: Log a set of records that is indexed for a specific index. +*********************************************************************/ +RCODE F_Rfl::logIndexSet( + FLMUINT uiIndex, + FLMUINT uiContainerNum, + FLMUINT uiStartDrn, + FLMUINT uiEndDrn) +{ + RCODE rc = FERR_OK; + FLMBYTE * pucPacketBody; + FLMUINT uiPacketBodyLen; + + // This call is a new database version. Database better have been + // upgraded. + + flmAssert( m_pFile->FileHdr.uiVersionNum >= FLM_FILE_FORMAT_VER_3_02); + + // Do nothing if logging is disabled. + + if (m_bLoggingOff) + { + goto Exit; + } + + // Better not be in the middle of logging unknown stuff for the + // application + + flmAssert( !m_bLoggingUnknown); + + // Better be in the middle of a transaction. + + flmAssert( m_uiCurrTransID); + + m_uiOperCount++; + uiPacketBodyLen = + (FLMUINT)((m_pFile->FileHdr.uiVersionNum >= FLM_FILE_FORMAT_VER_4_50) + ? (FLMUINT) 16 + : (FLMUINT) 14); + + // Make sure we have space in the RFL buffer for a complete packet. + + if (!haveBuffSpace( uiPacketBodyLen + RFL_PACKET_OVERHEAD)) + { + if (RC_BAD( rc = flush( m_pCurrentBuf))) + { + goto Exit; + } + } + + // Get a pointer to where we will be laying down the packet body. + + pucPacketBody = getPacketBodyPtr(); + + // Output the transaction ID. + + UD2FBA( (FLMUINT32) m_uiCurrTransID, pucPacketBody); + pucPacketBody += 4; + + // Output the container number, if db version is >= 4.50 + + if (m_pFile->FileHdr.uiVersionNum >= FLM_FILE_FORMAT_VER_4_50) + { + UW2FBA( (FLMUINT16) uiContainerNum, pucPacketBody); + pucPacketBody += 2; + } + + // Output the index number. + + UW2FBA( (FLMUINT16) uiIndex, pucPacketBody); + pucPacketBody += 2; + + // Output the starting DRN. + + UD2FBA( (FLMUINT32) (uiStartDrn), pucPacketBody); + pucPacketBody += 4; + + // Output the ending DRN. + + UD2FBA( (FLMUINT32) (uiEndDrn), pucPacketBody); + pucPacketBody += 4; + + // Finish the packet + + if (RC_BAD( rc = finishPacket( (FLMUINT) ( + (m_pFile->FileHdr.uiVersionNum >= FLM_FILE_FORMAT_VER_4_50) + ? (FLMUINT) RFL_INDEX_SET_PACKET_VER_2 + : (FLMUINT) RFL_INDEX_SET_PACKET), + uiPacketBodyLen, FALSE))) + { + goto Exit; + } + +Exit: + + return (rc); +} + +/******************************************************************** +Desc: Start logging unknown packets. +*********************************************************************/ +RCODE F_Rfl::startLoggingUnknown(void) +{ + RCODE rc = FERR_OK; + FLMBYTE * pucPacketBody; + FLMUINT uiPacketBodyLen; + + flmAssert( m_pFile); + + // Do nothing if logging is disabled. Also, ignore these packets if we + // are operating on a pre-4.3 database. + + if (m_bLoggingOff || m_pFile->FileHdr.uiVersionNum < FLM_FILE_FORMAT_VER_4_3) + { + goto Exit; + } + + // Better not already be in the middle of logging unknown stuff for + // the application + + flmAssert( !m_bLoggingUnknown); + + // Better be inside a transaction. + + flmAssert( m_uiCurrTransID); + + m_uiOperCount++; + uiPacketBodyLen = 4; + + // Make sure we have space in the RFL buffer for a complete packet. + + if (!haveBuffSpace( uiPacketBodyLen + RFL_PACKET_OVERHEAD)) + { + if (RC_BAD( rc = flush( m_pCurrentBuf))) + { + goto Exit; + } + } + + // Get a pointer to where we will be laying down the packet body. + + pucPacketBody = getPacketBodyPtr(); + + // Output the transaction ID. + + UD2FBA( (FLMUINT32) m_uiCurrTransID, pucPacketBody); + pucPacketBody += 4; + + // Finish the packet + + if (RC_BAD( rc = finishPacket( RFL_START_UNKNOWN_PACKET, uiPacketBodyLen, + FALSE))) + { + goto Exit; + } + + m_bLoggingUnknown = TRUE; + m_uiUnknownPacketLen = RFL_PACKET_OVERHEAD; + +Exit: + + return (rc); +} + +/******************************************************************** +Desc: Log unknown data. +*********************************************************************/ +RCODE F_Rfl::logUnknown( + FLMBYTE * pucUnknown, + FLMUINT uiLen) +{ + RCODE rc = FERR_OK; + + // Do nothing if logging is disabled. Also, ignore these packets if we + // are operating on a pre-4.3 database. + + if (m_bLoggingOff || m_pFile->FileHdr.uiVersionNum < FLM_FILE_FORMAT_VER_4_3) + { + goto Exit; + } + + flmAssert( m_bLoggingUnknown); + if (RC_BAD( rc = logData( uiLen, pucUnknown, RFL_UNKNOWN_PACKET, + &m_uiUnknownPacketLen, NULL, NULL, NULL))) + { + goto Exit; + } + +Exit: + + return (rc); +} + +/******************************************************************** +Desc: End logging unknown packets. +*********************************************************************/ +RCODE F_Rfl::endLoggingUnknown(void) +{ + RCODE rc = FERR_OK; + + flmAssert( m_pFile); + + // Do nothing if logging is disabled. Also, ignore these packets if we + // are operating on a pre-4.3 database. + + if (m_bLoggingOff || m_pFile->FileHdr.uiVersionNum < FLM_FILE_FORMAT_VER_4_3) + { + goto Exit; + } + + // Better be in the middle of logging unknown stuff for the application + + flmAssert( m_bLoggingUnknown); + if (m_uiUnknownPacketLen > RFL_PACKET_OVERHEAD) + { + if (RC_BAD( rc = finishPacket( RFL_UNKNOWN_PACKET, + m_uiUnknownPacketLen - RFL_PACKET_OVERHEAD, FALSE))) + { + goto Exit; + } + } + +Exit: + + m_bLoggingUnknown = FALSE; + m_uiUnknownPacketLen = RFL_PACKET_OVERHEAD; + return (rc); +} + +/******************************************************************** +Desc: Log a reduce packet +*********************************************************************/ +RCODE F_Rfl::logReduce( + FLMUINT uiTransID, + FLMUINT uiCount) +{ + RCODE rc = FERR_OK; + FLMBYTE * pucPacketBody; + FLMUINT uiPacketBodyLen; + + // This call is new with 4.3 databases - not supported in older + // versions, so don't log it. + + if (m_pFile->FileHdr.uiVersionNum < FLM_FILE_FORMAT_VER_4_3) + { + goto Exit; + } + + // Do nothing if logging is disabled. + + if (m_bLoggingOff) + { + goto Exit; + } + + // Better not be in the middle of logging unknown stuff for the + // application + + flmAssert( !m_bLoggingUnknown); + + // We need to set up to log this packet as if we were logging a + // transaction. The only difference is that we don't log the begin + // transaction packet. + + if (RC_BAD( rc = setupTransaction())) + { + goto Exit; + } + + uiPacketBodyLen = 8; + + // Make sure we have space in the RFL buffer for a complete packet. + + if (!haveBuffSpace( uiPacketBodyLen + RFL_PACKET_OVERHEAD)) + { + if (RC_BAD( rc = flush( m_pCurrentBuf))) + { + goto Exit; + } + } + + // Get a pointer to where we will be laying down the packet body. + + pucPacketBody = getPacketBodyPtr(); + + // Output the transaction ID. + + UD2FBA( (FLMUINT32) uiTransID, pucPacketBody); + pucPacketBody += 4; + + // Output the count + + UD2FBA( (FLMUINT32) uiCount, pucPacketBody); + pucPacketBody += 4; + + // Finish the packet + + if (RC_BAD( rc = finishPacket( RFL_REDUCE_PACKET, uiPacketBodyLen, TRUE))) + { + goto Exit; + } + + // Finalize the transaction (as if we were committing a transaction) + + finalizeTransaction(); + +Exit: + + return (rc); +} + +/******************************************************************** +Desc: Log a database conversion packet +Note: This routine performs most of the setup for logging a full + transaction, but it does not cause begin and commit packets + to be logged. It is a "standalone" transaction. +*********************************************************************/ +RCODE F_Rfl::logUpgrade( + FLMUINT uiTransID, + FLMUINT uiOldVersion, + FLMBYTE * pucDBKey, + FLMUINT32 ui32DBKeyLen) +{ + RCODE rc = FERR_OK; + FLMBYTE * pucPacketBody; + FLMUINT uiPacketBodyLen; + + // Do nothing if logging is disabled. + + if (m_bLoggingOff) + { + goto Exit; + } + + // Better not be in the middle of logging unknown stuff for the + // application + + flmAssert( !m_bLoggingUnknown); + + // We need to set up to log this packet as if we were logging a + // transaction. The only difference is that we don't log the begin + // transaction packet. + + if (RC_BAD( rc = setupTransaction())) + { + goto Exit; + } + + uiPacketBodyLen = 14 + ui32DBKeyLen; + + // Make sure we have space in the RFL buffer for a complete packet. + + if (!haveBuffSpace( uiPacketBodyLen + RFL_PACKET_OVERHEAD)) + { + if (RC_BAD( rc = flush( m_pCurrentBuf))) + { + goto Exit; + } + } + + // Get a pointer to where we will be laying down the packet body. + + pucPacketBody = getPacketBodyPtr(); + + // Output the transaction ID + + UD2FBA( (FLMUINT32) uiTransID, pucPacketBody); + pucPacketBody += 4; + + // Output the old database version + + UD2FBA( (FLMUINT32) uiOldVersion, pucPacketBody); + pucPacketBody += 4; + + // Output the new database version + + UD2FBA( (FLMUINT32) FLM_CUR_FILE_FORMAT_VER_NUM, pucPacketBody); + pucPacketBody += 4; + + // For versions >= 4.60, the next two bytes will give the length of + // the DB Key. + + flmAssert( ui32DBKeyLen <= 0xFFFF); + UW2FBA( (FLMUINT16) ui32DBKeyLen, pucPacketBody); + pucPacketBody += 2; + + // If we were built without encryption, the key length will be zero, + // so no need to store the key. + + if (ui32DBKeyLen) + { + f_memcpy( pucPacketBody, pucDBKey, ui32DBKeyLen); + pucPacketBody += ui32DBKeyLen; + } + + // Finish the packet + + if (RC_BAD( rc = finishPacket( RFL_UPGRADE_PACKET, uiPacketBodyLen, TRUE))) + { + goto Exit; + } + + // Finalize the transaction (as if we were committing a transaction) + + finalizeTransaction(); + +Exit: + + if (!m_bLoggingOff) + { + m_uiCurrTransID = 0; + } + + return (rc); +} + +/******************************************************************** +Desc: Log the wrapped database key +*********************************************************************/ +RCODE F_Rfl::logWrappedKey( + FLMUINT uiTransID, + FLMBYTE * pucDBKey, + FLMUINT32 ui32DBKeyLen) +{ + RCODE rc = FERR_OK; + FLMBYTE * pucPacketBody; + FLMUINT uiPacketBodyLen; + + // Do nothing if logging is disabled. + + if (m_bLoggingOff) + { + goto Exit; + } + + // Better not be in the middle of logging unknown stuff for the + // application + + flmAssert( !m_bLoggingUnknown); + + if (RC_BAD( rc = setupTransaction())) + { + goto Exit; + } + + uiPacketBodyLen = 6 + ui32DBKeyLen; + + // Make sure we have space in the RFL buffer for a complete packet. + + if (!haveBuffSpace( uiPacketBodyLen + RFL_PACKET_OVERHEAD)) + { + if (RC_BAD( rc = flush( m_pCurrentBuf))) + { + goto Exit; + } + } + + // Get a pointer to where we will be laying down the packet body. + + pucPacketBody = getPacketBodyPtr(); + + // Output the transaction ID + + UD2FBA( (FLMUINT32) uiTransID, pucPacketBody); + pucPacketBody += 4; + + // The next two bytes will give the length of the DB Key. + + flmAssert( ui32DBKeyLen <= 0xFFFF); + UW2FBA( (FLMUINT16) ui32DBKeyLen, pucPacketBody); + pucPacketBody += 2; + + // If we were built without encryption, the key length will be zero, + // so no need to store the key. + + if (ui32DBKeyLen) + { + f_memcpy( pucPacketBody, pucDBKey, ui32DBKeyLen); + pucPacketBody += ui32DBKeyLen; + } + + // Finish the packet + + if (RC_BAD( rc = finishPacket( RFL_WRAP_KEY_PACKET, uiPacketBodyLen, TRUE))) + { + goto Exit; + } + + finalizeTransaction(); + +Exit: + + return (rc); +} + +/******************************************************************** +Desc: Log that we have enabled encryption +*********************************************************************/ +RCODE F_Rfl::logEnableEncryption( + FLMUINT uiTransID, + FLMBYTE * pucDBKey, + FLMUINT32 ui32DBKeyLen) +{ + RCODE rc = FERR_OK; + FLMBYTE * pucPacketBody; + FLMUINT uiPacketBodyLen; + + // Do nothing if logging is disabled. + + if (m_bLoggingOff) + { + goto Exit; + } + + // Better not be in the middle of logging unknown stuff for the + // application + + flmAssert( !m_bLoggingUnknown); + + if (RC_BAD( rc = setupTransaction())) + { + goto Exit; + } + + uiPacketBodyLen = 6 + ui32DBKeyLen; + + // Make sure we have space in the RFL buffer for a complete packet. + + if (!haveBuffSpace( uiPacketBodyLen + RFL_PACKET_OVERHEAD)) + { + if (RC_BAD( rc = flush( m_pCurrentBuf))) + { + goto Exit; + } + } + + // Get a pointer to where we will be laying down the packet body. + + pucPacketBody = getPacketBodyPtr(); + + // Output the transaction ID + + UD2FBA( (FLMUINT32) uiTransID, pucPacketBody); + pucPacketBody += 4; + + // The next two bytes will give the length of the DB Key. + + flmAssert( ui32DBKeyLen <= 0xFFFF); + UW2FBA( (FLMUINT16) ui32DBKeyLen, pucPacketBody); + pucPacketBody += 2; + + // If we were built without encryption, the key length will be zero, + // so no need to store the key. + + if (ui32DBKeyLen) + { + f_memcpy( pucPacketBody, pucDBKey, ui32DBKeyLen); + pucPacketBody += ui32DBKeyLen; + } + + // Finish the packet + + if (RC_BAD( rc = finishPacket( RFL_ENABLE_ENCRYPTION_PACKET, uiPacketBodyLen, + TRUE))) + { + goto Exit; + } + + finalizeTransaction(); + +Exit: + + return (rc); +} + +/******************************************************************** +Desc: Log a block chain free operation +*********************************************************************/ +RCODE F_Rfl::logBlockChainFree( + FLMUINT uiTrackerDrn, + FLMUINT uiCount, + FLMUINT uiEndAddr) +{ + RCODE rc = FERR_OK; + FLMBYTE * pucPacketBody; + FLMUINT uiPacketBodyLen; + + // This call is new with 4.52 databases - not supported in older + // versions, so don't log it. + + if (m_pFile->FileHdr.uiVersionNum < FLM_FILE_FORMAT_VER_4_60) + { + flmAssert( 0); + goto Exit; + } + + // Do nothing if logging is disabled. + + if (m_bLoggingOff) + { + goto Exit; + } + + // Better not be in the middle of logging unknown stuff for the + // application + + flmAssert( !m_bLoggingUnknown); + + // Better be in the middle of a transaction. + + flmAssert( m_uiCurrTransID); + m_uiOperCount++; + + uiPacketBodyLen = 16; + + // Make sure we have space in the RFL buffer for a complete packet. + + if (!haveBuffSpace( uiPacketBodyLen + RFL_PACKET_OVERHEAD)) + { + if (RC_BAD( rc = flush( m_pCurrentBuf))) + { + goto Exit; + } + } + + // Get a pointer to where we will be laying down the packet body. + + pucPacketBody = getPacketBodyPtr(); + + // Output the transaction ID. + + UD2FBA( (FLMUINT32) m_uiCurrTransID, pucPacketBody); + pucPacketBody += 4; + + // Output the tracker record number + + UD2FBA( (FLMUINT32) uiTrackerDrn, pucPacketBody); + pucPacketBody += 4; + + // Output the count + + UD2FBA( (FLMUINT32) uiCount, pucPacketBody); + pucPacketBody += 4; + + // Output the ending block address + + UD2FBA( (FLMUINT32) uiEndAddr, pucPacketBody); + pucPacketBody += 4; + + // Finish the packet + + if (RC_BAD( rc = finishPacket( RFL_BLK_CHAIN_FREE_PACKET, uiPacketBodyLen, + TRUE))) + { + goto Exit; + } + +Exit: + + return (rc); +} + +/******************************************************************** +Desc: Reads a full packet, based on what file offset and read + offset are currently set to. +*********************************************************************/ +RCODE F_Rfl::readPacket( + FLMUINT uiMinBytesNeeded) +{ + RCODE rc = FERR_OK; + FLMUINT uiTmpOffset; + FLMUINT uiReadLen; + FLMUINT uiBytesRead; + + // If we have enough bytes in the buffer for the minimum bytes needed, + // we don't need to retrieve any more bytes. + + if (m_pCurrentBuf->uiRflBufBytes - m_uiRflReadOffset >= uiMinBytesNeeded) + { + goto Exit; + } + + // If we are doing restore, we have to do only sequential reads - + // cannot depend on doing reads on 512 byte boundaries. Otherwise, we + // read directly from disk on 512 byte boundaries. + + if (m_pRestore) + { + FLMUINT uiCurrFilePos = m_pCurrentBuf->uiRflFileOffset + + m_pCurrentBuf->uiRflBufBytes; + + if (m_uiRflReadOffset > 0) + { + + // Move the bytes left in the buffer down to the beginning of + // the buffer. + + f_memmove( m_pCurrentBuf->pIOBuffer->m_pucBuffer, + &(m_pCurrentBuf->pIOBuffer->m_pucBuffer[m_uiRflReadOffset]), + m_pCurrentBuf->uiRflBufBytes - m_uiRflReadOffset); + m_pCurrentBuf->uiRflBufBytes -= m_uiRflReadOffset; + m_pCurrentBuf->uiRflFileOffset += m_uiRflReadOffset; + m_uiRflReadOffset = 0; + } + + uiReadLen = m_uiBufferSize - m_pCurrentBuf->uiRflBufBytes; + + // Read enough to fill the rest of the buffer, which is guaranteed + // to hold at least one full packet. + + if (!m_uiFileEOF) + { + if (uiCurrFilePos > (FLMUINT) (-1) - uiReadLen) + { + uiReadLen = (FLMUINT) (-1) - uiCurrFilePos; + } + } + else + { + if (uiCurrFilePos + uiReadLen > m_uiFileEOF) + { + uiReadLen = m_uiFileEOF - uiCurrFilePos; + } + } + + // If reading will not give us the minimum bytes needed, we cannot + // satisfy this request from the current file. + + if (uiReadLen + m_pCurrentBuf->uiRflBufBytes < uiMinBytesNeeded) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + + // Read enough to get the entire packet. + + if (RC_BAD( rc = m_pRestore->read( uiReadLen, &( + m_pCurrentBuf->pIOBuffer->m_pucBuffer[ + m_pCurrentBuf->uiRflBufBytes]), &uiBytesRead))) + { + if (rc == FERR_IO_END_OF_FILE) + { + rc = FERR_OK; + } + else + { + goto Exit; + } + } + + // If we didn't read enough to satisfy the minimum bytes needed, we + // cannot satisfy this request from the current file. + + if (uiBytesRead + m_pCurrentBuf->uiRflBufBytes < uiMinBytesNeeded) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + + m_pCurrentBuf->uiRflBufBytes += uiBytesRead; + } + else + { + + // Set offsets so we are on a 512 byte boundary for our next read. + // No need to move data, since we will be re-reading it anyway. + + if (m_uiRflReadOffset > 0) + { + uiTmpOffset = m_uiRflReadOffset - (m_uiRflReadOffset & 511); + m_pCurrentBuf->uiRflFileOffset += uiTmpOffset; + m_uiRflReadOffset -= uiTmpOffset; + } + else if (m_pCurrentBuf->uiRflFileOffset & 511) + { + m_uiRflReadOffset = m_pCurrentBuf->uiRflFileOffset & 511; + m_pCurrentBuf->uiRflFileOffset -= m_uiRflReadOffset; + } + + m_pCurrentBuf->uiRflBufBytes = 0; + + // Read enough to fill the rest of the buffer, which is guaranteed + // to hold at least one full packet. + + uiReadLen = m_uiBufferSize; + + // m_uiFileEOF better not be zero at this point - we should always + // know precisely where the RFL file ends when we are doing recovery + // as opposed to doing a restore. + + flmAssert( m_uiFileEOF >= 512); + if (m_pCurrentBuf->uiRflFileOffset + uiReadLen > m_uiFileEOF) + { + uiReadLen = m_uiFileEOF - m_pCurrentBuf->uiRflFileOffset; + } + + // If reading will not give us the minimum number of bytes needed, + // we have a bad packet. + + if (uiReadLen < m_uiRflReadOffset || + uiReadLen - m_uiRflReadOffset < uiMinBytesNeeded) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + + // Read to get the entire packet. + + if (RC_BAD( rc = m_pFileHdl->SectorRead( m_pCurrentBuf->uiRflFileOffset, + uiReadLen, m_pCurrentBuf->pIOBuffer->m_pucBuffer, + &uiBytesRead))) + { + if (rc == FERR_IO_END_OF_FILE) + { + rc = FERR_OK; + } + else + { + m_bRflVolumeOk = FALSE; + goto Exit; + } + } + + if (uiBytesRead < uiReadLen) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + + m_pCurrentBuf->uiRflBufBytes = uiReadLen; + } + +Exit: + + return (rc); +} + +/******************************************************************** +Desc: Gets and verifies the next packet from the roll-forward + log file. Packet checksum will be verified. +*********************************************************************/ +RCODE F_Rfl::getPacket( + FLMBOOL bForceNextFile, + FLMUINT * puiPacketTypeRV, + FLMBYTE ** ppucPacketBodyRV, + FLMUINT * puiPacketBodyLenRV, + FLMBOOL * pbLoggedTimes) +{ + RCODE rc = FERR_OK; + FLMBYTE * pucPacket; + FLMBYTE * pucPacketBody; + FLMUINT uiPacketType; + FLMUINT uiEncryptPacketBodyLen; + FLMBYTE ucHdr[512]; + FLMUINT uiBytesRead; + + // See if we need to go to the next file. Note that we only check for + // this exactly on packet boundaries. We do not expect packets to be + // split across files. If we are not at the end of processing what is + // in the buffer, we should be able to read the rest of the packet from + // the current file. + +Get_Next_File: + + if (bForceNextFile || + (m_uiFileEOF && m_uiRflReadOffset == m_pCurrentBuf->uiRflBufBytes && + m_pCurrentBuf->uiRflFileOffset + m_pCurrentBuf->uiRflBufBytes == + m_uiFileEOF)) + { + if (m_bKeepRflFiles) + { + if (!m_pRestore) + { + + // Only doing recovery after a failure, see if we are at the + // last file already. + + if (m_pCurrentBuf->uiCurrFileNum == m_uiLastRecoverFileNum) + { + rc = RC_SET( FERR_END); + goto Exit; + } + else if( (m_pCurrentBuf->uiCurrFileNum + 1 ) == + m_uiLastRecoverFileNum && + !(FLMUINT)FB2UD( &m_pFile->ucLastCommittedLogHdr[ + LOG_RFL_LAST_TRANS_OFFSET])) + { + + // We are going to try to open the last file. Since the + // log header shows a current offset of 0, the file may + // have been created but nothing was logged to it. We don't + // want to try to open it here because it may not have been + // initialized fully at the time of the server crash. + + m_pCurrentBuf->uiCurrFileNum = m_uiLastRecoverFileNum; + rc = RC_SET( FERR_END); + goto Exit; + } + + // Open the next file in the sequence. + + if (RC_BAD( rc = openFile( m_pCurrentBuf->uiCurrFileNum + 1, + m_ucNextSerialNum))) + { + if (rc == FERR_IO_PATH_NOT_FOUND || rc == FERR_IO_INVALID_PATH) + { + rc = RC_SET( FERR_END); + } + + goto Exit; + } + + // If this is the last RFL file, the EOF is contained in the + // log header. Otherwise, it will be in the RFL file's header, + // and openFile will already have retrieved it. + + if (m_pCurrentBuf->uiCurrFileNum == m_uiLastRecoverFileNum) + { + m_uiFileEOF = (FLMUINT) FB2UD( &m_pFile->ucLastCommittedLogHdr[ + LOG_RFL_LAST_TRANS_OFFSET]); + + // Could be zero if RFL file wasn't created yet. + + if (!m_uiFileEOF) + { + m_uiFileEOF = 512; + } + } + + // By this point, the EOF better be greater than or equal to + // 512. + + flmAssert( m_uiFileEOF >= 512); + } + else + { + if (RC_BAD( rc = m_pRestore->close())) + { + goto Exit; + } + + // Ask the recovery object to open the file. + + if (RC_BAD( rc = m_pRestore->openRflFile( + m_pCurrentBuf->uiCurrFileNum + 1))) + { + if (rc == FERR_IO_PATH_NOT_FOUND) + { + rc = RC_SET( FERR_END); + } + + goto Exit; + } + + // Get the first 512 bytes from the file and verify the + // header. + + if (RC_BAD( rc = m_pRestore->read( 512, ucHdr, &uiBytesRead))) + { + goto Exit; + } + + if (uiBytesRead < 512) + { + rc = RC_SET( FERR_NOT_RFL); + goto Exit; + } + + if (RC_BAD( rc = verifyHeader( ucHdr, + m_pCurrentBuf->uiCurrFileNum + 1, m_ucNextSerialNum))) + { + goto Exit; + } + + // We may not know the actual EOF of files during restore + // operations. m_uiFileEOF could be zero here. + + m_uiFileEOF = (FLMUINT) FB2UD( &ucHdr[RFL_EOF_POS]); + + // File EOF may be zero or >= 512 at this point. + + flmAssert( !m_uiFileEOF || m_uiFileEOF >= 512); + + // Need to increment current file number. + + m_pCurrentBuf->uiCurrFileNum++; + } + + m_pCurrentBuf->uiRflFileOffset = 512; + m_uiRflReadOffset = 0; + m_pCurrentBuf->uiRflBufBytes = 0; + + // Get the next packet from the new file. + + if (RC_BAD( rc = readPacket( RFL_PACKET_OVERHEAD))) + { + if (m_uiFileEOF == 512 && m_bKeepRflFiles) + { + + // File was empty, try to go to the next file. + + bForceNextFile = TRUE; + goto Get_Next_File; + } + + goto Exit; + } + } + else + { + + // This is the case where we are not keeping the RFL files. So, + // there is no next file to go to. If we get to this point, we + // had better not be doing a restore. + + flmAssert( m_pRestore == NULL && !bForceNextFile); + rc = RC_SET( FERR_END); + goto Exit; + } + } + + // Make sure we at least have a packet header in the buffer. + + if (RC_BAD( rc = readPacket( RFL_PACKET_OVERHEAD))) + { + goto Exit; + } + + // Verify the packet address. + + m_uiPacketAddress = m_pCurrentBuf->uiRflFileOffset + m_uiRflReadOffset; + pucPacket = &(m_pCurrentBuf->pIOBuffer->m_pucBuffer[m_uiRflReadOffset]); + if ((FLMUINT) FB2UD( &pucPacket[RFL_PACKET_ADDRESS_OFFSET]) != m_uiPacketAddress) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + + // Get packet type, time flag, and packet body length + + *puiPacketTypeRV = uiPacketType = RFL_GET_PACKET_TYPE( + pucPacket[RFL_PACKET_TYPE_OFFSET]); + + if (pbLoggedTimes) + { + *pbLoggedTimes = (pucPacket[RFL_PACKET_TYPE_OFFSET] & RFL_TIME_LOGGED_FLAG) + ? TRUE + : FALSE; + } + + *puiPacketBodyLenRV = (FLMUINT) FB2UW( + &pucPacket[RFL_PACKET_BODY_LENGTH_OFFSET]); + + // Adjust the packet body length for encryption if necessary. NOTE: + // This adjusted length is NOT returned to the caller. The actual body + // length is what will be returned. + + uiEncryptPacketBodyLen = getEncryptPacketBodyLen( uiPacketType, + *puiPacketBodyLenRV); + + // Make sure we have the entire packet in the buffer. + + if (RC_BAD( rc = readPacket( uiEncryptPacketBodyLen + RFL_PACKET_OVERHEAD))) + { + goto Exit; + } + + pucPacket = &(m_pCurrentBuf->pIOBuffer->m_pucBuffer[m_uiRflReadOffset]); + + // At this point, we are guaranteed to have the entire packet in the + // buffer. + + *ppucPacketBodyRV = pucPacketBody = &pucPacket[RFL_PACKET_OVERHEAD]; + + // Validate the packet checksum + + if (RflCalcChecksum( pucPacket, uiEncryptPacketBodyLen) != + pucPacket[RFL_PACKET_CHECKSUM_OFFSET]) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + + if (uiPacketType == RFL_TRNS_BEGIN_PACKET || + uiPacketType == RFL_TRNS_BEGIN_EX_PACKET || + uiPacketType == RFL_UPGRADE_PACKET || + uiPacketType == RFL_REDUCE_PACKET || + uiPacketType == RFL_WRAP_KEY_PACKET || + uiPacketType == RFL_ENABLE_ENCRYPTION_PACKET) + { + + // Current transaction ID better be zero, otherwise, we have two or + // more begin packets in a row. + + if (m_uiCurrTransID) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + + m_uiCurrTransID = (FLMUINT) FB2UD( pucPacketBody); + pucPacketBody += 4; + + // Make sure the transaction numbers are ascending + + if (m_uiCurrTransID <= m_uiLastTransID) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + + if (uiPacketType == RFL_TRNS_BEGIN_EX_PACKET) + { + FLMUINT uiLastLoggedCommitTransID; + + // Skip past seconds + + pucPacketBody += 4; + + uiLastLoggedCommitTransID = (FLMUINT) FB2UD( pucPacketBody); + pucPacketBody += 4; + + if (m_pFile->FileHdr.uiVersionNum >= FLM_FILE_FORMAT_VER_4_31 && + m_uiLastLoggedCommitTransID != uiLastLoggedCommitTransID) + { + rc = RC_SET( FERR_RFL_TRANS_GAP); + goto Exit; + } + } + } + else + { + + // If transaction ID is not zero, we are not inside a transaction, + // and it is likely that we have a corrupt packet. + + if (!m_uiCurrTransID) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + + // Decrypt the packet if it is a packet type that was encrypted. + + if (uiPacketType == RFL_TRNS_COMMIT_PACKET || + uiPacketType == RFL_TRNS_ABORT_PACKET) + { + if ((FLMUINT) FB2UD( pucPacketBody) != m_uiCurrTransID) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + } + } + + // Set read offset to beginning of next packet. + + m_uiRflReadOffset += (RFL_PACKET_OVERHEAD + uiEncryptPacketBodyLen); + +Exit: + + return (rc); +} + +/******************************************************************** +Desc: Get a record from the packets in the roll-forward log. + This expects a series of RFL_DATA_RECORD_PACKETs. +*********************************************************************/ +RCODE F_Rfl::getRecord( + FDB * pDb, + FLMUINT uiPacketType, + FLMBYTE * pucPacketBody, + FLMUINT uiPacketBodyLen, + FlmRecord * pRecord) +{ + RCODE rc = FERR_OK; + FLMUINT uiTagNum; + FLMUINT uiDataType; + FLMUINT uiLevel; + FLMUINT uiDataLen; + FLMBYTE * pucFieldData = NULL; + void * pvField; + FLMBOOL bEncrypted = FALSE; + FLMUINT uiEncId = 0; + FLMUINT uiEncDataLen = 0; + POOL pool; + + GedPoolInit( &pool, 512); + + // Go into a loop processing packets until we have retrieved all of + // the fields of the record. At that point, we had better be at the end + // of the record. + + for (;;) + { + + // If we don't currently have a packet, get one Packet type had + // better be RFL_DATA_RECORD_PACKET. + + if (!uiPacketBodyLen) + { + if (RC_BAD( rc = getPacket( FALSE, &uiPacketType, &pucPacketBody, + &uiPacketBodyLen, NULL))) + { + goto Exit; + } + + if (uiPacketType != RFL_DATA_RECORD_PACKET && + uiPacketType != RFL_ENC_DATA_RECORD_PACKET && + uiPacketType != RFL_DATA_RECORD_PACKET_VER_3) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + } + + // Packet body length better be at least two or we have an + // incomplete packet - we need to at least be able to get the tag + // number at this point. + + if (uiPacketBodyLen < 2) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + else if (uiPacketBodyLen == 2) + { + + // If the packet body length is only two, we had better be at + // the end of the record with a tag number of zero. Otherwise, we + // have an incomplete packet. + + if ((uiTagNum = (FLMUINT) FB2UW( pucPacketBody)) != 0) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + break; + } + else if (uiPacketType == RFL_DATA_RECORD_PACKET) + { + flmAssert( m_pFile->FileHdr.uiVersionNum <= FLM_FILE_FORMAT_VER_4_60); + if (uiPacketBodyLen < 6) + { + + // If we have a packet body length less than six (for + // RFL_DATA_RECORD_PACKETs), we have an incomplete field + // header. + + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + } + else if (uiPacketType == RFL_ENC_DATA_RECORD_PACKET) + { + + // This type of packet is only valid with versions of flaim >= + // 4.60 + + flmAssert( m_pFile->FileHdr.uiVersionNum == FLM_FILE_FORMAT_VER_4_60); + + if (uiPacketBodyLen < 7) + { + + // If we have a packet body length less than seven we have an + // incomplete field header. + + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + } + else + { + flmAssert( uiPacketType == RFL_DATA_RECORD_PACKET_VER_3); + flmAssert( m_pFile->FileHdr.uiVersionNum >= FLM_FILE_FORMAT_VER_4_61); + if (uiPacketBodyLen < 9) + { + + // If we have a packet body length less than nine we have an + // incomplete field header. + + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + } + + // At this point, we have a packet body length that is greater than + // or equal to seven (or six), meaning we could not possibly be on + // the last field of the record. Hence, a zero tag number is invalid + // here. + + if ((uiTagNum = (FLMUINT) FB2UW( pucPacketBody)) == 0) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + + pucPacketBody += 2; + uiDataType = *pucPacketBody++; + uiLevel = *pucPacketBody++; + if (uiPacketType != RFL_DATA_RECORD_PACKET_VER_3) + { + uiDataLen = (FLMUINT) FB2UW( pucPacketBody); + pucPacketBody += 2; + uiPacketBodyLen -= 6; + } + else + { + uiDataLen = (FLMUINT) FB2UD( pucPacketBody); + pucPacketBody += 4; + uiPacketBodyLen -= 8; + } + + // If the database version supports encryption, we need to check + // for it. + + if (uiPacketType == RFL_ENC_DATA_RECORD_PACKET) + { + bEncrypted = (FLMBOOL) * pucPacketBody++; + --uiPacketBodyLen; + + if (bEncrypted) + { + if (uiPacketBodyLen < 4) + { + flmAssert( 0); + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + + // Extract the encryption ID and the encrypted length. + + uiEncId = FB2UW( pucPacketBody); + pucPacketBody += 2; + + uiEncDataLen = FB2UW( pucPacketBody); + pucPacketBody += 2; + + uiPacketBodyLen -= 4; + } + } + else if (uiPacketType == RFL_DATA_RECORD_PACKET_VER_3) + { + bEncrypted = (FLMBOOL) * pucPacketBody++; + --uiPacketBodyLen; + + if (bEncrypted) + { + if (uiPacketBodyLen < 6) + { + flmAssert( 0); + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + + // Extract the encryption ID and the encrypted length. + + uiEncId = FB2UW( pucPacketBody); + pucPacketBody += 2; + + uiEncDataLen = FB2UD( pucPacketBody); + pucPacketBody += 4; + + uiPacketBodyLen -= 6; + } + } + + // Create a new field. + + if (RC_BAD( rc = pRecord->insertLast( uiLevel, uiTagNum, uiDataType, + &pvField))) + { + goto Exit; + } + + if (!bEncrypted && uiDataLen) + { + if (RC_BAD( rc = pRecord->allocStorageSpace( pvField, uiDataType, + uiDataLen, 0, 0, 0, &pucFieldData, NULL))) + { + goto Exit; + } + + while (uiDataLen) + { + if (uiDataLen > uiPacketBodyLen) + { + f_memcpy( pucFieldData, pucPacketBody, uiPacketBodyLen); + pucFieldData += uiPacketBodyLen; + pucPacketBody += uiPacketBodyLen; + uiDataLen -= uiPacketBodyLen; + + uiPacketBodyLen = 0; + + // Get the next packet. Packet type had better be + // RFL_DATA_RECORD_PACKET. + + if (RC_BAD( rc = getPacket( FALSE, &uiPacketType, &pucPacketBody, + &uiPacketBodyLen, NULL))) + { + goto Exit; + } + + if (uiPacketType != RFL_DATA_RECORD_PACKET && + uiPacketType != RFL_ENC_DATA_RECORD_PACKET && + uiPacketType != RFL_DATA_RECORD_PACKET_VER_3) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + } + else + { + f_memcpy( pucFieldData, pucPacketBody, uiDataLen); + pucFieldData += uiDataLen; + uiPacketBodyLen -= uiDataLen; + pucPacketBody += uiDataLen; + uiDataLen = 0; + } + } + + pucFieldData = NULL; + } + else if (bEncrypted) + { + FLMBYTE * pucEncFieldData; + + if (uiEncDataLen) + { + if (RC_BAD( rc = pRecord->allocStorageSpace( pvField, uiDataType, + uiDataLen, uiEncDataLen, uiEncId, + FLD_HAVE_ENCRYPTED_DATA, &pucFieldData, &pucEncFieldData + ))) + { + goto Exit; + } + } + + while (uiEncDataLen) + { + if (uiEncDataLen > uiPacketBodyLen) + { + f_memcpy( pucEncFieldData, pucPacketBody, uiPacketBodyLen); + pucEncFieldData += uiPacketBodyLen; + pucPacketBody += uiPacketBodyLen; + uiEncDataLen -= uiPacketBodyLen; + + uiPacketBodyLen = 0; + + // Get the next packet. Packet type had better be + // RFL_ENC_DATA_RECORD_PACKET. + + if (RC_BAD( rc = getPacket( FALSE, &uiPacketType, &pucPacketBody, + &uiPacketBodyLen, NULL))) + { + goto Exit; + } + + if (uiPacketType != RFL_ENC_DATA_RECORD_PACKET && + uiPacketType != RFL_DATA_RECORD_PACKET_VER_3) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + } + else + { + f_memcpy( pucEncFieldData, pucPacketBody, uiEncDataLen); + pucEncFieldData += uiEncDataLen; + uiPacketBodyLen -= uiEncDataLen; + pucPacketBody += uiEncDataLen; + uiEncDataLen = 0; + } + } + + pucEncFieldData = NULL; + + if (!m_pFile->bInLimitedMode) + { + if (RC_BAD( rc = flmDecryptField( pDb->pDict, pRecord, pvField, + uiEncId, &pool))) + { + goto Exit; + } + } + } + } + +Exit: + + GedPoolFree( &pool); + + return (rc); +} + +/******************************************************************** +Desc: Modify a record using RFL_DATA_RECORD_PACKETs or + RFL_CHANGE_FIELD_PACKETs. +*********************************************************************/ +RCODE F_Rfl::modifyRecord( + HFDB hDb, + FlmRecord * pRecord) +{ + RCODE rc = FERR_OK; + FLMUINT uiPacketType; + FLMBYTE * pucPacketBody; + FLMUINT uiPacketBodyLen; + + FLMUINT uiChangeType; + FLMUINT uiPosition; + FLMUINT uiTagNum = 0; + FLMUINT uiDataType = 0; + FLMUINT uiLevel = 0; + FLMUINT uiDataLen = 0; + FLMBYTE * pucData; + FLMBYTE * pucEncData; + FDB * pDb = (FDB *) hDb; + FLMBOOL bEncrypted = FALSE; + FLMUINT uiEncDataLen; + FLMUINT uiEncId; + FLMUINT uiFlags; + FlmField * pField; + FLMUINT uiCurPos = 1; + void * pvField; + + // Get the first packet and see what it is. If it is an + // RFL_DATA_RECORD_PACKET, just call Rfl3GetRecord to get the entire + // new record. + + if (RC_BAD( rc = getPacket( FALSE, &uiPacketType, &pucPacketBody, + &uiPacketBodyLen, NULL))) + { + goto Exit; + } + + if (uiPacketType == RFL_DATA_RECORD_PACKET || + uiPacketType == RFL_ENC_DATA_RECORD_PACKET || + uiPacketType == RFL_DATA_RECORD_PACKET_VER_3) + { + pRecord->clear(); + rc = getRecord( pDb, uiPacketType, pucPacketBody, uiPacketBodyLen, pRecord); + goto Exit; + } + else if (uiPacketType != RFL_CHANGE_FIELDS_PACKET) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + + // Go into a loop processing packets until we have processed all of + // the changed fields for the record. + + pField = pRecord->getFieldPointer( pRecord->root()); + + flmAssert( pField); + + for (;;) + { + uiEncDataLen = 0; + uiEncId = 0; + + // If we don't currently have a packet, get one Packet type had + // better be RFL_CHANGE_FIELDS_PACKET. + + if (!uiPacketBodyLen) + { + if (RC_BAD( rc = getPacket( FALSE, &uiPacketType, &pucPacketBody, + &uiPacketBodyLen, NULL))) + { + goto Exit; + } + + if (uiPacketType != RFL_CHANGE_FIELDS_PACKET) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + } + + // Packet body length better be at least three or we have an + // incomplete packet - we need to at least be able to get the type + // of change and the absolute position of the change. + + if (uiPacketBodyLen < 3) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + + // Get the change type and the absolute position where the change + // is to be put. A position of zero is illegal. + + uiChangeType = *pucPacketBody++; + uiPosition = (FLMUINT) FB2UW( pucPacketBody); + pucPacketBody += 2; + uiPacketBodyLen -= 3; + + if (uiChangeType == RFL_END_FIELD_CHANGES) + { + + // If we are not at the end of the packet, it must be a bad + // packet. Also, uiPosition should be a zero for this packet. + + if (uiPacketBodyLen || uiPosition) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + break; + } + + // If not RFL_END_FIELD_CHANGES, a position of zero is illegal. + + if (!uiPosition) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + + switch (uiChangeType) + { + case RFL_INSERT_FIELD: + { + flmAssert( m_pFile->FileHdr.uiVersionNum <= FLM_FILE_FORMAT_VER_4_60); + bEncrypted = FALSE; + if (uiPacketBodyLen < 6) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + + uiTagNum = (FLMUINT) FB2UW( pucPacketBody); + pucPacketBody += 2; + uiDataType = *pucPacketBody++; + uiLevel = *pucPacketBody++; + uiDataLen = (FLMUINT)FB2UW( pucPacketBody); + pucPacketBody += 2; + uiPacketBodyLen -= 6; + break; + } + case RFL_INSERT_ENC_FIELD: + { + flmAssert( m_pFile->FileHdr.uiVersionNum == FLM_FILE_FORMAT_VER_4_60); + bEncrypted = TRUE; + if (uiPacketBodyLen < 10) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + + uiTagNum = (FLMUINT) FB2UW( pucPacketBody); + pucPacketBody += 2; + uiDataType = *pucPacketBody++; + uiLevel = *pucPacketBody++; + uiDataLen = (FLMUINT)FB2UW( pucPacketBody); + pucPacketBody += 2; + + uiEncId = FB2UW( pucPacketBody); + pucPacketBody += 2; + + uiEncDataLen = FB2UW( pucPacketBody); + pucPacketBody += 2; + + uiPacketBodyLen -= 10; + break; + } + case RFL_INSERT_LARGE_FIELD: + { + flmAssert( m_pFile->FileHdr.uiVersionNum >= FLM_FILE_FORMAT_VER_4_61); + bEncrypted = FALSE; + if (uiPacketBodyLen < 8) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + uiTagNum = (FLMUINT) FB2UW( pucPacketBody); + pucPacketBody += 2; + uiDataType = *pucPacketBody++; + uiLevel = *pucPacketBody++; + uiDataLen = (FLMUINT)FB2UD( pucPacketBody); + pucPacketBody += 4; + uiPacketBodyLen -= 8; + break; + } + case RFL_INSERT_ENC_LARGE_FIELD: + { + flmAssert( m_pFile->FileHdr.uiVersionNum >= FLM_FILE_FORMAT_VER_4_61); + bEncrypted = TRUE; + if (uiPacketBodyLen < 14) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + uiTagNum = (FLMUINT) FB2UW( pucPacketBody); + pucPacketBody += 2; + uiDataType = *pucPacketBody++; + uiLevel = *pucPacketBody++; + uiDataLen = (FLMUINT)FB2UD( pucPacketBody); + pucPacketBody += 4; + + uiEncId = FB2UW( pucPacketBody); + pucPacketBody += 2; + + uiEncDataLen = FB2UW( pucPacketBody); + pucPacketBody += 4; + + uiPacketBodyLen -= 14; + break; + } + + case RFL_MODIFY_FIELD: + { + flmAssert( m_pFile->FileHdr.uiVersionNum <= FLM_FILE_FORMAT_VER_4_60); + bEncrypted = FALSE; + if (uiPacketBodyLen < 3 || *pucPacketBody != RFL_REPLACE_BYTES) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + + pucPacketBody++; + uiDataLen = (FLMUINT) FB2UW( pucPacketBody); + pucPacketBody += 2; + uiPacketBodyLen -= 3; + break; + } + + case RFL_MODIFY_ENC_FIELD: + { + flmAssert( m_pFile->FileHdr.uiVersionNum == FLM_FILE_FORMAT_VER_4_60); + bEncrypted = TRUE; + if (uiPacketBodyLen < 7 || *pucPacketBody != RFL_REPLACE_BYTES) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + + pucPacketBody++; + uiDataLen = (FLMUINT) FB2UW( pucPacketBody); + pucPacketBody += 2; + + uiEncId = FB2UW( pucPacketBody); + pucPacketBody += 2; + + uiEncDataLen = FB2UW( pucPacketBody); + pucPacketBody += 2; + + uiPacketBodyLen -= 7; + break; + } + case RFL_MODIFY_LARGE_FIELD: + { + flmAssert( m_pFile->FileHdr.uiVersionNum >= FLM_FILE_FORMAT_VER_4_61); + bEncrypted = FALSE; + if (uiPacketBodyLen < 5 || *pucPacketBody != RFL_REPLACE_BYTES) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + pucPacketBody++; + uiDataLen = (FLMUINT)FB2UD( pucPacketBody); + pucPacketBody += 4; + uiPacketBodyLen -= 5; + break; + } + case RFL_MODIFY_ENC_LARGE_FIELD: + { + flmAssert( m_pFile->FileHdr.uiVersionNum >= FLM_FILE_FORMAT_VER_4_61); + bEncrypted = TRUE; + if (uiPacketBodyLen < 11 || *pucPacketBody != RFL_REPLACE_BYTES) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + + pucPacketBody++; + uiDataLen = (FLMUINT)FB2UD( pucPacketBody); + pucPacketBody += 4; + + uiEncId = FB2UW( pucPacketBody); + pucPacketBody += 2; + + uiEncDataLen = FB2UD( pucPacketBody); + pucPacketBody += 4; + + uiPacketBodyLen -= 11; + break; + } + + case RFL_DELETE_FIELD: + { + break; + } + + default: + { + + // Bad change type in packet. + + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + } + + // Now position to the target field. + + switch (uiChangeType) + { + case RFL_DELETE_FIELD: + case RFL_MODIFY_FIELD: + case RFL_MODIFY_ENC_FIELD: + case RFL_MODIFY_LARGE_FIELD: + case RFL_MODIFY_ENC_LARGE_FIELD: + { + while (uiCurPos != uiPosition) + { + if (uiPosition < uiCurPos) + { + flmAssert( pField->uiPrev); + pField = pRecord->prevField( pField); + --uiCurPos; + } + else + { + flmAssert( pField->uiNext); + pField = pRecord->nextField( pField); + uiCurPos++; + } + } + + if (uiChangeType != RFL_DELETE_FIELD) + { + + // Get the data type ... not supplied in the modify field + // packet. + + uiDataType = pRecord->getFieldDataType( pField); + uiTagNum = pField->ui16FieldID; + } + break; + } + + case RFL_INSERT_FIELD: + case RFL_INSERT_ENC_FIELD: + case RFL_INSERT_LARGE_FIELD: + case RFL_INSERT_ENC_LARGE_FIELD: + { + FlmField * pNewField; + + // On insert, we may be trying to position to a field that + // does not exist yet. Therefore we need to position to the + // field prior to the field position we want to insert. + + flmAssert( uiPosition > 1); // cannot insert at the root + ///position. + + while (uiCurPos != uiPosition - 1) + { + if (uiPosition - 1 < uiCurPos) + { + flmAssert( pField->uiPrev); + pField = pRecord->prevField( pField); + --uiCurPos; + } + else + { + flmAssert( pField->uiNext); + pField = pRecord->nextField( pField); + uiCurPos++; + } + } + + // Insert the new field at the specified position and get + // back a new field to use later. + + if (RC_BAD( rc = pRecord->createField( pField, &pNewField))) + { + goto Exit; + } + + if (RC_BAD( rc = pRecord->setFieldLevel( pNewField, uiLevel))) + { + goto Exit; + } + + pField = pNewField; + pField->ui16FieldID = (FLMUINT16) uiTagNum; + uiCurPos++; // Bump the position as we have + ///just added a new field and + + // we are positioned on it. + + break; + } + } + + if (uiChangeType == RFL_DELETE_FIELD) + { + + // Remove the specified field or subtree + + pvField = (void *) ((FLMUINT) (pField->uiPrev)); + --uiCurPos; + if (!pvField) + { + pvField = pRecord->root(); + uiCurPos = 1; + } + + // For versions 4.60 and greater, the interpretation for + // RFL_DELETE_FIELD is to delete the entire sub-tree. Prior to + // that, it is to delete only a single field. + + if (m_pFile->FileHdr.uiVersionNum >= FLM_FILE_FORMAT_VER_4_60) + { + if (RC_BAD( rc = pRecord->remove( pField))) + { + goto Exit; + } + } + else + { + + // Passing in the same pointer to removeFields will + // effectively delete just pField. + + if (RC_BAD( rc = pRecord->removeFields( pField, pField))) + { + goto Exit; + } + } + + // We need to reset our pField. + + pField = pRecord->getFieldPointer( pvField); + + continue; // Next field... + } + + // Both insert & modify need to have space allocated. + + if (bEncrypted) + { + uiFlags = FLD_HAVE_ENCRYPTED_DATA; + } + else + { + uiFlags = 0; + } + + flmAssert( pField); + + // Allocate space for the data. We call this even if uiDataLen is + // zero so that the appropriate data type will be set in the node as + // well. ; + // Before we allocate storage space, save the field offset. The + // field buffer may get reallocated, resulting in a nwe address for + // pField. + pvField = pRecord->getFieldVoid( pField); + if (RC_BAD( rc = pRecord->getNewDataPtr( pField, uiDataType, uiDataLen, + uiEncDataLen, uiEncId, uiFlags, &pucData, &pucEncData))) + { + goto Exit; + } + + pField = pRecord->getFieldPointer( pvField); + + // Get the data for insert or modify, if any + + if (bEncrypted) + { + while (uiEncDataLen) + { + if (uiEncDataLen > uiPacketBodyLen) + { + f_memcpy( pucEncData, pucPacketBody, uiPacketBodyLen); + pucEncData += uiPacketBodyLen; + uiEncDataLen -= uiPacketBodyLen; + uiPacketBodyLen = 0; + + // Get the next packet. Packet type had better be + // RFL_CHANGE_FIELDS_PACKET. + + if (RC_BAD( rc = getPacket( FALSE, &uiPacketType, &pucPacketBody, + &uiPacketBodyLen, NULL))) + { + goto Exit; + } + + if (uiPacketType != RFL_CHANGE_FIELDS_PACKET) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + } + else + { + f_memcpy( pucEncData, pucPacketBody, uiEncDataLen); + pucEncData += uiEncDataLen; + uiPacketBodyLen -= uiEncDataLen; + pucPacketBody += uiEncDataLen; + uiEncDataLen = 0; + } + } + } + else // Not encrypted + { + while (uiDataLen) + { + if (uiDataLen > uiPacketBodyLen) + { + f_memcpy( pucData, pucPacketBody, uiPacketBodyLen); + pucData += uiPacketBodyLen; + uiDataLen -= uiPacketBodyLen; + uiPacketBodyLen = 0; + + // Get the next packet. Packet type had better be + // RFL_CHANGE_FIELDS_PACKET. + + if (RC_BAD( rc = getPacket( FALSE, &uiPacketType, &pucPacketBody, + &uiPacketBodyLen, NULL))) + { + goto Exit; + } + + if (uiPacketType != RFL_CHANGE_FIELDS_PACKET) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + } + else + { + f_memcpy( pucData, pucPacketBody, uiDataLen); + pucData += uiDataLen; + uiPacketBodyLen -= uiDataLen; + pucPacketBody += uiDataLen; + uiDataLen = 0; + } + } + } + + // If this field is involved in an index, and it is encrypted, we + // need to make sure we decrypt it too. If it is not encrypted, we + // don't care if it involved in an index. + + if (bEncrypted && !(pDb->pFile->bInLimitedMode)) + { + IFD * pIfd; + + if (RC_BAD( rc = fdictGetField( pDb->pDict, uiTagNum, NULL, &pIfd, NULL))) + { + goto Exit; + } + + if (pIfd) + { + if (RC_BAD( rc = flmDecryptField( pDb->pDict, pRecord, + pRecord->getFieldVoid( pField), uiEncId, &pDb->TempPool))) + { + goto Exit; + } + } + } + } + +Exit: + + return (rc); +} + +/******************************************************************** +Desc: Read the next operation from the roll-forward log. +*********************************************************************/ +RCODE F_Rfl::readOp( + FDB * pDb, + FLMBOOL bForceNextFile, + RFL_OP_INFO * pOpInfo, + FlmRecord * pRecord) +{ + RCODE rc = FERR_OK; + FLMBYTE * pucPacketBody; + FLMUINT uiPacketBodyLen; + FLMUINT uiExpectedBodyLen; + FLMBOOL bLoggedTimes; + + f_memset( pOpInfo, 0, sizeof( RFL_OP_INFO)); + + // Get the next packet. + + if (RC_BAD( rc = getPacket( bForceNextFile, &pOpInfo->uiPacketType, + &pucPacketBody, &uiPacketBodyLen, &bLoggedTimes))) + { + goto Exit; + } + + // Must be one of our packet types that represents an operation. + + switch (pOpInfo->uiPacketType) + { + case RFL_TRNS_BEGIN_PACKET: + { + uiExpectedBodyLen = 8; + if (bLoggedTimes) + { + uiExpectedBodyLen += 4; + } + + if (uiExpectedBodyLen != uiPacketBodyLen) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + + pOpInfo->uiTransId = (FLMUINT) FB2UD( pucPacketBody); + pucPacketBody += 4; + pOpInfo->uiStartTime = (FLMUINT) FB2UD( pucPacketBody); + pucPacketBody += 4; + break; + } + + case RFL_TRNS_BEGIN_EX_PACKET: + { + uiExpectedBodyLen = 12; + if (uiExpectedBodyLen != uiPacketBodyLen) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + + pOpInfo->uiTransId = (FLMUINT) FB2UD( pucPacketBody); + pucPacketBody += 4; + pOpInfo->uiStartTime = (FLMUINT) FB2UD( pucPacketBody); + pucPacketBody += 4; + pOpInfo->uiLastLoggedCommitTransId = (FLMUINT) FB2UD( pucPacketBody); + pucPacketBody += 4; + break; + } + + case RFL_TRNS_COMMIT_PACKET: + case RFL_TRNS_ABORT_PACKET: + { + uiExpectedBodyLen = 8; + if (bLoggedTimes) + { + uiExpectedBodyLen += 8; + } + + if (uiExpectedBodyLen != uiPacketBodyLen) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + + pOpInfo->uiTransId = (FLMUINT) FB2UD( pucPacketBody); + pucPacketBody += 8; + break; + } + + case RFL_ADD_RECORD_PACKET: + case RFL_MODIFY_RECORD_PACKET: + case RFL_DELETE_RECORD_PACKET: + case RFL_RESERVE_DRN_PACKET: + { + uiExpectedBodyLen = 10; + if (bLoggedTimes) + { + uiExpectedBodyLen += 16; + } + + if (uiExpectedBodyLen != uiPacketBodyLen) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + + if ((pOpInfo->uiTransId = + (FLMUINT) FB2UD( pucPacketBody)) != m_uiCurrTransID) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + pucPacketBody += 4; + + pOpInfo->uiContainer = (FLMUINT) FB2UW( pucPacketBody); + pucPacketBody += 2; + + pOpInfo->uiDrn = (FLMUINT) FB2UD( pucPacketBody); + pucPacketBody += 4; + + if (pOpInfo->uiPacketType == RFL_ADD_RECORD_PACKET) + { + if (RC_BAD( rc = getRecord( pDb, 0, NULL, 0, pRecord))) + { + goto Exit; + } + } + break; + } + + case RFL_ADD_RECORD_PACKET_VER_2: + case RFL_MODIFY_RECORD_PACKET_VER_2: + case RFL_DELETE_RECORD_PACKET_VER_2: + { + uiExpectedBodyLen = 11; + if (bLoggedTimes) + { + uiExpectedBodyLen += 16; + } + + if (uiExpectedBodyLen != uiPacketBodyLen) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + + if ((pOpInfo->uiTransId = + (FLMUINT) FB2UD( pucPacketBody)) != m_uiCurrTransID) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + pucPacketBody += 4; + + pOpInfo->uiContainer = (FLMUINT) FB2UW( pucPacketBody); + pucPacketBody += 2; + + pOpInfo->uiDrn = (FLMUINT) FB2UD( pucPacketBody); + pucPacketBody += 4; + + pOpInfo->uiFlags = *pucPacketBody; + pucPacketBody++; + + // Translate the flags + + if (pOpInfo->uiFlags) + { + FLMUINT uiTmp = 0; + + if (pOpInfo->uiFlags & RFL_UPDATE_BACKGROUND) + { + uiTmp |= FLM_DO_IN_BACKGROUND; + } + + if (pOpInfo->uiFlags & RFL_UPDATE_SUSPENDED) + { + uiTmp |= FLM_SUSPENDED; + } + + pOpInfo->uiFlags = uiTmp; + } + + if (pOpInfo->uiPacketType == RFL_ADD_RECORD_PACKET_VER_2) + { + if (RC_BAD( rc = getRecord( pDb, 0, NULL, 0, pRecord))) + { + goto Exit; + } + } + break; + } + + case RFL_INDEX_SET_PACKET: + case RFL_INDEX_SET_PACKET_VER_2: + { + uiExpectedBodyLen = + (FLMUINT) ((pOpInfo->uiPacketType == RFL_INDEX_SET_PACKET_VER_2) + ? (FLMUINT) 16 + : (FLMUINT) 14); + + if (bLoggedTimes) + { + uiExpectedBodyLen += 16; + } + + if (uiExpectedBodyLen != uiPacketBodyLen) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + + if ((pOpInfo->uiTransId = + (FLMUINT) FB2UD( pucPacketBody)) != m_uiCurrTransID) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + pucPacketBody += 4; + + if (pOpInfo->uiPacketType == RFL_INDEX_SET_PACKET_VER_2) + { + pOpInfo->uiContainer = (FLMUINT) FB2UW( pucPacketBody); + pucPacketBody += 2; + } + + pOpInfo->uiIndex = (FLMUINT) FB2UW( pucPacketBody); + pucPacketBody += 2; + + pOpInfo->uiDrn = (FLMUINT) FB2UD( pucPacketBody); + pucPacketBody += 4; + + pOpInfo->uiEndDrn = (FLMUINT) FB2UD( pucPacketBody); + pucPacketBody += 4; + + break; + } + + case RFL_START_UNKNOWN_PACKET: + { + uiExpectedBodyLen = 4; + if (uiExpectedBodyLen != uiPacketBodyLen) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + + if ((pOpInfo->uiTransId = + (FLMUINT) FB2UD( pucPacketBody)) != m_uiCurrTransID) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + + pucPacketBody += 4; + break; + } + + case RFL_REDUCE_PACKET: + { + uiExpectedBodyLen = 8; + if (uiExpectedBodyLen != uiPacketBodyLen) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + + pOpInfo->uiTransId = (FLMUINT) FB2UD( pucPacketBody); + pOpInfo->uiLastLoggedCommitTransId = pOpInfo->uiTransId; + pucPacketBody += 4; + + pOpInfo->uiCount = (FLMUINT) FB2UD( pucPacketBody); + pucPacketBody += 4; + + break; + } + + case RFL_BLK_CHAIN_FREE_PACKET: + { + uiExpectedBodyLen = 16; + + if (bLoggedTimes) + { + uiExpectedBodyLen += 16; + } + + if (uiExpectedBodyLen != uiPacketBodyLen) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + + if ((pOpInfo->uiTransId = + (FLMUINT) FB2UD( pucPacketBody)) != m_uiCurrTransID) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + + pucPacketBody += 4; + + // Tracker record ID + + pOpInfo->uiDrn = (FLMUINT) FB2UD( pucPacketBody); + pucPacketBody += 4; + + // Count + + pOpInfo->uiCount = (FLMUINT) FB2UD( pucPacketBody); + pucPacketBody += 4; + + // Ending block address + + pOpInfo->uiEndBlock = (FLMUINT) FB2UD( pucPacketBody); + pucPacketBody += 4; + + break; + } + + case RFL_INDEX_SUSPEND_PACKET: + case RFL_INDEX_RESUME_PACKET: + { + uiExpectedBodyLen = 6; + if (uiExpectedBodyLen != uiPacketBodyLen) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + + if ((pOpInfo->uiTransId = + (FLMUINT) FB2UD( pucPacketBody)) != m_uiCurrTransID) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + + pucPacketBody += 4; + + pOpInfo->uiIndex = (FLMUINT) FB2UW( pucPacketBody); + pucPacketBody += 2; + break; + } + + case RFL_UPGRADE_PACKET: + { + FLMUINT uiDBKeyLen; + + uiExpectedBodyLen = 12; + if (uiExpectedBodyLen > uiPacketBodyLen) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + + pOpInfo->uiTransId = (FLMUINT) FB2UD( pucPacketBody); + pucPacketBody += 4; + uiPacketBodyLen -= 4; + + pOpInfo->uiOldVersion = (FLMUINT) FB2UD( pucPacketBody); + pucPacketBody += 4; + uiPacketBodyLen -= 4; + + pOpInfo->uiNewVersion = (FLMUINT) FB2UD( pucPacketBody); + pucPacketBody += 4; + uiPacketBodyLen -= 4; + + // Only look for the wrapping key if the new database version is + // greater than 4.60 and there isn't already a key. + + if (pOpInfo->uiEndDrn >= FLM_FILE_FORMAT_VER_4_60 && + !m_pFile->pDbWrappingKey) + { + if (uiPacketBodyLen < 2) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + + uiDBKeyLen = FB2UW( pucPacketBody); + pucPacketBody += 2; + uiPacketBodyLen -= 2; + + if (uiDBKeyLen) + { + if (uiPacketBodyLen != uiDBKeyLen) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + + if ((m_pFile->pDbWrappingKey = f_new F_CCS) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + if (RC_BAD( rc = m_pFile->pDbWrappingKey->init( TRUE, + FLM_NICI_AES))) + { + goto Exit; + } + + if (RC_BAD( rc = m_pFile->pDbWrappingKey->setKeyFromStore( + pucPacketBody, (FLMUINT32) uiDBKeyLen, NULL, NULL, + FALSE))) + { + goto Exit; + } + + pucPacketBody += uiDBKeyLen; + uiPacketBodyLen -= uiDBKeyLen; + flmAssert( !uiPacketBodyLen); + } + } + break; + } + + case RFL_CONFIG_SIZE_EVENT_PACKET: + { + uiExpectedBodyLen = 16; + if (uiExpectedBodyLen != uiPacketBodyLen) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + + pOpInfo->uiTransId = (FLMUINT) FB2UD( pucPacketBody); + pOpInfo->uiLastLoggedCommitTransId = pOpInfo->uiTransId; + pucPacketBody += 4; + + pOpInfo->uiSizeThreshold = (FLMUINT) FB2UD( pucPacketBody); + pucPacketBody += 4; + + pOpInfo->uiTimeInterval = (FLMUINT) FB2UD( pucPacketBody); + pucPacketBody += 4; + + pOpInfo->uiSizeInterval = (FLMUINT) FB2UD( pucPacketBody); + pucPacketBody += 4; + + break; + } + + case RFL_WRAP_KEY_PACKET: + case RFL_ENABLE_ENCRYPTION_PACKET: + { + FLMUINT uiDBKeyLen; + FLMBYTE * pucUncommittedLogHdr = &m_pFile->ucUncommittedLogHdr[0]; + eRestoreActionType eRestoreAction; + + uiExpectedBodyLen = 6; + if (uiExpectedBodyLen >= uiPacketBodyLen) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + + pOpInfo->uiTransId = (FLMUINT) FB2UD( pucPacketBody); + pOpInfo->uiLastLoggedCommitTransId = pOpInfo->uiTransId; + pucPacketBody += 4; + uiPacketBodyLen -= 4; + + if (uiPacketBodyLen < 2) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + + uiDBKeyLen = FB2UW( pucPacketBody); + pucPacketBody += 2; + uiPacketBodyLen -= 2; + + if (m_pRestore) + { + if (RC_BAD( rc = m_pRestore->status( + pOpInfo->uiPacketType == RFL_WRAP_KEY_PACKET + ? RESTORE_WRAP_KEY + : RESTORE_ENABLE_ENCRYPTION, + pOpInfo->uiTransId, (void *) uiDBKeyLen, (void *) 0, + (void *) 0, &eRestoreAction))) + { + goto Exit; + } + + if (eRestoreAction == RESTORE_ACTION_STOP) + { + m_uiCurrTransID = 0; + break; + } + } + + if (uiDBKeyLen) + { + if (uiPacketBodyLen != uiDBKeyLen) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + + // We cannot directly set the key at this point as it may be + // encrypted using a password, which we do not have here. We + // will write the key out to the log header and trust the user + // to know whether or not a password is needed to open the + // database. + + if (RC_BAD( rc = flmBeginDbTrans( pDb, FLM_UPDATE_TRANS, 0, 0))) + { + goto Exit; + } + + f_memcpy( &pucUncommittedLogHdr[ LOG_DATABASE_KEY], pucPacketBody, + uiDBKeyLen); + + UW2FBA( uiDBKeyLen, &pucUncommittedLogHdr[ LOG_DATABASE_KEY_LEN]); + + if (RC_BAD( rc = flmCommitDbTrans( pDb, 0, TRUE))) + { + goto Exit; + } + + pucPacketBody += uiDBKeyLen; + uiPacketBodyLen -= uiDBKeyLen; + flmAssert( !uiPacketBodyLen); + } + + m_uiCurrTransID = 0; + break; + } + + default: + { + flmAssert( 0); + rc = RC_SET( FERR_BAD_RFL_PACKET); + break; + } + } + +Exit: + + return (rc); +} + +/******************************************************************** +Desc: Reads through unknown packets. +*********************************************************************/ +RCODE F_Rfl::readUnknown( + FLMUINT uiLenToRead, + FLMBYTE * pucBuffer, + FLMUINT * puiBytesRead) +{ + RCODE rc = FERR_OK; + FLMUINT uiPacketType; + FLMUINT uiBytesRead = 0; + FLMUINT uiBytesToCopy; + + // If we have read through all of the unknown packets, return + // FERR_EOF_HIT. + + if (!m_bReadingUnknown) + { + rc = RC_SET( FERR_EOF_HIT); + goto Exit; + } + + // Process packets until we have satisfied the read request or until + // we run out of unknown packets. + + while (uiLenToRead) + { + + // Get a packet, if we don't have one. + + if (!m_uiUnknownPacketBodyLen) + { + if (RC_BAD( rc = getPacket( FALSE, &uiPacketType, + &m_pucUnknownPacketBody, &m_uiUnknownPacketBodyLen, NULL))) + { + m_bReadingUnknown = FALSE; + m_uiUnknownPacketRc = rc; + goto Exit; + } + + if (uiPacketType != RFL_UNKNOWN_PACKET) + { + if (!uiBytesRead) + { + rc = RC_SET( FERR_EOF_HIT); + } + + m_bReadingUnknown = FALSE; + + // At this point, we know that the entire packet is inside + // our memory buffer, so it is safe to reset m_uiRflReadOffset + // back to the beginning of the packet. The call to readOp() + // will call getPacket again, which will get this exact same + // packet for processing. + + m_uiRflReadOffset -= (RFL_PACKET_OVERHEAD + m_uiUnknownPacketBodyLen); + goto Exit; + } + + m_uiUnknownBodyLenProcessed = 0; + } + + uiBytesToCopy = uiLenToRead; + if (uiBytesToCopy > m_uiUnknownPacketBodyLen - + m_uiUnknownBodyLenProcessed) + { + uiBytesToCopy = m_uiUnknownPacketBodyLen - m_uiUnknownBodyLenProcessed; + } + + f_memcpy( pucBuffer, m_pucUnknownPacketBody + m_uiUnknownBodyLenProcessed, + uiBytesToCopy); + pucBuffer += uiBytesToCopy; + uiLenToRead -= uiBytesToCopy; + uiBytesRead += uiBytesToCopy; + m_uiUnknownBodyLenProcessed += uiBytesToCopy; + + // If we have exhausted the current packet, reset things so that we + // will get a new packet the next time around. + + if (m_uiUnknownBodyLenProcessed == m_uiUnknownPacketBodyLen) + { + m_uiUnknownPacketBodyLen = 0; + m_uiUnknownBodyLenProcessed = 0; + m_pucUnknownPacketBody = NULL; + } + } + +Exit: + + *puiBytesRead = uiBytesRead; + return (rc); +} + +/******************************************************************** +Desc: Restore transactions from the roll-forward log to the + database. +*********************************************************************/ +RCODE F_Rfl::recover( + FDB * pDb, + F_Restore * pRestore) +{ + RCODE rc = FERR_OK; + HFDB hDb = (HFDB) pDb; + FLMUINT uiStartFileNum; + FLMUINT uiStartOffset; + FLMUINT uiOffset; + FLMUINT uiReadLen; + FLMUINT uiBytesRead; + FLMBYTE ucHdr[ 512]; + FLMUINT uiCount; + RFL_OP_INFO opInfo; + FlmRecord * pRecord = NULL; + FlmRecord * pTmpRecord = NULL; + eRestoreActionType eRestoreAction; + FLMBOOL bTransActive = FALSE; + FLMBOOL bHadOperations = FALSE; + FLMBOOL bLastTransEndedAtFileEOF = FALSE; + FLMBOOL bForceNextFile; + + flmAssert( m_pFile); + + m_pCurrentBuf = &m_Buf1; + m_uiLastLoggedCommitTransID = 0; + + // We need to allow all updates logged in the RFL (including + // dictionary updates). + + pDb->bFldStateUpdOk = TRUE; + + // If we are less than version 4.3, we cannot do restore. + + if (pRestore && m_pFile->FileHdr.uiVersionNum < FLM_FILE_FORMAT_VER_4_3) + { + goto Exit; + } + + // Turn off logging. + + m_bLoggingOff = TRUE; + + // Set the replay flag on the database. + + pDb->uiFlags |= FDB_REPLAYING_RFL; + + // Set the flag as to whether or not we are using multiple RFL files. + + if (m_pFile->FileHdr.uiVersionNum < FLM_FILE_FORMAT_VER_4_3) + { + m_bKeepRflFiles = FALSE; + } + else + { + m_bKeepRflFiles = m_pFile->ucLastCommittedLogHdr[LOG_KEEP_RFL_FILES] + ? TRUE + : FALSE; + } + + // Determine the current, on-disk size of the RFL + + if( m_bKeepRflFiles) + { + FLMUINT64 ui64RflDiskUsage; + + if( RC_BAD( rc = flmRflCalcDiskUsage( m_szRflDir, m_szDbPrefix, + m_pFile->FileHdr.uiVersionNum, &ui64RflDiskUsage))) + { + goto Exit; + } + + f_mutexLock( gv_FlmSysData.hShareMutex); + m_pFile->ui64RflDiskUsage = ui64RflDiskUsage; + f_mutexUnlock( gv_FlmSysData.hShareMutex); + } + + // If pRestore is NULL, we are doing a database recovery after open. + // In that case, we start from the last checkpoint offset and only run + // until the last transaction offset. + + if ((m_pRestore = pRestore) == NULL) + { + FLMBYTE * pucCheckSerialNum; + FLMUINT uiEndOffset; + + uiStartFileNum = (FLMUINT) FB2UD( + &m_pFile->ucLastCommittedLogHdr[ LOG_RFL_LAST_CP_FILE_NUM]); + + m_uiLastRecoverFileNum = (FLMUINT) FB2UD( + &m_pFile->ucLastCommittedLogHdr[ LOG_RFL_FILE_NUM]); + + uiStartOffset = (FLMUINT) FB2UD( + &m_pFile->ucLastCommittedLogHdr[ LOG_RFL_LAST_CP_OFFSET]); + + uiEndOffset = (FLMUINT) FB2UD( + &m_pFile->ucLastCommittedLogHdr[LOG_RFL_LAST_TRANS_OFFSET]); + + // Could be zero if the file was created, but no transactions were + // ever committed to it. + + if (!uiEndOffset) + { + uiEndOffset = 512; + } + + // Start offset better not be less than 512. + + flmAssert( uiStartOffset >= 512); + flmAssert( uiEndOffset >= 512); + + // If start and end are at the same place, there is nothing to + // recover. + + if (uiStartFileNum == m_uiLastRecoverFileNum && + uiStartOffset == uiEndOffset) + { + goto Finish_Recovery; + } + + // We have not recorded the serial number of the last checkpoint + // file number, so we pass in NULL, unless it happens to be the same + // as the last transaction file number, in which case we can pass in + // the serial number we have stored in the log header. + + pucCheckSerialNum = (uiStartFileNum == m_uiLastRecoverFileNum) + ? &m_pFile->ucLastCommittedLogHdr[LOG_LAST_TRANS_RFL_SERIAL_NUM] + : NULL; + + if (RC_BAD( rc = openFile( uiStartFileNum, pucCheckSerialNum))) + { + goto Exit; + } + + // If this is the last RFL file, the EOF is contained in the log + // header. Otherwise, it will be in the RFL file's header, and + // openFile will already have retrieved it. + + if (uiStartFileNum == m_uiLastRecoverFileNum) + { + m_uiFileEOF = uiEndOffset; + } + + // At this point, file EOF better be greater than or equal to 512. + + flmAssert( m_uiFileEOF >= 512); + } + else if (!m_bKeepRflFiles) + { + + // FlmDbRestore should be checking the "keep" flag and not + // attempting to do a restore of the RFL. + + flmAssert( 0); + rc = RC_SET( FERR_CANNOT_RESTORE_RFL_FILES); + goto Exit; + } + else + { + uiStartFileNum = (FLMUINT) FB2UD( + &m_pFile->ucLastCommittedLogHdr[LOG_RFL_FILE_NUM]); + + uiStartOffset = (FLMUINT) FB2UD( + &m_pFile->ucLastCommittedLogHdr[LOG_RFL_LAST_TRANS_OFFSET]); + + // Could be zero if the RFL file had never been created. + + if (!uiStartOffset) + { + uiStartOffset = 512; + } + + // Ask the recovery object to open the file. + +Retry_Open: + + flmAssert( uiStartFileNum); + if (RC_BAD( rc = m_pRestore->openRflFile( uiStartFileNum))) + { + if (rc == FERR_IO_PATH_NOT_FOUND) + { + + // Need to set m_pCurrentBuf->uiCurrFileNum in case the first + // call to openRflFile fails. This will cause the code at the + // Finish_Recovery label to correctly set up the log header. + + if (!uiStartOffset) + { + m_pCurrentBuf->uiCurrFileNum = uiStartFileNum - 1; + } + else + { + m_pCurrentBuf->uiCurrFileNum = uiStartFileNum; + } + + rc = FERR_OK; + goto Finish_Recovery; + } + else + { + goto Exit; + } + } + + // Get the first 512 bytes from the file and verify the header. + + if (RC_BAD( rc = m_pRestore->read( 512, ucHdr, &uiBytesRead))) + { + goto Exit; + } + + if (uiBytesRead < 512) + { + rc = RC_SET( FERR_NOT_RFL); + goto Exit; + } + + if (RC_BAD( rc = verifyHeader( ucHdr, uiStartFileNum, + &m_pFile->ucLastCommittedLogHdr[LOG_LAST_TRANS_RFL_SERIAL_NUM]))) + { + RCODE tmpRc; + + if (RC_BAD( tmpRc = m_pRestore->status( RESTORE_ERROR, 0, + (void *) ((FLMUINT) rc), (void *) 0, (void *) 0, + &eRestoreAction))) + { + rc = tmpRc; + goto Exit; + } + + if (eRestoreAction == RESTORE_ACTION_RETRY) + { + if (RC_BAD( rc = m_pRestore->close())) + { + goto Exit; + } + + goto Retry_Open; + } + + goto Exit; + } + + // We may not know the actual EOF of files during restore + // operations. + + if ((m_uiFileEOF = (FLMUINT) FB2UD( &ucHdr[RFL_EOF_POS])) == 0) + { + bLastTransEndedAtFileEOF = TRUE; + } + else + { + bLastTransEndedAtFileEOF = (m_uiFileEOF == uiStartOffset) + ? TRUE + : FALSE; + } + + // Position to the start offset. Unfortunately, this means reading + // through the data and discarding it. + + uiOffset = 512; + while (uiOffset < uiStartOffset) + { + uiReadLen = (uiStartOffset - uiOffset); + if (uiReadLen > m_uiBufferSize) + { + uiReadLen = m_uiBufferSize; + } + + if (RC_BAD( rc = m_pRestore->read( uiReadLen, + m_pCurrentBuf->pIOBuffer->m_pucBuffer, &uiBytesRead))) + { + goto Exit; + } + + // RFL file is incomplete if we could not read up to the last + // committed transaction. + + if (uiBytesRead < uiReadLen) + { + rc = RC_SET( FERR_RFL_INCOMPLETE); + goto Exit; + } + + uiOffset += uiBytesRead; + } + + // Need to set current file number + + m_pCurrentBuf->uiCurrFileNum = uiStartFileNum; + + // Better not be any transactions to recover - last database state + // needs to be a completed checkpoint. + + flmAssert( + FB2UD( &m_pFile->ucLastCommittedLogHdr [LOG_LAST_CP_TRANS_ID]) == + FB2UD( &m_pFile->ucLastCommittedLogHdr [LOG_CURR_TRANS_ID])); + + // Use uiStartOffset here instead of LOG_RFL_LAST_TRANS_OFFSET, + // because LOG_RFL_LAST_TRANS_OFFSET may be zero, but we in that + // case we should be comparing to 512, and uiStartOffset will have + // been adjusted to 512 if that is the case. + + flmAssert( FB2UD( &m_pFile->ucLastCommittedLogHdr [ + LOG_RFL_LAST_CP_OFFSET]) == uiStartOffset); + + flmAssert( FB2UD( &m_pFile->ucLastCommittedLogHdr[ + LOG_RFL_LAST_CP_FILE_NUM]) == uiStartFileNum); + } + + // Set last transaction ID to the last transaction that was + // checkpointed - transaction numbers should ascend from here. + + m_uiLastTransID = (FLMUINT) FB2UD( + &m_pFile->ucLastCommittedLogHdr[LOG_LAST_CP_TRANS_ID]); + + // Set the last committed trans ID if this is a 4.31+ database + + if (m_pFile->FileHdr.uiVersionNum >= FLM_FILE_FORMAT_VER_4_31) + { + m_uiLastLoggedCommitTransID = (FLMUINT) FB2UD( + &m_pFile->ucLastCommittedLogHdr[LOG_LAST_RFL_COMMIT_ID]); + } + + m_pCurrentBuf->uiRflFileOffset = uiStartOffset; + m_uiRflReadOffset = 0; + m_pCurrentBuf->uiRflBufBytes = 0; + + // Now, read until we are done. + + bForceNextFile = FALSE; + for (;;) + { + if (!pRecord) + { + if ((pRecord = f_new FlmRecord) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + } + + // Get the next operation from the file. + + rc = readOp( pDb, bForceNextFile, &opInfo, pRecord); + bForceNextFile = FALSE; + + if (RC_BAD( rc)) + { +Handle_Packet_Error: + + if (rc == FERR_END) + { + if (!m_pRestore) + { + + // If we didn't end exactly where we should have, we have + // an incomplete log. The same is true if we are in the + // middle of a transaction. + + if (m_pCurrentBuf->uiCurrFileNum != m_uiLastRecoverFileNum || + bTransActive) + { + rc = RC_SET( FERR_RFL_INCOMPLETE); + } + else + { + rc = FERR_OK; + goto Finish_Recovery; + } + } + else + { + + // If we are doing a restore, and we get to the end of the + // log, it is OK - even if we are in the middle of a + // transaction - the transaction will simply be aborted. + + rc = FERR_OK; + goto Finish_Recovery; + } + } + else if (rc == FERR_BAD_RFL_PACKET) + { + + // If we don't know the current file size, and we are doing a + // restore, it is OK to end on a bad packet - we will simply + // abort the current transaction, if any. Then, try to go to + // the next file, because we really don't know where this file + // ends. + + if (m_pRestore && !m_uiFileEOF) + { + if (bTransActive) + { + FlmDbTransAbort( hDb); + bTransActive = FALSE; + } + + // Set current transaction ID to zero - as if we had + // encountered an abort packet. + + m_uiCurrTransID = 0; + bLastTransEndedAtFileEOF = TRUE; + + // Force to go to the next file + + bForceNextFile = TRUE; + rc = FERR_OK; + continue; + } + } + + goto Exit; + } + + // At this point, we know we have a good packet, see what it is and + // handle it. + + bHadOperations = TRUE; + switch (opInfo.uiPacketType) + { + case RFL_TRNS_BEGIN_EX_PACKET: + case RFL_TRNS_BEGIN_PACKET: + { + if (m_pRestore) + { + if (RC_BAD( rc = m_pRestore->status( RESTORE_BEGIN_TRANS, + opInfo.uiTransId, (void *) opInfo.uiStartTime, + (void *) 0, (void *) 0, &eRestoreAction))) + { + goto Exit; + } + + if (eRestoreAction == RESTORE_ACTION_STOP) + { + + // Need to set m_uiCurrTransID to 0 since it was set by + // getPacket(). We are not going to start a transaction + // because of the user's request to exit. + + m_uiCurrTransID = 0; + bLastTransEndedAtFileEOF = FALSE; + goto Finish_Recovery; + } + } + + // If we already have a transaction active, we have a problem. + + flmAssert( !bTransActive); + + if (RC_BAD( rc = FlmDbTransBegin( hDb, FLM_UPDATE_TRANS, 0))) + { + goto Exit; + } + + bTransActive = TRUE; + break; + } + + case RFL_TRNS_COMMIT_PACKET: + { + + // Commit the current transaction. + + if (m_pRestore) + { + if (RC_BAD( rc = m_pRestore->status( RESTORE_COMMIT_TRANS, + opInfo.uiTransId, (void *) 0, (void *) 0, (void *) 0, + &eRestoreAction))) + { + goto Exit; + } + + if (eRestoreAction == RESTORE_ACTION_STOP) + { + bLastTransEndedAtFileEOF = FALSE; + goto Finish_Recovery; + } + } + + flmAssert( bTransActive); + pDb->uiFlags |= FDB_REPLAYING_COMMIT; + rc = FlmDbTransCommit( hDb); + pDb->uiFlags &= ~FDB_REPLAYING_COMMIT; + bTransActive = FALSE; + + if (RC_BAD( rc)) + { + goto Exit; + } + + m_uiLastLoggedCommitTransID = opInfo.uiTransId; + +Finish_Transaction: + + if (!m_uiFileEOF) + { + bLastTransEndedAtFileEOF = TRUE; + } + else + { + bLastTransEndedAtFileEOF = + (m_uiRflReadOffset == m_pCurrentBuf->uiRflBufBytes && + m_pCurrentBuf->uiRflFileOffset + + m_pCurrentBuf->uiRflBufBytes == m_uiFileEOF) + ? TRUE + : FALSE; + } + + m_uiLastTransID = opInfo.uiTransId; + m_uiCurrTransID = 0; + break; + } + + case RFL_TRNS_ABORT_PACKET: + { + + // Abort the current transaction. + + if (m_pRestore) + { + if (RC_BAD( rc = m_pRestore->status( RESTORE_ABORT_TRANS, + opInfo.uiTransId, (void *) 0, (void *) 0, (void *) 0, + &eRestoreAction))) + { + goto Exit; + } + + if (eRestoreAction == RESTORE_ACTION_STOP) + { + bLastTransEndedAtFileEOF = FALSE; + goto Finish_Recovery; + } + } + + flmAssert( bTransActive); + rc = FlmDbTransAbort( hDb); + bTransActive = FALSE; + + if (RC_BAD( rc)) + { + goto Exit; + } + + goto Finish_Transaction; + } + + case RFL_ADD_RECORD_PACKET: + case RFL_ADD_RECORD_PACKET_VER_2: + { + if (m_pRestore) + { + if (RC_BAD( rc = m_pRestore->status( RESTORE_ADD_REC, + opInfo.uiTransId, (void *) opInfo.uiContainer, + (void *) opInfo.uiDrn, + (void *) pRecord, &eRestoreAction))) + { + goto Exit; + } + + if (eRestoreAction == RESTORE_ACTION_STOP) + { + bLastTransEndedAtFileEOF = FALSE; + goto Finish_Recovery; + } + } + + rc = FlmRecordAdd( hDb, opInfo.uiContainer, &opInfo.uiDrn, + pRecord, opInfo.uiFlags); + pRecord->Release(); + pRecord = NULL; + if (RC_BAD( rc)) + { + goto Exit; + } + break; + } + + case RFL_MODIFY_RECORD_PACKET: + case RFL_MODIFY_RECORD_PACKET_VER_2: + { + + // Must retrieve the record and then get the modify packet(s) + // to alter it. + + if (RC_BAD( rc = FlmRecordRetrieve( hDb, opInfo.uiContainer, + opInfo.uiDrn, FO_EXACT, &pRecord, NULL))) + { + goto Exit; + } + + if ((pTmpRecord = pRecord->copy()) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + pRecord->Release(); + pRecord = NULL; + + if (RC_BAD( rc = modifyRecord( hDb, pTmpRecord))) + { + goto Handle_Packet_Error; + } + + // Finally, modify the record in the database. + + if (m_pRestore) + { + if (RC_BAD( rc = m_pRestore->status( RESTORE_MOD_REC, + opInfo.uiTransId, + (void *) opInfo.uiContainer, (void *) opInfo.uiDrn, + (void *) pTmpRecord, &eRestoreAction))) + { + goto Exit; + } + + if (eRestoreAction == RESTORE_ACTION_STOP) + { + bLastTransEndedAtFileEOF = FALSE; + goto Finish_Recovery; + } + } + + rc = FlmRecordModify( hDb, opInfo.uiContainer, opInfo.uiDrn, + pTmpRecord, opInfo.uiFlags); + + pTmpRecord->Release(); + pTmpRecord = NULL; + + if (RC_BAD( rc)) + { + goto Exit; + } + + break; + } + + case RFL_DELETE_RECORD_PACKET: + case RFL_DELETE_RECORD_PACKET_VER_2: + { + if (m_pRestore) + { + if (RC_BAD( rc = m_pRestore->status( RESTORE_DEL_REC, + opInfo.uiTransId, + (void *) opInfo.uiContainer, (void *) opInfo.uiDrn, + (void *) 0, &eRestoreAction))) + { + goto Exit; + } + + if (eRestoreAction == RESTORE_ACTION_STOP) + { + bLastTransEndedAtFileEOF = FALSE; + goto Finish_Recovery; + } + } + + if (RC_BAD( rc = FlmRecordDelete( hDb, opInfo.uiContainer, + opInfo.uiDrn, opInfo.uiFlags))) + { + goto Exit; + } + break; + } + + case RFL_RESERVE_DRN_PACKET: + { + if (m_pRestore) + { + if (RC_BAD( rc = m_pRestore->status( RESTORE_RESERVE_DRN, + opInfo.uiTransId, (void *) opInfo.uiContainer, + (void *) opInfo.uiDrn, (void *) 0, &eRestoreAction))) + { + goto Exit; + } + + if (eRestoreAction == RESTORE_ACTION_STOP) + { + bLastTransEndedAtFileEOF = FALSE; + goto Finish_Recovery; + } + } + + if (RC_BAD( rc = FlmReserveNextDrn( hDb, opInfo.uiContainer, + &opInfo.uiDrn))) + { + goto Exit; + } + + break; + } + + case RFL_INDEX_SUSPEND_PACKET: + { + + if (m_pRestore) + { + if (RC_BAD( rc = m_pRestore->status( RESTORE_INDEX_SUSPEND, + opInfo.uiTransId, (void *) opInfo.uiIndex, + (void *) 0, (void *) 0, &eRestoreAction))) + { + goto Exit; + } + + if (eRestoreAction == RESTORE_ACTION_STOP) + { + bLastTransEndedAtFileEOF = FALSE; + goto Finish_Recovery; + } + } + + if (RC_BAD( rc = FlmIndexSuspend( hDb, opInfo.uiIndex))) + { + goto Exit; + } + + break; + } + + case RFL_INDEX_RESUME_PACKET: + { + + if (m_pRestore) + { + if (RC_BAD( rc = m_pRestore->status( RESTORE_INDEX_RESUME, + opInfo.uiTransId, (void *) opInfo.uiIndex, (void *) 0, + (void *) 0, &eRestoreAction))) + { + goto Exit; + } + + if (eRestoreAction == RESTORE_ACTION_STOP) + { + bLastTransEndedAtFileEOF = FALSE; + goto Finish_Recovery; + } + } + + if (RC_BAD( rc = FlmIndexResume( hDb, opInfo.uiIndex))) + { + goto Exit; + } + break; + } + + case RFL_INDEX_SET_PACKET: + case RFL_INDEX_SET_PACKET_VER_2: + { + if (m_pRestore) + { + if (RC_BAD( rc = m_pRestore->status( RESTORE_INDEX_SET, + opInfo.uiTransId, (void *) opInfo.uiIndex, + (void *) opInfo.uiDrn, (void *) opInfo.uiEndDrn, + &eRestoreAction))) + { + goto Exit; + } + + if (eRestoreAction == RESTORE_ACTION_STOP) + { + bLastTransEndedAtFileEOF = FALSE; + goto Finish_Recovery; + } + } + + if (m_pFile->FileHdr.uiVersionNum < FLM_FILE_FORMAT_VER_4_50 && + opInfo.uiPacketType != RFL_INDEX_SET_PACKET) + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + goto Exit; + } + + if (RC_BAD( rc = flmDbIndexSetOfRecords( hDb, opInfo.uiIndex, + opInfo.uiContainer, opInfo.uiDrn, opInfo.uiEndDrn))) + { + goto Exit; + } + + break; + } + + case RFL_BLK_CHAIN_FREE_PACKET: + { + if (m_pRestore) + { + if (RC_BAD( rc = m_pRestore->status( RESTORE_BLK_CHAIN_DELETE, + opInfo.uiTransId, (void *) opInfo.uiDrn, + (void *) opInfo.uiCount, + (void *) opInfo.uiEndBlock, &eRestoreAction))) + { + goto Exit; + } + + if (eRestoreAction == RESTORE_ACTION_STOP) + { + bLastTransEndedAtFileEOF = FALSE; + goto Finish_Recovery; + } + } + + if (RC_BAD( rc = flmMaintFreeBlockChain( pDb, opInfo.uiDrn, + opInfo.uiCount, opInfo.uiEndBlock, NULL))) + { + goto Exit; + } + break; + } + + case RFL_START_UNKNOWN_PACKET: + { + if (m_pRestore) + { + F_RflUnknownStream unkStrm; + + unkStrm.setup( this, TRUE); + m_bReadingUnknown = TRUE; + m_uiUnknownPacketBodyLen = 0; + m_pucUnknownPacketBody = NULL; + m_uiUnknownBodyLenProcessed = 0; + m_uiUnknownPacketRc = FERR_OK; + + if (RC_BAD( rc = m_pRestore->processUnknown( + (F_UnknownStream*) &unkStrm))) + { + if (m_uiUnknownPacketRc != FERR_OK) + { + rc = m_uiUnknownPacketRc; + goto Handle_Packet_Error; + } + + goto Exit; + } + + // If we did not read through all of the unknown packets, + // skip them at this time. + + if (m_bReadingUnknown) + { + goto Skip_Unknown_Packets; + } + } + else + { +Skip_Unknown_Packets: + + // Skip all unknown packets. + + for (;;) + { + FLMUINT uiPacketType; + FLMBYTE * pucPacketBody; + FLMUINT uiPacketBodyLen; + + if (RC_BAD( rc = getPacket( FALSE, &uiPacketType, + &pucPacketBody, &uiPacketBodyLen, NULL))) + { + goto Handle_Packet_Error; + } + + // If we hit something other than an unknown packet, + // "push" it back into the pipe so it will be processed + // by readOp() up above. + + if (uiPacketType != RFL_UNKNOWN_PACKET) + { + + // At this point, we know that the entire packet is + // inside our memory buffer, so it is safe to reset + // m_uiRflReadOffset back to the beginning of the + // packet. The call to readOp() above will call + // getPacket again, which will get this exact same + // packet for processing. + + m_uiRflReadOffset -= + (RFL_PACKET_OVERHEAD + uiPacketBodyLen); + break; + } + } + } + break; + } + + case RFL_REDUCE_PACKET: + { + + if (m_pRestore) + { + if (RC_BAD( rc = m_pRestore->status( RESTORE_REDUCE, + opInfo.uiTransId, (void *) opInfo.uiCount, + (void *) 0, (void *) 0, &eRestoreAction))) + { + goto Exit; + } + + if (eRestoreAction == RESTORE_ACTION_STOP) + { + + // Need to set m_uiCurrTransID to 0 since it was set by + // getPacket(). We are not going to start a transaction + // because of the user's request to exit. + + m_uiCurrTransID = 0; + bLastTransEndedAtFileEOF = FALSE; + goto Finish_Recovery; + } + } + + if (RC_BAD( rc = FlmDbReduceSize( hDb, opInfo.uiCount, &uiCount))) + { + goto Exit; + } + + goto Finish_Transaction; + } + + case RFL_UPGRADE_PACKET: + { + if (m_pRestore) + { + if (RC_BAD( rc = m_pRestore->status( RESTORE_UPGRADE, + opInfo.uiTransId, + (void *) opInfo.uiOldVersion, + (void *) opInfo.uiNewVersion, + (void *) 0, &eRestoreAction))) + { + goto Exit; + } + + if (eRestoreAction == RESTORE_ACTION_STOP) + { + + // Need to set m_uiCurrTransID to 0 since it was set by + // getPacket(). We are not going to start a transaction + // because of the user's request to exit. + + m_uiCurrTransID = 0; + bLastTransEndedAtFileEOF = FALSE; + goto Finish_Recovery; + } + } + + // Attempt the conversion if the current version is less than + // the target version and the target version is less than or + // equal to the highest version supported by this code. + + if (opInfo.uiNewVersion > FLM_CUR_FILE_FORMAT_VER_NUM) + { + rc = RC_SET( FERR_UNALLOWED_UPGRADE); + goto Exit; + } + else + { + flmAssert( m_pFile->FileHdr.uiVersionNum < opInfo.uiNewVersion); + + // The logged "new" version may be a lesser version than + // FLM_CURRENT_FILE_FORMAT_VERSION, which is what FlmDbUpgrade + // upgrades to. This is OK because the current version + // should support all packets in the RFL for versions that + // are less than it. Otherwise, the RFL chain would have + // been broken by the original upgrade and it would not + // have logged an upgrade packet. + + if (RC_BAD( rc = FlmDbUpgrade( hDb, opInfo.uiNewVersion, + NULL, NULL))) + { + goto Exit; + } + } + + goto Finish_Transaction; + } + + case RFL_WRAP_KEY_PACKET: + case RFL_ENABLE_ENCRYPTION_PACKET: + { + goto Finish_Transaction; + } + + case RFL_CONFIG_SIZE_EVENT_PACKET: // here + { + if (m_pRestore) + { + if (RC_BAD( rc = m_pRestore->status( RESTORE_CONFIG_SIZE_EVENT, + opInfo.uiTransId, + (void *) opInfo.uiSizeThreshold, + (void *) opInfo.uiTimeInterval, + (void *) opInfo.uiSizeInterval, &eRestoreAction))) + { + goto Exit; + } + + if (eRestoreAction == RESTORE_ACTION_STOP) + { + + // Need to set m_uiCurrTransID to 0 since it was set by + // getPacket(). We are not going to start a transaction + // because of the user's request to exit. + + m_uiCurrTransID = 0; + bLastTransEndedAtFileEOF = FALSE; + goto Finish_Recovery; + } + } + + if (RC_BAD( rc = flmSetRflSizeThreshold( + hDb, opInfo.uiSizeThreshold, opInfo.uiTimeInterval, + opInfo.uiSizeInterval))) + { + goto Exit; + } + + goto Finish_Transaction; + } + + default: + { + + // Should not be getting other packet types at this point. ; + // If we don't know the current file size, and we are doing a + // restore, it is OK to end on a bad packet - we will simply + // abort the current transaction, if any. + + if (m_pRestore && !m_uiFileEOF) + { + rc = FERR_OK; + goto Finish_Recovery; + } + else + { + rc = RC_SET( FERR_BAD_RFL_PACKET); + } + + goto Exit; + } + } + } + +Finish_Recovery: + + if (bTransActive) + { + FlmDbTransAbort( hDb); + bTransActive = FALSE; + } + + if (m_pRestore) + { + FLMUINT uiNextRflFileNum = m_pCurrentBuf->uiCurrFileNum + 1; + + // At the end of the restore operation, we need to set things up so + // that the next transaction will begin a new RFL file. If we ended + // the restore in the middle of an RFL file, we need to set it up so + // that the new RFL file will have a new serial number. If we ended + // at the end of an RFL file, we can set it up so that the new RFL + // file will have the next serial number. ; + // Set up the next RFL file number and offset. + + UD2FBA( uiNextRflFileNum, + &m_pFile->ucLastCommittedLogHdr[LOG_RFL_FILE_NUM]); + + // Set a zero into the offset, this is a special case which tells + // us that we should create the file no matter what - even if it + // already exists - it should be overwritten. + + UD2FBA( 0, &m_pFile->ucLastCommittedLogHdr[LOG_RFL_LAST_TRANS_OFFSET]); + + if (bLastTransEndedAtFileEOF) + { + + // Move the next serial number of the last RFL file processed + // into into the current RFL serial number so that the log header + // will be correct + + f_memcpy( &m_pFile->ucLastCommittedLogHdr[ + LOG_LAST_TRANS_RFL_SERIAL_NUM], m_ucNextSerialNum, + F_SERIAL_NUM_SIZE); + } + else + { + + // Must create a new serial number so that when the new RFL file + // is created, it will have that next serial number. + + if (RC_BAD( rc = f_createSerialNumber( &m_pFile->ucLastCommittedLogHdr[ + LOG_LAST_TRANS_RFL_SERIAL_NUM]))) + { + goto Exit; + } + } + + // Save the last logged commit transaction ID. + + if (m_pFile->FileHdr.uiVersionNum >= FLM_FILE_FORMAT_VER_4_31 && + m_uiLastLoggedCommitTransID) + { + UD2FBA( m_uiLastLoggedCommitTransID, + &m_pFile->ucLastCommittedLogHdr[LOG_LAST_RFL_COMMIT_ID]); + } + + // No matter what, we must generate a new next serial number. This + // is what will be written to the new RFL file's header when it is + // created. + + if (RC_BAD( rc = f_createSerialNumber( + &m_pFile->ucLastCommittedLogHdr[LOG_RFL_NEXT_SERIAL_NUM]))) + { + goto Exit; + } + } + + if (!bHadOperations) + { + + // No transactions were recovered, but still need to setup a few + // things. + + m_pFile->uiFirstLogCPBlkAddress = 0; + m_pFile->uiLastCheckpointTime = (FLMUINT) FLM_GET_TIMER(); + + // Save the state of the log header into the ucCheckpointLogHdr + // buffer. + + f_memcpy( m_pFile->ucCheckpointLogHdr, m_pFile->ucLastCommittedLogHdr, + LOG_HEADER_SIZE); + } + + // Force a checkpoint to force the log files to be truncated and + // everything to be reset. This is done because during recovery the + // checkpoints that are executed do NOT truncate the RFL file - because + // it is using the log file to recover! + + closeFile(); + m_pRestore = NULL; + m_bLoggingOff = FALSE; + pDb->uiFlags &= ~FDB_REPLAYING_RFL; + + if (RC_BAD( rc = FlmDbCheckpoint( hDb, 0))) + { + goto Exit; + } + +Exit: + + if (pRecord) + { + pRecord->Release(); + } + + if (pTmpRecord) + { + pTmpRecord->Release(); + } + + if (bTransActive) + { + FlmDbTransAbort( hDb); + } + + pDb->bFldStateUpdOk = FALSE; + pDb->uiFlags &= ~FDB_REPLAYING_RFL; + + return (rc); +} + +/******************************************************************** +Desc: +********************************************************************/ +RCODE flmRflCalcDiskUsage( + const char * pszRflDir, + const char * pszRflPrefix, + FLMUINT uiDbVersionNum, + FLMUINT64 * pui64DiskUsage) +{ + RCODE rc = FERR_OK; + F_DirHdl * pDirHdl = NULL; + FLMUINT uiFileNumber; + FLMUINT64 ui64DiskUsage; + + ui64DiskUsage = 0; + + if( RC_BAD( rc = gv_FlmSysData.pFileSystem->OpenDir( pszRflDir, + (char *) "*", &pDirHdl))) + { + if( rc == FERR_IO_PATH_NOT_FOUND) + { + rc = FERR_OK; + } + + goto Exit; + } + + for( ;;) + { + if( RC_BAD( rc = pDirHdl->Next())) + { + if( rc != FERR_IO_NO_MORE_FILES && rc != FERR_IO_PATH_NOT_FOUND) + { + goto Exit; + } + + rc = FERR_OK; + break; + } + + // If the current file is an RFL file, increment the disk usage + + if( rflGetFileNum( uiDbVersionNum, pszRflPrefix, + pDirHdl->CurrentItemName(), &uiFileNumber)) + { + ui64DiskUsage += pDirHdl->CurrentItemSize(); + } + } + +Exit: + + *pui64DiskUsage = ui64DiskUsage; + + if( pDirHdl) + { + pDirHdl->Release(); + } + + return( rc); +} + +/******************************************************************** +Desc: Returns the name of an RFL file given its number +********************************************************************/ +FLMEXP RCODE FLMAPI FlmDbGetRflFileName( + HFDB hDb, + FLMUINT uiFileNum, + char * pszFileName) +{ + ((FDB *) hDb)->pFile->pRfl->getBaseRflFileName( uiFileNum, pszFileName); + return (FERR_OK); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +F_RflUnknownStream::F_RflUnknownStream() +{ + m_pRfl = NULL; + m_bStartedWriting = FALSE; + m_bInputStream = FALSE; + m_bSetupCalled = FALSE; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +F_RflUnknownStream::~F_RflUnknownStream() +{ + if (m_bSetupCalled) + { + (void)close(); + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_RflUnknownStream::setup( + F_Rfl * pRfl, + FLMBOOL bInputStream) +{ + RCODE rc = FERR_OK; + + flmAssert( !m_bSetupCalled); + + if (!pRfl) + { + flmAssert( 0); + rc = RC_SET( FERR_INVALID_PARM); + goto Exit; + } + m_pRfl = pRfl; + m_bInputStream = bInputStream; + m_bSetupCalled = TRUE; + m_bStartedWriting = FALSE; + +Exit: + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_RflUnknownStream::close( void) +{ + RCODE rc = FERR_OK; + + flmAssert( m_bSetupCalled); + + // There is nothing to do for input streams, because the RFL + // code handles skipping over any unknown data that may not have + // been read yet. + // For output streams, we need to call the endLoggingUnknown + // routine so that the last packet gets written out. + + if (!m_bInputStream) + { + if (m_bStartedWriting) + { + m_bStartedWriting = FALSE; + if (RC_BAD( rc = m_pRfl->endLoggingUnknown())) + { + goto Exit; + } + } + } +Exit: + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_RflUnknownStream::read( + FLMUINT uiLength, + void * pvBuffer, + FLMUINT * puiBytesRead) +{ + RCODE rc = FERR_OK; + + flmAssert( m_bSetupCalled); + + if (!m_bInputStream) + { + + // Cannot read from an output stream. + + flmAssert( 0); + rc = RC_SET( FERR_ILLEGAL_OP); + goto Exit; + } + + if (RC_BAD( rc = m_pRfl->readUnknown( uiLength, (FLMBYTE *)pvBuffer, + puiBytesRead))) + { + goto Exit; + } + +Exit: + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_RflUnknownStream::write( + FLMUINT uiLength, + void * pvBuffer) +{ + RCODE rc = FERR_OK; + + flmAssert( m_bSetupCalled); + flmAssert( m_pRfl); + + if (m_bInputStream) + { + + // Cannot write to an input stream. + + flmAssert( 0); + rc = RC_SET( FERR_ILLEGAL_OP); + goto Exit; + } + + // Need to start logging on the first write. + + if (!m_bStartedWriting) + { + if (RC_BAD( rc = m_pRfl->startLoggingUnknown())) + { + goto Exit; + } + m_bStartedWriting = TRUE; + } + + // Log the data. + + if (RC_BAD( rc = m_pRfl->logUnknown( (FLMBYTE *)pvBuffer, uiLength))) + { + goto Exit; + } +Exit: + return( rc); +} + +/**************************************************************************** +Desc: Returns an unknown stream object - suitable for writing unknown + streams into the roll-forward log. +****************************************************************************/ +FLMEXP RCODE FLMAPI FlmDbGetUnknownStreamObj( + HFDB hDb, + F_UnknownStream ** ppUnknownStream) +{ + RCODE rc = FERR_OK; + FDB * pDb = (FDB *)hDb; + F_RflUnknownStream * pUnkStream = NULL; + + flmAssert( pDb); + flmAssert( ppUnknownStream); + + // See if the database is being forced to close + + if( RC_BAD( rc = flmCheckDatabaseState( pDb))) + { + goto Exit; + } + + // This is only valid on 4.3 and greater. + + if (pDb->pFile->FileHdr.uiVersionNum < FLM_FILE_FORMAT_VER_4_3) + { + goto Exit; // Will return FERR_OK and a NULL pointer. + } + + // Must be in an update transaction. + + if (pDb->uiTransType == FLM_NO_TRANS) + { + rc = RC_SET( FERR_NO_TRANS_ACTIVE); + goto Exit; + } + if (pDb->uiTransType != FLM_UPDATE_TRANS) + { + rc = RC_SET( FERR_ILLEGAL_TRANS_OP); + goto Exit; + } + + // Allocate the stream object we want. + + if ((pUnkStream = f_new F_RflUnknownStream) == NULL) + { + rc = RC_SET( FERR_MEM); + goto Exit; + } + + // Setup the unknown stream object. + + if (RC_BAD( rc = pUnkStream->setup( pDb->pFile->pRfl, FALSE))) + { + goto Exit; + } + +Exit: + + if (RC_BAD( rc) && pUnkStream) + { + pUnkStream->Release(); + pUnkStream = NULL; + } + *ppUnknownStream = (F_UnknownStream *)pUnkStream; + return( rc); +}