Files
mars-flaim/flaim/src/flbackup.cpp
ahodgkinson 7de8b6be39 Ported FLAIM to FTK.
git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@509 0109f412-320b-0410-ab79-c3e0c5ffbbe6
2006-06-05 22:59:36 +00:00

3157 lines
69 KiB
C++

//-------------------------------------------------------------------------
// Desc: Backup and restore routines.
// Tabs: 3
//
// Copyright (c) 2000-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: flbackup.cpp 12334 2006-01-23 12:45:35 -0700 (Mon, 23 Jan 2006) dsanders $
//-------------------------------------------------------------------------
#include "flaimsys.h"
typedef struct
{
char szPath[ F_PATH_MAX_SIZE];
IF_MultiFileHdl * pMultiFileHdl;
FLMUINT64 ui64Offset;
void * pvAppData;
RCODE rc;
} BACKER_HOOK_STATE;
#define FLM_BACKER_SIGNATURE_OFFSET 0
#define FLM_BACKER_SIGNATURE "!DB_BACKUP_FILE!"
#define FLM_BACKER_SIGNATURE_SIZE 16
#define FLM_BACKER_VERSION_OFFSET 16
#define FLM_BACKER_VERSION_1_0_1 101
#define FLM_BACKER_VERSION FLM_BACKER_VERSION_1_0_1
#define FLM_BACKER_DB_BLOCK_SIZE_OFFSET 20
#define FLM_BACKER_BFMAX_OFFSET 24
#define FLM_BACKER_MTU_OFFSET 28
#define FLM_BACKER_TIME_OFFSET 32
#define FLM_BACKER_DB_NAME_OFFSET 36
#define FLM_BACKER_BACKUP_TYPE_OFFSET 40
#define FLM_BACKER_NEXT_INC_SERIAL_NUM 44
#define FLM_BACKER_DB_VERSION 60
// The backer MTU size must be a multiple of the largest
// supported block size. Additionally, it must be at least
// 2 * FLM_BACKER_MAX_DB_BLOCK_SIZE. DO NOT CHANGE THE MTU
// SIZE UNLESS YOU ALSO BUMP THE BACKUP VERSION.
#define FLM_BACKER_MTU_SIZE ((FLMUINT) 1024 * 512)
#define FLM_BACKER_MAX_FILE_SIZE ((FLMUINT) 1024 * 1024 * 1024 * 2) // 2 Gigabytes
#define FLM_BACKER_MIN_DB_BLOCK_SIZE ((FLMUINT) 2 * 1024)
#define FLM_BACKER_MAX_DB_BLOCK_SIZE ((FLMUINT) 16 * 1024)
// Backup block header
#define FLM_BACKER_BLK_HDR_SIZE ((FLMUINT) 8)
#define FLM_BACKER_BLK_ADDR_OFFSET 0
#define FLM_BACKER_BLK_SIZE_OFFSET 4
FSTATIC RCODE flmDefaultBackerWriteHook(
void * pvBuffer,
FLMUINT uiBytesToWrite,
void * pvCallbackData);
FSTATIC RCODE flmRestoreFile(
F_Restore * pRestoreObj,
const char * pszDbPath,
const char * pszDataDir,
const char * pszPassword,
F_SuperFileHdl ** ppSFile,
FLMBOOL bIncremental,
FLMUINT * puiDbVersion,
FLMUINT * puiNextIncSeqNum,
FLMBOOL * pbRflPreserved,
eRestoreActionType * peRestoreAction,
FLMBOOL * pbOKToRetry,
FLMBYTE ** ppucKeyToSave,
FLMBYTE * pucKeyToUse,
FLMUINT * puiKeyLen);
/*******************************************************************************
Desc:
*******************************************************************************/
class F_BackerStream : public F_Object
{
public:
F_BackerStream( void);
~F_BackerStream( void);
RCODE setup(
FLMUINT uiMTUSize,
F_Restore * pRestoreObj);
RCODE setup(
FLMUINT uiMTUSize,
BACKER_WRITE_HOOK fnWrite,
void * pvCallbackData);
RCODE startThreads( void);
void shutdownThreads( void);
RCODE read(
FLMUINT uiLength,
FLMBYTE * pucData,
FLMUINT * puiBytesRead = NULL);
RCODE write(
FLMUINT uiLength,
FLMBYTE * pucData,
FLMUINT * puiBytesWritten = NULL);
RCODE flush( void);
FINLINE FLMUINT64 getByteCount( void)
{
// Returns the total number of bytes read or written.
return( m_ui64ByteCount);
}
FINLINE FLMUINT getMTUSize( void)
{
return( m_uiMTUSize);
}
private:
// Methods
RCODE signalThread( void);
RCODE _setup( void);
static RCODE readThread(
IF_Thread * pThread);
static RCODE writeThread(
IF_Thread * pThread);
FLMBOOL m_bSetup;
FLMBOOL m_bFirstRead;
FLMUINT m_uiBufOffset;
FLMUINT64 m_ui64ByteCount;
F_Restore * m_pRestoreObj;
F_SEM m_hDataSem;
F_SEM m_hIdleSem;
IF_Thread * m_pThread;
RCODE m_rc;
FLMBYTE * m_pucInBuf;
FLMUINT * m_puiInOffset;
FLMBYTE * m_pucOutBuf;
FLMUINT * m_puiOutOffset;
FLMBYTE * m_pucBufs[ 2];
FLMUINT m_uiOffsets[ 2];
FLMUINT m_uiMTUSize;
FLMUINT m_uiPendingIO;
BACKER_WRITE_HOOK m_fnWrite;
void * m_pvCallbackData;
};
/*******************************************************************************
Desc: Prepares FLAIM to backup a database.
Notes: Only one backup of a particular database can be active at any time
*******************************************************************************/
FLMEXP RCODE FLMAPI FlmDbBackupBegin(
HFDB hDb,
FBackupType eBackupType,
FLMBOOL bHotBackup,
HFBACKUP * phBackup
)
{
FDB * pDb = (FDB *)hDb;
FBak * pFBak = NULL;
FLMBOOL bBackupFlagSet = FALSE;
FLMUINT uiLastCPFileNum;
FLMUINT uiLastTransFileNum;
FLMUINT uiDbVersion;
FLMUINT uiTmp;
FLMBYTE * pLogHdr;
RCODE rc = FERR_OK;
FLMUINT uiTransType = bHotBackup ? FLM_READ_TRANS : FLM_UPDATE_TRANS;
// Initialize the handle
*phBackup = HFBACKUP_NULL;
// Make sure we are not being called inside a transaction
if( RC_BAD( rc = FlmDbGetTransType( hDb, &uiTmp)))
{
goto Exit;
}
if( uiTmp != FLM_NO_TRANS)
{
rc = RC_SET( FERR_TRANS_ACTIVE);
goto Exit;
}
// Make sure a valid backup type has been specified
if( RC_BAD( rc = FlmDbGetConfig( hDb,
FDB_GET_VERSION, (FLMUINT *)&uiDbVersion)))
{
goto Exit;
}
if( uiDbVersion < FLM_FILE_FORMAT_VER_4_3 &&
eBackupType != FLM_FULL_BACKUP)
{
rc = RC_SET( FERR_NOT_IMPLEMENTED);
goto Exit;
}
// See if a backup is currently running against the database. If so,
// return an error. Otherwise, set the backup flag on the FFILE.
if( !IsInCSMode( hDb))
{
f_mutexLock( gv_FlmSysData.hShareMutex);
if( pDb->pFile->bBackupActive)
{
f_mutexUnlock( gv_FlmSysData.hShareMutex);
rc = RC_SET( FERR_BACKUP_ACTIVE);
goto Exit;
}
else
{
bBackupFlagSet = TRUE;
pDb->pFile->bBackupActive = TRUE;
}
f_mutexUnlock( gv_FlmSysData.hShareMutex);
}
else
{
if( RC_BAD( rc = fcsSetBackupActiveFlag( hDb, TRUE)))
{
goto Exit;
}
bBackupFlagSet = TRUE;
}
// Allocate the backup handle
if( RC_BAD( rc = f_alloc( sizeof( FBak), &pFBak)))
{
goto Exit;
}
f_memset( pFBak, 0, sizeof( FBak));
pFBak->hDb = hDb;
pFBak->uiDbVersion = uiDbVersion;
// Set the C/S mode flag
pFBak->bCSMode = IsInCSMode( hDb);
// Start a transaction
if( RC_BAD( rc = FlmDbTransBegin( hDb,
uiTransType | FLM_DONT_KILL_TRANS | FLM_DONT_POISON_CACHE,
FLM_NO_TIMEOUT, pFBak->ucDbHeader)))
{
goto Exit;
}
pFBak->bTransStarted = TRUE;
pFBak->uiTransType = uiTransType;
pLogHdr = &pFBak->ucDbHeader[ DB_LOG_HEADER_START];
// Don't allow an incremental backup to be performed
// if a full backup has not yet been done.
if( eBackupType == FLM_INCREMENTAL_BACKUP &&
FB2UD( &pLogHdr[ LOG_LAST_BACKUP_TRANS_ID]) == 0)
{
rc = RC_SET( FERR_ILLEGAL_OP);
goto Exit;
}
pFBak->eBackupType = eBackupType;
// Set the next incremental backup serial number. This is
// done regardless of the backup type to prevent the wrong
// set of incremental backup files from being applied
// to a database.
if( uiDbVersion >= FLM_FILE_FORMAT_VER_4_3)
{
if( !pFBak->bCSMode)
{
if( RC_BAD( rc = f_createSerialNumber(
pFBak->ucNextIncSerialNum)))
{
goto Exit;
}
}
else
{
fdbInitCS( pDb);
rc = fcsCreateSerialNumber(
pDb->pCSContext, pFBak->ucNextIncSerialNum);
fdbExit( pDb);
if( RC_BAD( rc))
{
goto Exit;
}
}
}
// Get the incremental sequence number from the log header
pFBak->uiIncSeqNum = FB2UD( &pLogHdr[ LOG_INC_BACKUP_SEQ_NUM]);
// Get version 4.3+ values from the header
if( uiDbVersion >= FLM_FILE_FORMAT_VER_4_3)
{
// Determine the transaction ID of the last backup
pFBak->uiLastBackupTransId =
FB2UD( &pLogHdr[ LOG_LAST_BACKUP_TRANS_ID]);
// Get the block change count
pFBak->uiBlkChgSinceLastBackup =
FB2UD( &pLogHdr[ LOG_BLK_CHG_SINCE_BACKUP]);
}
// Get the current transaction ID
if( RC_BAD( rc = FlmDbGetConfig( hDb, FDB_GET_TRANS_ID,
(void *)&pFBak->uiTransId)))
{
goto Exit;
}
// Get the logical end of file
pFBak->uiLogicalEOF = FB2UD( &pLogHdr[ LOG_LOGICAL_EOF]);
// Get the first required RFL file needed by the restore.
uiLastCPFileNum = FB2UD( &pLogHdr[ LOG_RFL_LAST_CP_FILE_NUM]);
uiLastTransFileNum = FB2UD( &pLogHdr[ LOG_RFL_FILE_NUM]);
flmAssert( uiLastCPFileNum <= uiLastTransFileNum);
pFBak->uiFirstReqRfl = uiLastCPFileNum < uiLastTransFileNum
? uiLastCPFileNum
: uiLastTransFileNum;
flmAssert( pFBak->uiFirstReqRfl);
// Get the database block size
if( RC_BAD( rc = FlmDbGetConfig( hDb, FDB_GET_BLKSIZ,
&pFBak->uiBlockSize)))
{
goto Exit;
}
// Get the database path
if( RC_BAD( rc = FlmDbGetConfig( hDb,
FDB_GET_PATH, pFBak->ucDbPath)))
{
goto Exit;
}
*phBackup = pFBak;
Exit:
if( RC_BAD( rc))
{
if( pFBak)
{
if( pFBak->bTransStarted)
{
(void)FlmDbTransAbort( hDb);
}
f_free( &pFBak);
}
if( bBackupFlagSet)
{
if( !IsInCSMode( hDb))
{
f_mutexLock( gv_FlmSysData.hShareMutex);
pDb->pFile->bBackupActive = FALSE;
f_mutexUnlock( gv_FlmSysData.hShareMutex);
}
else
{
(void)fcsSetBackupActiveFlag( hDb, FALSE);
}
}
}
return( rc);
}
/****************************************************************************
Desc : Returns information about a backup
****************************************************************************/
FLMEXP RCODE FLMAPI FlmBackupGetConfig(
HFBACKUP hBackup,
eBackupGetConfigType eConfigType,
void * pvValue1,
void * // pvValue2
)
{
RCODE rc = FERR_OK;
FBak * pFBak = (FBak *)hBackup;
switch( eConfigType)
{
case FBAK_GET_BACKUP_TRANS_ID:
{
*((FLMUINT *)pvValue1) = pFBak->uiTransId;
break;
}
case FBAK_GET_LAST_BACKUP_TRANS_ID:
{
*((FLMUINT *)pvValue1) = pFBak->uiLastBackupTransId;
break;
}
default:
{
rc = RC_SET( FERR_NOT_IMPLEMENTED);
goto Exit;
}
}
Exit:
return( rc);
}
/****************************************************************************
Desc : Streams the contents of a database to the write hook supplied by
the application.
Notes: This routine attempts to create a backup of a database without
excluding any readers or updaters. However, if the backup runs
too long in an environment where extensive updates are happening,
an old view error could be returned.
****************************************************************************/
FLMEXP RCODE FLMAPI FlmDbBackup(
HFBACKUP hBackup,
const char * pszBackupPath,
const char * pszPassword,
BACKER_WRITE_HOOK fnWrite,
STATUS_HOOK fnStatus,
void * pvAppData,
FLMUINT * puiIncSeqNum)
{
FDB * pDb = NULL;
FLMBOOL bDbInitialized = FALSE;
FLMBOOL bFullBackup = TRUE;
FLMINT iFileNum;
FLMUINT uiBlkAddr;
FLMUINT uiTime;
SCACHE * pSCache = NULL;
BACKER_HOOK_STATE hookState;
FLMBYTE * pLogHdr;
DB_BACKUP_INFO backupInfo;
FLMUINT uiBlockFileOffset;
FLMUINT uiActualBlkSize;
FLMUINT uiCount;
FLMUINT uiBlockCount;
FLMUINT uiBlockCountLastCB = 0;
FLMUINT uiBytesToPad;
void * pvCallbackData;
FBak * pFBak = (FBak *)hBackup;
FLMUINT uiBlockSize = pFBak->uiBlockSize;
F_BackerStream * pBackerStream = NULL;
FLMBYTE * pucBlkBuf = NULL;
FLMUINT uiBlkBufOffset;
FLMUINT uiBlkBufSize;
FLMUINT uiMaxCSBlocks;
FLMUINT uiCPTransOffset;
FLMUINT uiMaxFileSize;
RCODE rc = FERR_OK;
F_CCS * pDbKey;
#ifndef FLM_USE_NICI
F_UNREFERENCED_PARM( pszPassword);
#endif
pDb = (FDB *)(pFBak->hDb);
if( puiIncSeqNum)
{
*puiIncSeqNum = 0;
}
f_memset( &hookState, 0, sizeof( BACKER_HOOK_STATE));
// Make sure a backup attempt has not been made with this
// backup handle.
if( pFBak->bCompletedBackup)
{
rc = RC_SET( FERR_FAILURE);
goto Exit;
}
if( RC_BAD( pFBak->backupRc))
{
rc = pFBak->backupRc;
goto Exit;
}
// Look at the backup type
if( pFBak->eBackupType == FLM_INCREMENTAL_BACKUP)
{
if( puiIncSeqNum)
{
*puiIncSeqNum = pFBak->uiIncSeqNum;
}
bFullBackup = FALSE;
}
// Set up the callback
if( !fnWrite)
{
fnWrite = flmDefaultBackerWriteHook;
}
if( fnWrite == flmDefaultBackerWriteHook)
{
if( !pszBackupPath)
{
rc = RC_SET( FERR_INVALID_PARM);
goto Exit;
}
f_strcpy( hookState.szPath, pszBackupPath);
hookState.pvAppData = pvAppData;
pvCallbackData = &hookState;
}
else
{
pvCallbackData = pvAppData;
}
// Allocate and initialize the backer stream object
if( (pBackerStream = f_new F_BackerStream) == NULL)
{
rc = RC_SET( FERR_MEM);
goto Exit;
}
if( RC_BAD( rc = pBackerStream->setup( FLM_BACKER_MTU_SIZE,
fnWrite, pvCallbackData)))
{
goto Exit;
}
// Allocate a temporary buffer
uiBlkBufSize = FLM_BACKER_MTU_SIZE;
uiMaxCSBlocks = uiBlkBufSize / uiBlockSize;
if( RC_BAD( rc = f_alloc( uiBlkBufSize, &pucBlkBuf)))
{
goto Exit;
}
// Setup the status callback info
f_memset( &backupInfo, 0, sizeof( DB_BACKUP_INFO));
// Setup the backup file header
uiBlkBufOffset = 0;
f_memset( pucBlkBuf, 0, uiBlockSize);
f_memcpy( &pucBlkBuf[ FLM_BACKER_SIGNATURE_OFFSET],
FLM_BACKER_SIGNATURE, FLM_BACKER_SIGNATURE_SIZE);
UD2FBA( FLM_BACKER_VERSION,
&pucBlkBuf[ FLM_BACKER_VERSION_OFFSET]);
UD2FBA( uiBlockSize,
&pucBlkBuf[ FLM_BACKER_DB_BLOCK_SIZE_OFFSET]);
uiMaxFileSize = flmGetMaxFileSize( pFBak->uiDbVersion,
&pFBak->ucDbHeader [DB_LOG_HEADER_START]);
UD2FBA( uiMaxFileSize,
&pucBlkBuf[ FLM_BACKER_BFMAX_OFFSET]);
UD2FBA( FLM_BACKER_MTU_SIZE,
&pucBlkBuf[ FLM_BACKER_MTU_OFFSET]);
f_timeGetSeconds( &uiTime);
UD2FBA( uiTime,
&pucBlkBuf[ FLM_BACKER_TIME_OFFSET]);
uiCount = f_strlen( (const char *)pFBak->ucDbPath);
if( uiCount <= 3)
{
pucBlkBuf[ FLM_BACKER_DB_NAME_OFFSET] =
pFBak->ucDbPath[ uiCount - 6];
pucBlkBuf[ FLM_BACKER_DB_NAME_OFFSET + 1] =
pFBak->ucDbPath[ uiCount - 5];
pucBlkBuf[ FLM_BACKER_DB_NAME_OFFSET + 2] =
pFBak->ucDbPath[ uiCount - 4];
pucBlkBuf[ FLM_BACKER_DB_NAME_OFFSET + 3] = '\0';
}
UD2FBA( (FLMUINT32)pFBak->eBackupType,
&pucBlkBuf[ FLM_BACKER_BACKUP_TYPE_OFFSET]);
// Set the next incremental serial number in the backup's
// header so that it can be put into the database's log header
// after the backup has been restored.
f_memcpy( &pucBlkBuf[ FLM_BACKER_NEXT_INC_SERIAL_NUM],
pFBak->ucNextIncSerialNum, F_SERIAL_NUM_SIZE);
// Set the database version number
UD2FBA( (FLMUINT32)pFBak->uiDbVersion,
&pucBlkBuf[ FLM_BACKER_DB_VERSION]);
uiBlkBufOffset += uiBlockSize;
// Copy the database header into the backup's buffer
f_memset( &pucBlkBuf[ uiBlkBufOffset], 0, uiBlockSize);
f_memcpy( &pucBlkBuf[ uiBlkBufOffset],
pFBak->ucDbHeader, F_TRANS_HEADER_SIZE);
pLogHdr = &pucBlkBuf[ uiBlkBufOffset + DB_LOG_HEADER_START];
uiBlkBufOffset += uiBlockSize;
// Fix up the log header
if( !pLogHdr[ LOG_KEEP_RFL_FILES] || pFBak->uiDbVersion < FLM_FILE_FORMAT_VER_4_3)
{
pLogHdr[ LOG_KEEP_RFL_FILES] = 0;
// Put zero in as the last transaction offset so that the current
// RFL file will be created if it does not exist after the database
// is restored. This has basically the same effect as setting the
// offset to 512 if the RFL file has already been created.
UD2FBA( (FLMUINT32)0, &pLogHdr[ LOG_RFL_LAST_TRANS_OFFSET]);
uiCPTransOffset = 512;
// Create new serial numbers for the RFL. We don't want anyone
// to be able to branch into a "no-keep" RFL sequence.
if( pFBak->uiDbVersion >= FLM_FILE_FORMAT_VER_4_3)
{
if (RC_BAD( rc = f_createSerialNumber(
&pLogHdr [LOG_LAST_TRANS_RFL_SERIAL_NUM])))
{
goto Exit;
}
if (RC_BAD( rc = f_createSerialNumber(
&pLogHdr [LOG_RFL_NEXT_SERIAL_NUM])))
{
goto Exit;
}
}
}
else
{
uiCPTransOffset = FB2UD( &pLogHdr[ LOG_RFL_LAST_TRANS_OFFSET]);
if( !uiCPTransOffset)
{
uiCPTransOffset = 512;
}
}
// Shroud the database key (stored in the log header) in the password
// so we can restore this backup to a different server
if ( pDb->pFile->FileHdr.uiVersionNum >= FLM_FILE_FORMAT_VER_4_60 &&
pszPassword && *pszPassword &&
FB2UW( &pLogHdr[ LOG_DATABASE_KEY_LEN]) > 0)
{
FLMBYTE * pucTmpBuf = NULL;
FLMUINT32 ui32KeyLen = 0;
pDbKey = pDb->pFile->pDbWrappingKey;
if( RC_BAD( rc = pDbKey->getKeyToStore( &pucTmpBuf, &ui32KeyLen,
pszPassword, NULL, FALSE)))
{
goto Exit;
}
// Verify that the field in the log header is long enough to
// hold the key.
if( ui32KeyLen > FLM_MAX_DB_ENC_KEY_LEN)
{
rc = RC_SET_AND_ASSERT( FERR_BAD_ENC_KEY);
goto Exit;
}
// Verify that the field in the log header is long enough to
// hold the key.
if( ui32KeyLen > FLM_MAX_DB_ENC_KEY_LEN)
{
f_free( &pucTmpBuf);
rc = RC_SET_AND_ASSERT( FERR_BAD_ENC_KEY);
goto Exit;
}
UW2FBA( ui32KeyLen, &pLogHdr[ LOG_DATABASE_KEY_LEN]);
f_memcpy( &pLogHdr[ LOG_DATABASE_KEY], pucTmpBuf, ui32KeyLen);
f_free( &pucTmpBuf);
}
// Set the CP offsets to the last trans offsets. This is done
// because the backup could actually read dirty (committed) blocks
// from the cache, resulting in a backup set that contains blocks
// that are more recent than the ones currently on disk.
f_memcpy( &pLogHdr[ LOG_RFL_LAST_CP_FILE_NUM],
&pLogHdr[ LOG_RFL_FILE_NUM], 4);
f_memcpy( &pLogHdr[ LOG_LAST_CP_TRANS_ID],
&pLogHdr[ LOG_CURR_TRANS_ID], 4);
UD2FBA( uiCPTransOffset, &pLogHdr[ LOG_RFL_LAST_CP_OFFSET]);
UD2FBA( ((FLMUINT32) uiBlockSize), &pLogHdr[ LOG_ROLLBACK_EOF]);
UD2FBA( (FLMUINT32)0, &pLogHdr[ LOG_PL_FIRST_CP_BLOCK_ADDR]);
// Compute the log header checksum
UW2FBA( (FLMUINT16)lgHdrCheckSum( pLogHdr, FALSE),
&pLogHdr[ LOG_HDR_CHECKSUM]);
// Output the header
if( RC_BAD( rc = pBackerStream->write( uiBlkBufOffset, pucBlkBuf)))
{
goto Exit;
}
// There is no way to quickly compute the actual number of bytes
// that will be written to the backup. This is due, in part, to the
// fact that the backup compresses unused space out of blocks before
// storing them.
backupInfo.ui64BytesToDo = FSGetSizeInBytes( uiMaxFileSize,
pFBak->uiLogicalEOF);
// Initialize the FDB
pDb = (FDB *)(pFBak->hDb);
if( !pFBak->bCSMode)
{
FLMBOOL bDummy;
bDbInitialized = TRUE;
if( RC_BAD( rc = fdbInit( pDb, FLM_NO_TRANS,
FDB_TRANS_GOING_OK, 0, &bDummy)))
{
goto Exit;
}
flmAssert( !bDummy);
}
else
{
bDbInitialized = TRUE;
fdbInitCS( pDb);
}
uiBlockFileOffset = 0;
uiBlockCount = 0;
iFileNum = 1;
for( ;;)
{
if( uiBlockFileOffset >= uiMaxFileSize)
{
uiBlockFileOffset = 0;
iFileNum++;
}
uiBlkAddr = FSBlkAddress( iFileNum, uiBlockFileOffset);
if( !FSAddrIsBelow( uiBlkAddr, pFBak->uiLogicalEOF))
{
break;
}
if( !pFBak->bCSMode)
{
// Get the block
if( RC_BAD( rc = ScaGetBlock( pDb, NULL, BHT_FREE,
uiBlkAddr, NULL, &pSCache)))
{
goto Exit;
}
if( bFullBackup ||
FB2UD( &pSCache->pucBlk[ BH_TRANS_ID]) >
pFBak->uiLastBackupTransId)
{
uiBlkBufOffset = 0;
uiActualBlkSize = getEncryptSize( pSCache->pucBlk);
if( uiActualBlkSize < BH_OVHD)
{
flmAssert( 0);
rc = RC_SET( FERR_DATA_ERROR);
goto Exit;
}
// Output the backup header for the block
UD2FBA( (FLMUINT32)uiBlkAddr,
&pucBlkBuf[ FLM_BACKER_BLK_ADDR_OFFSET]);
UD2FBA( (FLMUINT32)uiActualBlkSize,
&pucBlkBuf[ FLM_BACKER_BLK_SIZE_OFFSET]);
uiBlkBufOffset += FLM_BACKER_BLK_HDR_SIZE;
// Copy the block into the block buffer and compute the checksum
f_memcpy( &pucBlkBuf[ uiBlkBufOffset],
pSCache->pucBlk, uiActualBlkSize);
// If this is an encrypted block, we need to make sure it gets encrypted.
if ( pucBlkBuf[ BH_ENCRYPTED + uiBlkBufOffset])
{
if (RC_BAD( rc = ScaEncryptBlock( pSCache->pFile,
&pucBlkBuf[uiBlkBufOffset],
uiActualBlkSize,
pSCache->pFile->
FileHdr.uiBlockSize)))
{
goto Exit;
}
}
BlkCheckSum( &pucBlkBuf[ uiBlkBufOffset],
CHECKSUM_SET, uiBlkAddr, uiBlockSize);
uiBlkBufOffset += uiActualBlkSize;
// Write the block to the backup stream
if( RC_BAD( rc = pBackerStream->write(
uiBlkBufOffset, pucBlkBuf)))
{
goto Exit;
}
uiBlockCount++;
}
ScaReleaseCache( pSCache, FALSE);
pSCache = NULL;
uiBlockFileOffset += uiBlockSize;
}
else
{
rc = RC_SET( FERR_ILLEGAL_OP);
goto Exit;
}
// Call the status callback
if( fnStatus && (uiBlockCount - uiBlockCountLastCB) > 100)
{
backupInfo.ui64BytesDone = FSGetSizeInBytes( uiMaxFileSize,
uiBlkAddr);
if( RC_BAD( rc = fnStatus( FLM_DB_BACKUP_STATUS,
(void *)&backupInfo, NULL, pvAppData)))
{
goto Exit;
}
uiBlockCountLastCB = uiBlockCount;
}
}
// Output the end-of-backup marker
f_memset( pucBlkBuf, 0xFF, sizeof( FLM_BACKER_BLK_HDR_SIZE));
if( RC_BAD( rc = pBackerStream->write( FLM_BACKER_BLK_HDR_SIZE,
pucBlkBuf)))
{
goto Exit;
}
// Pad the backup so that FlmDbRestore will never read more
// data from the input stream than the backup wrote to it.
uiBytesToPad = (FLMUINT32)(pBackerStream->getMTUSize() -
(FLMUINT)(pBackerStream->getByteCount() %
(FLMUINT64)pBackerStream->getMTUSize()));
if( uiBytesToPad < pBackerStream->getMTUSize())
{
f_memset( pucBlkBuf, 0, uiBytesToPad);
if( RC_BAD( rc = pBackerStream->write( uiBytesToPad, pucBlkBuf)))
{
goto Exit;
}
}
// Because of the double buffering, we need to have one empty
// buffer at the end of the file.
f_memset( pucBlkBuf, 0, pBackerStream->getMTUSize());
if( RC_BAD( rc = pBackerStream->write( pBackerStream->getMTUSize(),
pucBlkBuf)))
{
goto Exit;
}
if( RC_BAD( rc = pBackerStream->flush()))
{
goto Exit;
}
Exit:
if( pSCache)
{
ScaReleaseCache( pSCache, FALSE);
}
if( bDbInitialized)
{
fdbExit( pDb);
}
if( pBackerStream)
{
pBackerStream->Release();
}
// Call the status callback now that the background
// thread has terminated.
if( RC_OK( rc) && fnStatus)
{
backupInfo.ui64BytesDone = backupInfo.ui64BytesToDo;
(void)fnStatus( FLM_DB_BACKUP_STATUS,
(void *)&backupInfo, NULL, pvAppData);
}
if( hookState.pMultiFileHdl)
{
hookState.pMultiFileHdl->Release();
}
if( pucBlkBuf)
{
f_free( &pucBlkBuf);
}
if( RC_OK( rc))
{
pFBak->bCompletedBackup = TRUE;
}
pFBak->backupRc = rc;
return( rc);
}
/****************************************************************************
Desc : Ends the backup, updating the log header if needed.
****************************************************************************/
FLMEXP RCODE FLMAPI FlmDbBackupEnd(
HFBACKUP * phBackup)
{
RCODE rc = FERR_OK;
FBak * pFBak = (FBak *)*phBackup;
FDB * pDb = (FDB *)pFBak->hDb;
FLMBOOL bDbInitialized = FALSE;
FLMBOOL bStartedTrans = FALSE;
FLMBYTE * pLogHdr = NULL;
// End the transaction
flmAssert( pFBak->uiTransType != FLM_NO_TRANS);
if( RC_BAD( rc = FlmDbTransAbort( (HFDB)pDb)))
{
goto Exit;
}
pFBak->uiTransType = FLM_NO_TRANS;
pFBak->bTransStarted = FALSE;
// Update log header fields
if( pFBak->bCompletedBackup &&
pFBak->uiDbVersion >= FLM_FILE_FORMAT_VER_4_3)
{
// Start an update transaction.
if( !pFBak->bCSMode)
{
bDbInitialized = TRUE;
if (RC_BAD( rc = fdbInit( pDb, FLM_UPDATE_TRANS,
0, FLM_NO_TIMEOUT | FLM_AUTO_TRANS, &bStartedTrans)))
{
goto Exit;
}
}
else
{
if( RC_BAD( rc = FlmDbTransBegin( (HFDB)pDb,
FLM_UPDATE_TRANS, FLM_NO_TIMEOUT, pFBak->ucDbHeader)))
{
goto Exit;
}
pLogHdr = &pFBak->ucDbHeader[ DB_LOG_HEADER_START];
bStartedTrans = TRUE;
}
// Update the log header fields.
if( !pFBak->bCSMode)
{
UD2FBA( pFBak->uiTransId,
&pDb->pFile->ucUncommittedLogHdr [LOG_LAST_BACKUP_TRANS_ID]);
}
else
{
UD2FBA( (FLMUINT32)pFBak->uiTransId,
&pLogHdr[ LOG_LAST_BACKUP_TRANS_ID]);
}
// Since there may have been transactions during the backup,
// we need to take into account the number of blocks that have
// changed during the backup when updating the LOG_BLK_CHG_SINCE_BACKUP
// statistic.
if( !pFBak->bCSMode)
{
flmDecrUint(
&pDb->pFile->ucUncommittedLogHdr [LOG_BLK_CHG_SINCE_BACKUP],
pFBak->uiBlkChgSinceLastBackup);
}
else
{
flmDecrUint(
&pLogHdr [LOG_BLK_CHG_SINCE_BACKUP],
pFBak->uiBlkChgSinceLastBackup);
}
// Bump the incremental backup sequence number
if( pFBak->eBackupType == FLM_INCREMENTAL_BACKUP)
{
if( !pFBak->bCSMode)
{
flmIncrUint(
&pDb->pFile->ucUncommittedLogHdr [LOG_INC_BACKUP_SEQ_NUM], 1);
}
else
{
flmIncrUint( &pLogHdr [LOG_INC_BACKUP_SEQ_NUM], 1);
}
}
// Always change the incremental backup serial number. This is
// needed so that if the user performs a full backup, runs some
// transactions against the database, performs another full backup,
// and then performs an incremental backup we will know that the
// incremental backup cannot be restored against the first full
// backup.
if( !pFBak->bCSMode)
{
f_memcpy(
&pDb->pFile->ucUncommittedLogHdr [LOG_INC_BACKUP_SERIAL_NUM],
pFBak->ucNextIncSerialNum, F_SERIAL_NUM_SIZE);
}
else
{
f_memcpy( &pLogHdr[ LOG_INC_BACKUP_SERIAL_NUM],
pFBak->ucNextIncSerialNum, F_SERIAL_NUM_SIZE);
}
// Commit the transaction and perform a checkpoint so that the
// modified log header values will be written.
if( !pFBak->bCSMode)
{
if (RC_BAD( rc = flmCommitDbTrans( pDb, 0, TRUE)))
{
goto Exit;
}
bStartedTrans = FALSE;
}
else
{
if( RC_BAD( rc = fcsDbTransCommitEx( (HFDB)pDb, TRUE,
pFBak->ucDbHeader)))
{
goto Exit;
}
bStartedTrans = FALSE;
}
}
Exit:
// Abort the active transaction (if any)
if( bStartedTrans)
{
if( !pFBak->bCSMode)
{
flmAbortDbTrans( pDb);
}
else
{
FlmDbTransAbort( (HFDB)pDb);
}
}
// Release the FDB
if( bDbInitialized)
{
fdbExit( pDb);
}
// Free the backup handle
f_free( &pFBak);
// Clear the handle
*phBackup = HFBACKUP_NULL;
// Unset the backup flag
if( !IsInCSMode( (HFDB)pDb))
{
f_mutexLock( gv_FlmSysData.hShareMutex);
pDb->pFile->bBackupActive = FALSE;
f_mutexUnlock( gv_FlmSysData.hShareMutex);
}
else
{
(void)fcsSetBackupActiveFlag( (HFDB)pDb, FALSE);
}
return( rc);
}
/****************************************************************************
Desc: Restores a database and supporting files.
****************************************************************************/
FLMEXP RCODE FLMAPI FlmDbRestore(
const char * pszDbPath,
const char * pszDataDir,
const char * pszBackupPath,
const char * pszRflDir,
const char * pszPassword,
F_Restore * pRestoreObj)
{
RCODE rc = FERR_OK;
HFDB hDb = HFDB_NULL;
IF_FileHdl * pFileHdl = NULL;
IF_FileHdl * pLockFileHdl = NULL;
F_SuperFileHdl * pSFile = NULL;
char szBasePath[ F_PATH_MAX_SIZE];
char szTmpPath[ F_PATH_MAX_SIZE];
FLMUINT uiDbVersion;
FLMUINT uiNextIncNum;
eRestoreActionType eRestoreAction;
FLMBOOL bRflPreserved;
FLMBOOL bMutexLocked = FALSE;
FFILE * pFile = NULL;
F_FSRestore * pFSRestoreObj = NULL;
FLMBOOL bOKToRetry;
FLMBYTE * pucDbKey = NULL;
FLMUINT uiKeyLen = 0;
char * pszTempPassword = NULL;
// Set up the callback
if( !pRestoreObj)
{
if( (pFSRestoreObj = f_new F_FSRestore) == NULL)
{
rc = RC_SET( FERR_MEM);
goto Exit;
}
if( RC_BAD( rc = pFSRestoreObj->setup( pszDbPath,
pszBackupPath, pszRflDir)))
{
goto Exit;
}
pRestoreObj = pFSRestoreObj;
}
// Get the base path
flmGetDbBasePath( szBasePath, pszDbPath, NULL);
// Force the file to close if it is not used by another thread
(void)FlmConfig( FLM_CLOSE_FILE, (void *)pszDbPath,
(void *)pszDataDir);
// Lock the global mutex
f_mutexLock( gv_FlmSysData.hShareMutex);
bMutexLocked = TRUE;
// Free any unused structures that have been unused for the maximum
// amount of time. May unlock and re-lock the global mutex.
flmCheckNUStructs( 0);
// Look up the file using flmFindFile to see if the file is already open.
// May unlock and re-lock the global mutex..
if( RC_BAD( rc = flmFindFile( pszDbPath, pszDataDir, &pFile)))
{
goto Exit;
}
// If the file is open, we cannot perform a restore
if( pFile)
{
rc = RC_SET( FERR_ACCESS_DENIED);
pFile = NULL;
f_mutexUnlock( gv_FlmSysData.hShareMutex);
bMutexLocked = FALSE;
goto Exit;
}
// Allocate the FFILE. This will prevent other threads from opening the
// database while the restore is being performed.
if( RC_BAD( rc = flmAllocFile( pszDbPath, pszDataDir, NULL, &pFile)))
{
goto Exit;
}
// Remove the FFILE from the NU list -- it was put in the NU list by
// flmAllocFile. We don't want the FFILE to disappear while we are
// doing the restore because it happens to age out of the NU list.
flmAssert( !pFile->uiUseCount);
flmUnlinkFileFromNUList( pFile);
// Unlock the global mutex
f_mutexUnlock( gv_FlmSysData.hShareMutex);
bMutexLocked = FALSE;
// Create a lock file. If this fails, it could indicate
// that the destination database exists and is in use by another
// process.
f_sprintf( szTmpPath, "%s.lck", szBasePath);
if( RC_BAD( rc = flmCreateLckFile( szTmpPath, &pLockFileHdl)))
{
goto Exit;
}
// Create the control file
if( RC_BAD( rc = gv_FlmSysData.pFileSystem->createFile(
pszDbPath, FLM_IO_RDWR, &pFileHdl)))
{
goto Exit;
}
// Open the backup set
if( RC_BAD( rc = pRestoreObj->openBackupSet()))
{
goto Exit;
}
// Make a copy of the password as flmRestoreFile may zero-out the original
// after unshrouding the key.
if ( pszPassword)
{
if ( RC_BAD( rc = f_alloc(
f_strlen( pszPassword) + 1, &pszTempPassword)))
{
goto Exit;
}
f_strcpy( pszTempPassword, pszPassword);
}
// Restore the data in the backup set
if( RC_BAD( rc = flmRestoreFile( pRestoreObj, pszDbPath, pszDataDir,
pszTempPassword, &pSFile, FALSE, &uiDbVersion, &uiNextIncNum,
&bRflPreserved, &eRestoreAction, NULL, &pucDbKey, NULL, &uiKeyLen)))
{
goto Exit;
}
// See if we should continue
if( eRestoreAction == RESTORE_ACTION_STOP)
{
goto Exit;
}
// Close the backup set
if( RC_BAD( rc = pRestoreObj->close()))
{
goto Exit;
}
// Apply any available incremental backups. uiNextIncNum will be 0 if
// the database version does not support incremental backups.
if( uiNextIncNum && uiDbVersion >= FLM_FILE_FORMAT_VER_4_3)
{
FLMUINT uiCurrentIncNum;
for( ;;)
{
uiCurrentIncNum = uiNextIncNum;
if( RC_BAD( rc = pRestoreObj->openIncFile( uiCurrentIncNum)))
{
if( rc == FERR_IO_PATH_NOT_FOUND)
{
rc = FERR_OK;
break;
}
else
{
goto Exit;
}
}
else
{
if( RC_BAD( rc = flmRestoreFile( pRestoreObj, pszDbPath, pszDataDir,
pszTempPassword, &pSFile, TRUE, &uiDbVersion, &uiNextIncNum,
&bRflPreserved, &eRestoreAction, &bOKToRetry, NULL,
pucDbKey, &uiKeyLen)))
{
RCODE tmpRc;
if( !bOKToRetry)
{
// Cannot retry the operation or continue ... the
// database is in an unknown state.
goto Exit;
}
if (RC_BAD( tmpRc = pRestoreObj->status( RESTORE_ERROR,
0, (void *)(FLMUINT)rc, NULL, NULL, &eRestoreAction)))
{
rc = tmpRc;
goto Exit;
}
if( eRestoreAction == RESTORE_ACTION_RETRY ||
eRestoreAction == RESTORE_ACTION_CONTINUE)
{
// Abort the current file (if any)
if( RC_BAD( rc = pRestoreObj->abortFile()))
{
goto Exit;
}
if( eRestoreAction == RESTORE_ACTION_CONTINUE)
{
// Break out and begin processing the RFL
break;
}
// Otherwise, retry opening the incremental file
uiNextIncNum = uiCurrentIncNum;
continue;
}
goto Exit;
}
// See if we should continue
if( eRestoreAction == RESTORE_ACTION_STOP)
{
goto Exit;
}
// Close the current file
if( RC_BAD( rc = pRestoreObj->close()))
{
goto Exit;
}
}
}
}
// Force everything out to disk
if( RC_BAD( rc = pSFile->flush()))
{
goto Exit;
}
pSFile->Release();
pSFile = NULL;
// Don't do anything with the RFL if the preserve flag
// isn't set.
if( !bRflPreserved)
{
if( pFSRestoreObj == pRestoreObj)
{
pFSRestoreObj->Release();
pFSRestoreObj = NULL;
}
pRestoreObj = NULL;
}
// Open the file and apply any available RFL files. The
// lock file handle is passed to the flmOpenFile call so
// that we don't have to give up our lock until the
// restore is complete. Also, we don't want to resume
// any indexing at this point. By not resuming the indexes,
// we can perform a DB diff of two restored databases that
// should be identical without having differences in the
// tracker container due to background indexing.
rc = flmOpenFile( pFile, pszDbPath, pszDataDir,
pszRflDir, FO_DONT_RESUME_BACKGROUND_THREADS,
TRUE, pRestoreObj, pLockFileHdl, NULL, (FDB **)&hDb);
pLockFileHdl = NULL;
pFile = NULL;
if( RC_BAD( rc))
{
goto Exit;
}
// Close the database
(void)FlmDbClose( &hDb);
(void)FlmConfig( FLM_CLOSE_FILE, (void *)pszDbPath,
(void *)pszDataDir);
Exit:
if( pSFile)
{
// Need to release the super file handle before cleaning up the
// FFILE because the super file still has a reference to the
// FFILE's file ID list.
pSFile->Release();
}
if( pFile)
{
// We should only get to this point if rc is bad.
flmAssert( RC_BAD( rc));
if( !bMutexLocked)
{
f_mutexLock( gv_FlmSysData.hShareMutex);
bMutexLocked = TRUE;
}
rc = flmNewFileFinish( pFile, rc);
flmFreeFile( pFile);
f_mutexUnlock( gv_FlmSysData.hShareMutex);
bMutexLocked = FALSE;
}
if( bMutexLocked)
{
f_mutexUnlock( gv_FlmSysData.hShareMutex);
}
if( hDb != HFDB_NULL)
{
FlmDbClose( &hDb);
}
if( pFileHdl)
{
pFileHdl->Release();
}
if( pLockFileHdl)
{
pLockFileHdl->Release();
}
if( pFSRestoreObj)
{
pFSRestoreObj->Release();
}
if (pucDbKey)
{
f_free(&pucDbKey);
}
if ( pszTempPassword)
{
f_free( &pszTempPassword);
}
// If restore failed, remove all database files (excluding RFL files)
if( RC_BAD( rc))
{
(void)FlmDbRemove( pszDbPath, pszDataDir, NULL, FALSE);
}
return( rc);
}
/***************************************************************************
Desc : Restores a full or incremental backup
****************************************************************************/
FSTATIC RCODE flmRestoreFile(
F_Restore * pRestoreObj,
const char * pszDbPath,
const char * pszDataDir,
const char * pszPassword,
F_SuperFileHdl ** ppSFile,
FLMBOOL bIncremental,
FLMUINT * puiDbVersion,
FLMUINT * puiNextIncSeqNum,
FLMBOOL * pbRflPreserved,
eRestoreActionType * peRestoreAction,
FLMBOOL * pbOKToRetry,
FLMBYTE ** ppucKeyToSave,
FLMBYTE * pucKeyToUse,
FLMUINT * puiKeyLen)
{
RCODE rc = FERR_OK;
FLMUINT uiBytesWritten;
FLMUINT uiLogicalEOF;
FLMUINT uiBlkAddr;
FLMUINT uiBlockCount = 0;
FLMUINT uiActualBlkSize;
FLMUINT uiBlockSize;
FLMUINT uiDbVersion;
FLMUINT uiMaxFileSize;
FLMUINT uiBackupMaxFileSize;
FLMUINT uiPriorBlkFile = 0;
FLMUINT uiSectorSize;
FLMBYTE * pLogHdr;
FLMBYTE ucIncSerialNum[ F_SERIAL_NUM_SIZE];
FLMBYTE ucNextIncSerialNum[ F_SERIAL_NUM_SIZE];
FLMUINT uiIncSeqNum;
FLMBYTE * pucBlkBuf = NULL;
FLMBYTE ucLowChecksumByte;
FLMUINT uiBlkBufSize;
FLMUINT uiPriorBlkAddr = 0;
BYTE_PROGRESS byteProgress;
FBackupType eBackupType;
F_BackerStream * pBackerStream = NULL;
F_CCS * pTmpCCS = NULL;
F_SuperFileHdl * pSFile = NULL;
#ifndef FLM_USE_NICI
F_UNREFERENCED_PARM( pszPassword);
#endif
// Initialize the "ok-to-retry" flag
if( pbOKToRetry)
{
*pbOKToRetry = TRUE;
}
// Initialize the progress struct
f_memset( &byteProgress, 0, sizeof( BYTE_PROGRESS));
// Set up the backer stream object
if( (pBackerStream = f_new F_BackerStream) == NULL)
{
rc = RC_SET( FERR_MEM);
goto Exit;
}
if( RC_BAD( rc = pBackerStream->setup( FLM_BACKER_MTU_SIZE, pRestoreObj)))
{
goto Exit;
}
// Get the sector size
if( RC_BAD( rc = gv_FlmSysData.pFileSystem->getSectorSize(
pszDbPath, &uiSectorSize)))
{
goto Exit;
}
// Allocate a temporary buffer. Try to align the buffer on a sector
// boundary to avoid memcpy operatons in the file system.
uiBlkBufSize = FLM_BACKER_MTU_SIZE;
if( uiSectorSize)
{
uiBlkBufSize = (((uiBlkBufSize / uiSectorSize) + 1) * uiSectorSize);
}
if( RC_BAD( rc = f_allocAlignedBuffer( uiBlkBufSize, (void **)&pucBlkBuf)))
{
goto Exit;
}
// Read and verify the backup header
if( RC_BAD( rc = pBackerStream->read( FLM_BACKER_MIN_DB_BLOCK_SIZE,
pucBlkBuf)))
{
goto Exit;
}
if( FB2UD( &pucBlkBuf[ FLM_BACKER_VERSION_OFFSET]) != FLM_BACKER_VERSION)
{
rc = RC_SET_AND_ASSERT( FERR_UNSUPPORTED_VERSION);
goto Exit;
}
if( f_strncmp( (const char *)&pucBlkBuf[ FLM_BACKER_SIGNATURE_OFFSET],
FLM_BACKER_SIGNATURE, FLM_BACKER_SIGNATURE_SIZE) != 0)
{
rc = RC_SET_AND_ASSERT( FERR_UNSUPPORTED_VERSION);
goto Exit;
}
uiBlockSize = (FLMUINT)FB2UW( &pucBlkBuf[ FLM_BACKER_DB_BLOCK_SIZE_OFFSET]);
if( uiBlockSize > FLM_BACKER_MAX_DB_BLOCK_SIZE)
{
rc = RC_SET_AND_ASSERT( FERR_INCONSISTENT_BACKUP);
goto Exit;
}
// Get the maximum file size from the backup header.
uiBackupMaxFileSize = (FLMUINT)FB2UD( &pucBlkBuf[ FLM_BACKER_BFMAX_OFFSET]);
// Make sure the MTU is correct
if( FB2UD( &pucBlkBuf[ FLM_BACKER_MTU_OFFSET]) != FLM_BACKER_MTU_SIZE)
{
rc = RC_SET_AND_ASSERT( FERR_INCONSISTENT_BACKUP);
goto Exit;
}
// Make sure the backup type is correct
eBackupType = (FBackupType)FB2UD(
&pucBlkBuf[ FLM_BACKER_BACKUP_TYPE_OFFSET]);
if( (eBackupType == FLM_INCREMENTAL_BACKUP && !bIncremental) ||
(eBackupType == FLM_FULL_BACKUP && bIncremental))
{
// Do not allow an incremental backup to be restored directly. The
// only way to restore an incremental backup is to provide the
// incremental files when requested by FlmDbRestore. Also, we don't
// want to allow the user to mistakenly hand us a full backup when
// we are expecting an incremental backup.
rc = RC_SET( FERR_ILLEGAL_OP);
goto Exit;
}
// Grab the "next" incremental backup serial number
f_memcpy( ucNextIncSerialNum,
&pucBlkBuf[ FLM_BACKER_NEXT_INC_SERIAL_NUM],
F_SERIAL_NUM_SIZE);
// Get the database version from the backup header
uiDbVersion = FB2UD( &pucBlkBuf[ FLM_BACKER_DB_VERSION]);
if( puiDbVersion)
{
*puiDbVersion = uiDbVersion;
}
// Seek to the database header block
if( uiBlockSize > FLM_BACKER_MIN_DB_BLOCK_SIZE)
{
if( RC_BAD( rc = pBackerStream->read(
uiBlockSize - FLM_BACKER_MIN_DB_BLOCK_SIZE, pucBlkBuf)))
{
goto Exit;
}
}
// Read the database header block from the backup
if( RC_BAD( rc = pBackerStream->read( uiBlockSize, pucBlkBuf)))
{
goto Exit;
}
// Sanity check - make sure the block size in the backup header
// is the same as the size in the database header
if( uiBlockSize !=
FB2UD( &pucBlkBuf[ FLAIM_HEADER_START + DB_BLOCK_SIZE]))
{
rc = RC_SET_AND_ASSERT( FERR_INCONSISTENT_BACKUP);
goto Exit;
}
// Get a pointer to the log header and verify the checksum
pLogHdr = &pucBlkBuf[ DB_LOG_HEADER_START];
if( lgHdrCheckSum( pLogHdr, TRUE))
{
rc = RC_SET( FERR_BLOCK_CHECKSUM);
goto Exit;
}
// Compare the database version in the log header with
// the one extracted from the backup header
if( (FLMUINT)FB2UW( &pLogHdr[ LOG_FLAIM_VERSION]) != uiDbVersion)
{
rc = RC_SET_AND_ASSERT( FERR_INCONSISTENT_BACKUP);
goto Exit;
}
uiMaxFileSize = flmGetMaxFileSize( uiDbVersion, pLogHdr);
// Make sure the maximum block file size matches what was read from the
// backup header.
if( uiBackupMaxFileSize != uiMaxFileSize)
{
rc = RC_SET_AND_ASSERT( FERR_INCONSISTENT_BACKUP);
goto Exit;
}
// Allocate a super file object
if( (pSFile = *ppSFile) != NULL)
{
pSFile->AddRef();
}
else
{
flmAssert( !bIncremental);
if( (pSFile = f_new F_SuperFileHdl) == NULL)
{
rc = RC_SET( FERR_MEM);
goto Exit;
}
if( RC_BAD( rc = pSFile->setup( pszDbPath, pszDataDir, uiDbVersion)))
{
goto Exit;
}
// Don't want to do extra file extensions or flush when file
// is extended. Setting the extend size to ~0 has the effect
// of not updating the directory entry (a time-consuming operation)
// on Windows platforms. On all other platforms, we will just
// go with the default behavior.
pSFile->setExtendSize( (FLMUINT)(~0));
*ppSFile = pSFile;
(*ppSFile)->AddRef();
}
// Unshroud the database key (stored in the log header) using the
// password the user gave us. (Note: this only re-writes the data
// in the log header. It's up to the database open call to actually
// create the F_CCS object when it initializes the FFILE.)
if( pszPassword && *pszPassword &&
FB2UD( &pLogHdr[ LOG_FLAIM_VERSION]) >= FLM_FILE_FORMAT_VER_4_60 &&
FB2UW( &pLogHdr[ LOG_DATABASE_KEY_LEN]) > 0 )
{
FLMBYTE * pucTmpBuf = NULL;
FLMUINT32 ui32KeyLen = 0;
FLMUINT uiNewChecksum;
if ( (pTmpCCS = f_new F_CCS) == NULL)
{
rc = RC_SET( FERR_MEM);
goto Exit;
}
if ( RC_BAD( rc = pTmpCCS->init( TRUE, FLM_NICI_AES)))
{
goto Exit;
}
ui32KeyLen = FB2UW( &pLogHdr [ LOG_DATABASE_KEY_LEN]);
if( RC_BAD( rc = pTmpCCS->setKeyFromStore( &pLogHdr[ LOG_DATABASE_KEY],
ui32KeyLen, pszPassword, NULL, FALSE)))
{
goto Exit;
}
if( RC_BAD( rc = pTmpCCS->getKeyToStore( &pucTmpBuf, &ui32KeyLen,
NULL, NULL, FALSE)))
{
goto Exit;
}
// Verify that the field in the log header is long enough to
// hold the key.
if( ui32KeyLen > FLM_MAX_DB_ENC_KEY_LEN)
{
f_free( &pucTmpBuf);
rc = RC_SET_AND_ASSERT( FERR_BAD_ENC_KEY);
goto Exit;
}
UW2FBA( ui32KeyLen, &pLogHdr[ LOG_DATABASE_KEY_LEN]);
f_memcpy( &pLogHdr[LOG_DATABASE_KEY], pucTmpBuf, ui32KeyLen);
uiNewChecksum = lgHdrCheckSum( pLogHdr, FALSE);
UW2FBA( (FLMUINT16)uiNewChecksum, &pLogHdr[ LOG_HDR_CHECKSUM]);
f_free( &pucTmpBuf);
pTmpCCS->Release();
pTmpCCS = NULL;
if( ppucKeyToSave)
{
// Need to allocate a buffer to save the key in
if ( RC_BAD( rc = f_alloc( ui32KeyLen, ppucKeyToSave)))
{
goto Exit;
}
f_memcpy( *ppucKeyToSave, &pLogHdr[ LOG_DATABASE_KEY], ui32KeyLen);
*puiKeyLen = ui32KeyLen;
}
}
else if( pucKeyToUse)
{
UW2FBA( *puiKeyLen, &pLogHdr[ LOG_DATABASE_KEY_LEN]);
f_memcpy( &pLogHdr[ LOG_DATABASE_KEY], pucKeyToUse, *puiKeyLen);
}
// Get the logical EOF from the log header
uiLogicalEOF = FB2UD( &pLogHdr[ LOG_LOGICAL_EOF]);
// Are RFL files being preserved?
if( pbRflPreserved)
{
*pbRflPreserved = pLogHdr[ LOG_KEEP_RFL_FILES]
? TRUE
: FALSE;
}
// Get the incremental backup sequence number
uiIncSeqNum = FB2UD( &pLogHdr[ LOG_INC_BACKUP_SEQ_NUM]);
*puiNextIncSeqNum = uiIncSeqNum;
if( bIncremental)
{
(*puiNextIncSeqNum)++;
}
// Get information about the incremental backup
if( bIncremental)
{
FLMBYTE ucTmpSerialNum[ F_SERIAL_NUM_SIZE];
FLMBYTE ucTmpSeqNum[ 4];
FLMUINT uiTmp;
f_memcpy( ucIncSerialNum, &pLogHdr[ LOG_INC_BACKUP_SERIAL_NUM],
F_SERIAL_NUM_SIZE);
// Compare the incremental backup sequence number to the value in the
// database's log header.
if( RC_BAD( rc = pSFile->readHeader(
DB_LOG_HEADER_START + LOG_INC_BACKUP_SEQ_NUM,
4, ucTmpSeqNum, &uiTmp)))
{
goto Exit;
}
if( FB2UD( &ucTmpSeqNum[ 0]) != uiIncSeqNum)
{
rc = RC_SET( FERR_INVALID_FILE_SEQUENCE);
goto Exit;
}
// Compare the incremental backup serial number to the value in the
// database's log header.
if( RC_BAD( rc = pSFile->readHeader(
DB_LOG_HEADER_START + LOG_INC_BACKUP_SERIAL_NUM,
F_SERIAL_NUM_SIZE, ucTmpSerialNum, &uiTmp)))
{
goto Exit;
}
if( f_memcmp( ucIncSerialNum,
ucTmpSerialNum, F_SERIAL_NUM_SIZE) != 0)
{
rc = RC_SET( FERR_SERIAL_NUM_MISMATCH);
goto Exit;
}
// Increment the incremental backup sequence number
UD2FBA( (FLMUINT32)(uiIncSeqNum + 1),
&pLogHdr[ LOG_INC_BACKUP_SEQ_NUM]);
}
// At the start of a backup, either incremental or full,
// we generate a new incremental serial number. This is needed so
// that if the user performs a full backup, runs some transactions
// against the database, performs another full backup, and then
// performs an incremental backup we will know that the incremental
// backup cannot be restored against the first full backup.
// Since the new serial number is not written to the database's log
// header until after the backup completes, we need to put the
// new serial number in the log header during the restore. In doing
// so, the log header will contain the correct serial number for a
// subsequent incremental backup that may have been made.
if( uiDbVersion >= FLM_FILE_FORMAT_VER_4_3)
{
f_memcpy( &pLogHdr[ LOG_INC_BACKUP_SERIAL_NUM],
ucNextIncSerialNum, F_SERIAL_NUM_SIZE);
}
// Re-calculate the log header checksum
UW2FBA( (FLMUINT16)lgHdrCheckSum( pLogHdr, FALSE),
&pLogHdr[ LOG_HDR_CHECKSUM]);
pLogHdr = NULL;
// Set the "ok-to-retry" flag
if( pbOKToRetry)
{
*pbOKToRetry = FALSE;
}
// Write the database header
if( RC_BAD( rc = pSFile->writeHeader( 0,
uiBlockSize, pucBlkBuf, &uiBytesWritten)))
{
goto Exit;
}
// The status callback will give a general idea of how much work
// is left to do. We don't have any way to get the total size
// of the stream to give a correct count, so a close estimate
// will have to suffice.
byteProgress.ui64BytesToDo = FSGetSizeInBytes( uiMaxFileSize,
uiLogicalEOF);
// Write the blocks in the backup file to the database
for( ;;)
{
if( RC_BAD( rc = pBackerStream->read( FLM_BACKER_BLK_HDR_SIZE,
pucBlkBuf)))
{
goto Exit;
}
uiBlockCount++;
uiBlkAddr = FB2UD( &pucBlkBuf[ FLM_BACKER_BLK_ADDR_OFFSET]);
uiActualBlkSize = FB2UD( &pucBlkBuf[ FLM_BACKER_BLK_SIZE_OFFSET]);
// Are we done?
if( uiBlkAddr == 0xFFFFFFFF)
{
break;
}
if( !uiBlkAddr ||
!FSAddrIsBelow( uiBlkAddr, uiLogicalEOF) ||
(uiPriorBlkAddr && !FSAddrIsBelow( uiPriorBlkAddr, uiBlkAddr)))
{
rc = RC_SET_AND_ASSERT( FERR_INCONSISTENT_BACKUP);
goto Exit;
}
// Read and process the block
if( uiActualBlkSize > uiBlockSize || uiActualBlkSize < BH_OVHD)
{
rc = RC_SET_AND_ASSERT( FERR_INCONSISTENT_BACKUP);
goto Exit;
}
if( RC_BAD( rc = pBackerStream->read( uiActualBlkSize, pucBlkBuf)))
{
goto Exit;
}
if( (GET_BH_ADDR( pucBlkBuf) & 0xFFFFFF00) != (uiBlkAddr & 0xFFFFFF00))
{
rc = RC_SET_AND_ASSERT( FERR_INCONSISTENT_BACKUP);
goto Exit;
}
if( uiActualBlkSize < uiBlockSize)
{
f_memset( &pucBlkBuf[ uiActualBlkSize], 0,
uiBlockSize - uiActualBlkSize);
}
// Verify the checksum
ucLowChecksumByte = pucBlkBuf[ BH_CHECKSUM_LOW];
if( RC_BAD( rc = BlkCheckSum( pucBlkBuf, CHECKSUM_CHECK,
uiBlkAddr, uiBlockSize)))
{
if( rc == FERR_BLOCK_CHECKSUM)
{
rc = RC_SET_AND_ASSERT( FERR_INCONSISTENT_BACKUP);
}
goto Exit;
}
pucBlkBuf[ BH_CHECKSUM_LOW] = ucLowChecksumByte;
// Write the block to the database
//
// Unix systems can have sector sizes that are larger than our
// typical 4K database blocks. The Unix implementation of SectorWrite
// (called by WriteBlock) will write the passed-in block and clobber any
// additional data beyond the end of the block to the end of the sector if
// it has enough room in the block buffer to write a full sector. If the
// block buffer is less than a full sector, the Unix SectorWrite will only
// write out the amount requested, not a full sector.
if( RC_BAD( rc = pSFile->writeBlock( uiBlkAddr,
uiBlockSize, pucBlkBuf, uiBlockSize,
NULL, &uiBytesWritten)))
{
if( rc == FERR_IO_PATH_NOT_FOUND ||
rc == FERR_IO_INVALID_PATH)
{
// Create a new block file
if( FSGetFileNumber( uiBlkAddr) != (uiPriorBlkFile + 1))
{
rc = RC_SET_AND_ASSERT( FERR_INCONSISTENT_BACKUP);
goto Exit;
}
if( RC_BAD( rc = pSFile->createFile(
FSGetFileNumber( uiBlkAddr))))
{
goto Exit;
}
if( RC_BAD( rc = pSFile->writeBlock( uiBlkAddr,
uiBlockSize, pucBlkBuf, uiBlockSize,
NULL, &uiBytesWritten)))
{
goto Exit;
}
}
else
{
goto Exit;
}
}
uiPriorBlkAddr = uiBlkAddr;
uiPriorBlkFile = FSGetFileNumber( uiBlkAddr);
if( (uiBlockCount & 0x7F) == 0x7F)
{
byteProgress.ui64BytesDone = FSGetSizeInBytes( uiMaxFileSize,
uiBlkAddr);
if( RC_BAD( rc = pRestoreObj->status( RESTORE_PROGRESS, 0,
(void *)&byteProgress, NULL, NULL, peRestoreAction)))
{
goto Exit;
}
if( *peRestoreAction == RESTORE_ACTION_STOP)
{
rc = RC_SET( FERR_USER_ABORT);
goto Exit;
}
}
}
// Call the status callback one last time.
byteProgress.ui64BytesDone = byteProgress.ui64BytesToDo;
if( RC_BAD( rc = pRestoreObj->status( RESTORE_PROGRESS, 0,
(void *)&byteProgress, NULL, NULL, peRestoreAction)))
{
goto Exit;
}
if( *peRestoreAction == RESTORE_ACTION_STOP)
{
// It is safe to jump to exit at this point
goto Exit;
}
Exit:
if( pucBlkBuf)
{
f_freeAlignedBuffer( (void **)&pucBlkBuf);
}
if( pBackerStream)
{
pBackerStream->Release();
}
if( pTmpCCS)
{
pTmpCCS->Release();
}
if( pSFile)
{
pSFile->Release();
}
return( rc);
}
/****************************************************************************
Desc: Default hook for creating a backup file set
****************************************************************************/
FSTATIC RCODE flmDefaultBackerWriteHook(
void * pvBuffer,
FLMUINT uiBytesToWrite,
void * pvCallbackData)
{
BACKER_HOOK_STATE * pState = (BACKER_HOOK_STATE *)pvCallbackData;
FLMUINT uiBytesWritten;
RCODE rc = pState->rc;
if( RC_BAD( rc))
{
goto Exit;
}
if( !pState->pMultiFileHdl)
{
// Remove any existing backup files
if( RC_BAD( rc = FlmAllocMultiFileHdl( &pState->pMultiFileHdl)))
{
goto Exit;
}
if( RC_BAD( rc = pState->pMultiFileHdl->deleteMultiFile(
pState->szPath)) && rc != FERR_IO_PATH_NOT_FOUND &&
rc != FERR_IO_INVALID_PATH)
{
pState->pMultiFileHdl->Release();
pState->pMultiFileHdl = NULL;
goto Exit;
}
if( RC_BAD( rc = pState->pMultiFileHdl->create( pState->szPath)))
{
pState->pMultiFileHdl->Release();
pState->pMultiFileHdl = NULL;
goto Exit;
}
}
rc = pState->pMultiFileHdl->write( pState->ui64Offset,
uiBytesToWrite, pvBuffer, &uiBytesWritten);
pState->ui64Offset += uiBytesWritten;
Exit:
if( RC_BAD( rc))
{
pState->rc = rc;
if( pState->pMultiFileHdl)
{
pState->pMultiFileHdl->Release();
pState->pMultiFileHdl = NULL;
}
}
return( rc);
}
// F_BackerStream methods
/****************************************************************************
Desc: Constructor
****************************************************************************/
F_BackerStream::F_BackerStream( void)
{
m_bSetup = FALSE;
m_bFirstRead = TRUE;
m_ui64ByteCount = 0;
m_uiBufOffset = 0;
m_pRestoreObj = NULL;
m_hDataSem = F_SEM_NULL;
m_hIdleSem = F_SEM_NULL;
m_pThread = NULL;
m_rc = FERR_OK;
m_pucInBuf = NULL;
m_puiInOffset = NULL;
m_pucOutBuf = NULL;
m_puiOutOffset = NULL;
m_pucBufs[ 0] = NULL;
m_pucBufs[ 1] = NULL;
m_uiOffsets[ 0] = 0;
m_uiOffsets[ 1] = 0;
m_uiMTUSize = 0;
m_uiPendingIO = 0;
m_fnWrite = NULL;
m_pvCallbackData = NULL;
}
/****************************************************************************
Desc: Destructor
****************************************************************************/
F_BackerStream::~F_BackerStream( void)
{
shutdownThreads();
if( m_hDataSem != F_SEM_NULL)
{
f_semDestroy( &m_hDataSem);
}
if( m_hIdleSem != F_SEM_NULL)
{
f_semDestroy( &m_hIdleSem);
}
if( m_pucBufs[ 0])
{
f_free( &m_pucBufs[ 0]);
}
if( m_pucBufs[ 1])
{
f_free( &m_pucBufs[ 1]);
}
}
/****************************************************************************
Desc: Start any background threads
****************************************************************************/
RCODE F_BackerStream::startThreads( void)
{
RCODE rc = FERR_OK;
if( m_pThread)
{
rc = RC_SET( FERR_FAILURE);
goto Exit;
}
// The semaphore handles better be null
flmAssert( m_hDataSem == F_SEM_NULL);
flmAssert( m_hIdleSem == F_SEM_NULL);
// Create a semaphore to signal the background thread
// that data is available
if( RC_BAD( rc = f_semCreate( &m_hDataSem)))
{
goto Exit;
}
// Create a semaphore to signal when the background thread
// is idle
if( RC_BAD( rc = f_semCreate( &m_hIdleSem)))
{
goto Exit;
}
// Start the thread
if( m_fnWrite)
{
if( RC_BAD( rc = f_threadCreate( &m_pThread,
F_BackerStream::writeThread, "backup",
0, 0, (void *)this)))
{
goto Exit;
}
}
else if( m_pRestoreObj)
{
if( RC_BAD( rc = f_threadCreate( &m_pThread,
F_BackerStream::readThread, "restore",
0, 0, (void *)this)))
{
goto Exit;
}
}
else
{
flmAssert( 0);
rc = RC_SET( FERR_FAILURE);
goto Exit;
}
Exit:
return( rc);
}
/****************************************************************************
Desc: Shut down any background threads
****************************************************************************/
void F_BackerStream::shutdownThreads( void)
{
if( m_pThread)
{
// Shut down the background read or write thread.
m_pThread->setShutdownFlag();
f_semSignal( m_hDataSem);
f_threadDestroy( &m_pThread);
// Now that the thread has terminated, it is safe
// to destroy the data and idle semaphores.
f_semDestroy( &m_hDataSem);
f_semDestroy( &m_hIdleSem);
}
}
/****************************************************************************
Desc: Setup method to use the backer stream as an input stream
****************************************************************************/
RCODE F_BackerStream::setup(
FLMUINT uiMTUSize,
F_Restore * pRestoreObj)
{
RCODE rc = FERR_OK;
flmAssert( pRestoreObj);
flmAssert( !m_bSetup);
m_pRestoreObj = pRestoreObj;
m_uiMTUSize = uiMTUSize;
if( RC_BAD( rc = _setup()))
{
goto Exit;
}
// Fire up the background threads
if( RC_BAD( rc = startThreads()))
{
goto Exit;
}
m_bSetup = TRUE;
Exit:
return( rc);
}
/****************************************************************************
Desc: Setup method to use the backer stream as an output stream
****************************************************************************/
RCODE F_BackerStream::setup(
FLMUINT uiMTUSize,
BACKER_WRITE_HOOK fnWrite,
void * pvCallbackData)
{
RCODE rc = FERR_OK;
flmAssert( fnWrite);
flmAssert( !m_bSetup);
m_fnWrite = fnWrite;
m_uiMTUSize = uiMTUSize;
m_pvCallbackData = pvCallbackData;
if( RC_BAD( rc = _setup()))
{
goto Exit;
}
// Fire up the background threads
if( RC_BAD( rc = startThreads()))
{
goto Exit;
}
m_bSetup = TRUE;
Exit:
return( rc);
}
/****************************************************************************
Desc: Performs setup operations common to read and write streams
****************************************************************************/
RCODE F_BackerStream::_setup( void)
{
RCODE rc = FERR_OK;
// Allocate a buffer for reading or writing blocks
if( (m_uiMTUSize < (2 * FLM_BACKER_MAX_DB_BLOCK_SIZE)) ||
m_uiMTUSize % FLM_BACKER_MAX_DB_BLOCK_SIZE)
{
rc = RC_SET( FERR_INVALID_PARM);
goto Exit;
}
// Allocate buffers for reading or writing
if( RC_BAD( rc = f_alloc( m_uiMTUSize, &m_pucBufs[ 0])))
{
goto Exit;
}
if( RC_BAD( rc = f_alloc( m_uiMTUSize, &m_pucBufs[ 1])))
{
goto Exit;
}
m_pucInBuf = m_pucBufs[ 0];
m_puiInOffset = &m_uiOffsets[ 0];
m_pucOutBuf = m_pucBufs[ 1];
m_puiOutOffset = &m_uiOffsets[ 1];
Exit:
return( rc);
}
/****************************************************************************
Desc: Reads data from the input stream
****************************************************************************/
RCODE F_BackerStream::read(
FLMUINT uiLength,
FLMBYTE * pucData,
FLMUINT * puiBytesRead)
{
FLMUINT uiBufSize;
FLMBYTE * pucBuf;
FLMUINT uiBytesRead = 0;
FLMUINT uiBytesAvail;
RCODE rc = FERR_OK;
flmAssert( m_bSetup);
flmAssert( m_pRestoreObj);
flmAssert( uiLength);
if( m_bFirstRead)
{
m_bFirstRead = FALSE;
// Prime the pump. Call signalThread twice ... once to
// get the first chunk of data and a second time to have
// the background thread pre-fetch the next chunk. A backup
// will always have at least two MTU data chunks, so we should
// never get an IO_END_OF_FILE error. If we do, the restore
// operation needs to abort (which will happen because the
// error will be returned to the caller of this routine).
if( RC_BAD( rc = signalThread()) ||
RC_BAD( rc = signalThread()))
{
goto Exit;
}
}
while( uiLength)
{
uiBufSize = *m_puiOutOffset;
pucBuf = m_pucOutBuf;
uiBytesAvail = uiBufSize - m_uiBufOffset;
flmAssert( uiBytesAvail);
if( uiBytesAvail < uiLength)
{
f_memcpy( &pucData[ uiBytesRead],
&pucBuf[ m_uiBufOffset], uiBytesAvail);
m_uiBufOffset += uiBytesAvail;
uiBytesRead += uiBytesAvail;
uiLength -= uiBytesAvail;
}
else
{
f_memcpy( &pucData[ uiBytesRead],
&pucBuf[ m_uiBufOffset], uiLength);
m_uiBufOffset += uiLength;
uiBytesRead += uiLength;
uiLength = 0;
}
if( m_uiBufOffset == uiBufSize)
{
m_uiBufOffset = 0;
if( RC_BAD( rc = signalThread()))
{
// Since we are reading MTU-sized units and the restore
// code knows when to stop reading, we should never
// get an IO_END_OF_FILE error back from a call to
// signalThread(). If we do, we need to return the
// error to the caller (FlmDbRestore).
goto Exit;
}
}
}
Exit:
if( puiBytesRead)
{
*puiBytesRead = uiBytesRead;
}
m_ui64ByteCount += (FLMUINT64)uiBytesRead;
return( rc);
}
/****************************************************************************
Desc: Writes data to the output stream
****************************************************************************/
RCODE F_BackerStream::write(
FLMUINT uiLength,
FLMBYTE * pucData,
FLMUINT * puiBytesWritten)
{
FLMUINT uiMaxWriteSize;
FLMUINT uiBytesWritten = 0;
RCODE rc = FERR_OK;
flmAssert( m_bSetup);
flmAssert( m_fnWrite);
flmAssert( uiLength);
while( uiLength)
{
uiMaxWriteSize = m_uiMTUSize - *m_puiInOffset;
flmAssert( uiMaxWriteSize);
if( uiMaxWriteSize < uiLength)
{
f_memcpy( &m_pucInBuf[ *m_puiInOffset],
&pucData[ uiBytesWritten], uiMaxWriteSize);
(*m_puiInOffset) += uiMaxWriteSize;
uiBytesWritten += uiMaxWriteSize;
uiLength -= uiMaxWriteSize;
}
else
{
f_memcpy( &m_pucInBuf[ *m_puiInOffset],
&pucData[ uiBytesWritten], uiLength);
(*m_puiInOffset) += uiLength;
uiBytesWritten += uiLength;
uiLength = 0;
}
if( (*m_puiInOffset) == m_uiMTUSize)
{
if( RC_BAD( rc = signalThread()))
{
goto Exit;
}
}
}
Exit:
if( puiBytesWritten)
{
*puiBytesWritten = uiBytesWritten;
}
m_ui64ByteCount += (FLMUINT64)uiBytesWritten;
return( rc);
}
/****************************************************************************
Desc: Flushes any pending writes to the output stream
****************************************************************************/
RCODE F_BackerStream::flush( void)
{
RCODE rc = FERR_OK;
flmAssert( m_bSetup);
if( m_fnWrite && m_pThread)
{
if( *m_puiInOffset)
{
if( RC_BAD( rc = signalThread()))
{
goto Exit;
}
}
// Wait for the background thread to become idle. When it
// does, we know that all writes have completed.
if( RC_BAD( rc = f_semWait( m_hIdleSem, F_SEM_WAITFOREVER)))
{
goto Exit;
}
// If the background thread set an error code, we need to return it.
rc = m_rc;
// At this point, we know the background thread is either waiting
// for the data semaphore to be signaled or it has exited due to
// an error. We need to re-signal the idle semaphore so that
// other F_BackerStream calls (i.e., additional calls to
// flush, etc.) will not block waiting for it to be signaled
// since it won't be signaled by the background thread until
// after the data semaphore has been signaled again.
f_semSignal( m_hIdleSem);
// Jump to exit if we have a bad rc
if( RC_BAD( rc))
{
goto Exit;
}
}
Exit:
return( rc);
}
/****************************************************************************
Desc: Signals the read or write thread indicating that data is needed or
that data is available.
****************************************************************************/
RCODE F_BackerStream::signalThread( void)
{
FLMBYTE * pucTmp;
FLMUINT * puiTmp;
RCODE rc = FERR_OK;
flmAssert( m_bSetup);
// Return an error if we don't have a thread.
if( !m_pThread)
{
rc = RC_SET( FERR_FAILURE);
goto Exit;
}
// Wait for the thread to become idle
if( RC_BAD( rc = f_semWait( m_hIdleSem, F_SEM_WAITFOREVER)))
{
goto Exit;
}
if( RC_BAD( rc = m_rc))
{
// If m_rc is bad, we know that the background thread has
// exited and will not signal the idle semaphore again.
// Thus, we will re-signal the idle semaphore so that if the
// code using this class happens to call flush() or some
// other method that waits on the idle semaphore, we
// won't wait forever on something that will never happen.
f_semSignal( m_hIdleSem);
// Check the error code
if( rc != FERR_IO_END_OF_FILE)
{
goto Exit;
}
}
pucTmp = m_pucOutBuf;
puiTmp = m_puiOutOffset;
m_pucOutBuf = m_pucInBuf;
m_puiOutOffset = m_puiInOffset;
m_pucInBuf = pucTmp;
m_puiInOffset = puiTmp;
*(m_puiInOffset) = 0;
// If m_rc is bad, the background thread has terminated.
if( RC_OK( m_rc))
{
// Signal the thread to read or write data
// NOTE: The background thread will never be decrementing
// m_uiPendingIO while we are incrementing it because it
// will be blocked on m_hDataSem waiting for it to
// be signaled.
m_uiPendingIO++;
f_semSignal( m_hDataSem);
}
Exit:
return( rc);
}
/****************************************************************************
Desc: This thread reads data in the background
****************************************************************************/
RCODE F_BackerStream::readThread(
IF_Thread * pThread)
{
F_BackerStream * pBackerStream = (F_BackerStream *)pThread->getParm1();
RCODE rc = FERR_OK;
for( ;;)
{
f_semSignal( pBackerStream->m_hIdleSem);
if( RC_BAD( rc = f_semWait( pBackerStream->m_hDataSem,
F_SEM_WAITFOREVER)))
{
goto Exit;
}
if( !pBackerStream->m_uiPendingIO &&
pThread->getShutdownFlag())
{
break;
}
if( RC_BAD( rc = pBackerStream->m_pRestoreObj->read(
pBackerStream->m_uiMTUSize, pBackerStream->m_pucInBuf,
pBackerStream->m_puiInOffset)))
{
goto Exit;
}
flmAssert( pBackerStream->m_uiPendingIO);
pBackerStream->m_uiPendingIO--;
}
Exit:
pBackerStream->m_rc = rc;
pBackerStream->m_uiPendingIO = 0;
f_semSignal( pBackerStream->m_hIdleSem);
return( rc);
}
/****************************************************************************
Desc: This thread writes data in the background
****************************************************************************/
RCODE F_BackerStream::writeThread(
IF_Thread * pThread)
{
RCODE rc = FERR_OK;
F_BackerStream * pBackerStream = (F_BackerStream *)pThread->getParm1();
for( ;;)
{
f_semSignal( pBackerStream->m_hIdleSem);
if( RC_BAD( rc = f_semWait( pBackerStream->m_hDataSem,
F_SEM_WAITFOREVER)))
{
goto Exit;
}
if( *(pBackerStream->m_puiOutOffset))
{
if( RC_BAD( rc = pBackerStream->m_fnWrite(
pBackerStream->m_pucOutBuf, *(pBackerStream->m_puiOutOffset),
pBackerStream->m_pvCallbackData)))
{
goto Exit;
}
// Reset *puiOutOffset so that we won't re-write
// the same data again if we receive a shut-down
// signal.
*(pBackerStream->m_puiOutOffset) = 0;
}
// Need to put the thread shutdown check here
// so that if the thread is signaled twice,
// once to do final work and once to shut down,
// we will actually do the work before we exit.
if( pThread->getShutdownFlag())
{
break;
}
}
Exit:
pBackerStream->m_rc = rc;
pBackerStream->m_uiPendingIO = 0;
f_semSignal( pBackerStream->m_hIdleSem);
return( rc);
}
/****************************************************************************
Desc:
****************************************************************************/
F_FSRestore::F_FSRestore()
{
m_pFileHdl = NULL;
m_pMultiFileHdl = NULL;
m_ui64Offset = 0;
m_bSetupCalled = FALSE;
m_szDbPath[ 0] = 0;
m_uiDbVersion = 0;
m_szBackupSetPath[ 0] = 0;
m_szRflDir[ 0] = 0;
m_bOpen = FALSE;
}
/****************************************************************************
Desc:
****************************************************************************/
F_FSRestore::~F_FSRestore()
{
if( m_bOpen)
{
(void)close();
}
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE F_FSRestore::setup(
const char * pucDbPath,
const char * pucBackupSetPath,
const char * pucRflDir)
{
flmAssert( !m_bSetupCalled);
flmAssert( pucDbPath);
flmAssert( pucBackupSetPath);
f_strcpy( m_szDbPath, pucDbPath);
f_strcpy( m_szBackupSetPath, pucBackupSetPath);
if( pucRflDir)
{
f_strcpy( m_szRflDir, pucRflDir);
}
m_bSetupCalled = TRUE;
return( FERR_OK);
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE F_FSRestore::openBackupSet( void)
{
RCODE rc = FERR_OK;
flmAssert( m_bSetupCalled);
flmAssert( !m_pMultiFileHdl);
if( RC_BAD( rc = FlmAllocMultiFileHdl( &m_pMultiFileHdl)))
{
goto Exit;
}
if( RC_BAD( rc = m_pMultiFileHdl->open( m_szBackupSetPath)))
{
m_pMultiFileHdl->Release();
m_pMultiFileHdl = NULL;
goto Exit;
}
m_ui64Offset = 0;
m_bOpen = TRUE;
Exit:
return( rc);
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE F_FSRestore::openRflFile(
FLMUINT uiFileNum)
{
RCODE rc = FERR_OK;
char szRflPath[ F_PATH_MAX_SIZE];
char szDbPrefix[ F_FILENAME_SIZE];
char szBaseName[ F_FILENAME_SIZE];
FLMBYTE * pBuf = NULL;
FILE_HDR fileHdr;
LOG_HDR logHdr;
IF_FileHdl * pFileHdl = NULL;
flmAssert( m_bSetupCalled);
flmAssert( uiFileNum);
flmAssert( !m_pFileHdl);
// Read the database header to determine the version number
if( !m_uiDbVersion)
{
if (RC_BAD( rc = f_alloc( 2048, &pBuf)))
{
goto Exit;
}
if( RC_BAD( rc = gv_FlmSysData.pFileSystem->openFile(
m_szDbPath, FLM_IO_RDWR | FLM_IO_SH_DENYNONE, &pFileHdl)))
{
goto Exit;
}
if( RC_BAD( rc = flmReadAndVerifyHdrInfo( NULL, pFileHdl,
pBuf, &fileHdr, &logHdr, NULL)))
{
goto Exit;
}
pFileHdl->Release();
pFileHdl = NULL;
m_uiDbVersion = fileHdr.uiVersionNum;
}
// Generate the log file name.
if( RC_BAD( rc = rflGetDirAndPrefix(
m_uiDbVersion, m_szDbPath, m_szRflDir, szRflPath, szDbPrefix)))
{
goto Exit;
}
rflGetBaseFileName( m_uiDbVersion, szDbPrefix, uiFileNum, szBaseName);
gv_FlmSysData.pFileSystem->pathAppend( szRflPath, szBaseName);
// Open the file.
if( RC_BAD( rc = gv_FlmSysData.pFileSystem->openFile(
szRflPath, FLM_IO_RDWR | FLM_IO_SH_DENYNONE | FLM_IO_DIRECT,
&m_pFileHdl)))
{
goto Exit;
}
m_bOpen = TRUE;
m_ui64Offset = 0;
Exit:
if( pBuf)
{
f_free( &pBuf);
}
if( pFileHdl)
{
pFileHdl->Release();
}
return( rc);
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE F_FSRestore::openIncFile(
FLMUINT uiFileNum)
{
RCODE rc = FERR_OK;
char szIncPath[ F_PATH_MAX_SIZE];
char szIncFile[ F_FILENAME_SIZE];
flmAssert( m_bSetupCalled);
flmAssert( !m_pMultiFileHdl);
/*
Since this is a non-interactive restore, we will "guess"
that incremental backups are located in the same parent
directory as the main backup set. We will further assume
that the incremental backup sets have been named XXXXXXXX.INC,
where X is a hex digit.
*/
if( RC_BAD( rc = gv_FlmSysData.pFileSystem->pathReduce( m_szBackupSetPath,
szIncPath, NULL)))
{
goto Exit;
}
f_sprintf( szIncFile, "%08X.INC", (unsigned)uiFileNum);
gv_FlmSysData.pFileSystem->pathAppend( szIncPath, szIncFile);
if( RC_BAD( rc = FlmAllocMultiFileHdl( &m_pMultiFileHdl)))
{
goto Exit;
}
if( RC_BAD( rc = m_pMultiFileHdl->open( szIncPath)))
{
m_pMultiFileHdl->Release();
m_pMultiFileHdl = NULL;
goto Exit;
}
m_ui64Offset = 0;
m_bOpen = TRUE;
Exit:
return( rc);
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE F_FSRestore::read(
FLMUINT uiLength,
void * pvBuffer,
FLMUINT * puiBytesRead)
{
FLMUINT uiBytesRead = 0;
RCODE rc = FERR_OK;
flmAssert( m_bSetupCalled);
flmAssert( m_pFileHdl || m_pMultiFileHdl);
if( m_pMultiFileHdl)
{
if( RC_BAD( rc = m_pMultiFileHdl->read( m_ui64Offset,
uiLength, pvBuffer, &uiBytesRead)))
{
goto Exit;
}
}
else
{
if( RC_BAD( rc = m_pFileHdl->read( (FLMUINT)m_ui64Offset,
uiLength, pvBuffer, &uiBytesRead)))
{
goto Exit;
}
}
Exit:
m_ui64Offset += uiBytesRead;
if( puiBytesRead)
{
*puiBytesRead = uiBytesRead;
}
return( rc);
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE F_FSRestore::close( void)
{
flmAssert( m_bSetupCalled);
if( m_pMultiFileHdl)
{
m_pMultiFileHdl->Release();
m_pMultiFileHdl = NULL;
}
if( m_pFileHdl)
{
m_pFileHdl->Release();
m_pFileHdl = NULL;
}
m_bOpen = FALSE;
m_ui64Offset = 0;
return( FERR_OK);
}