Files
mars-flaim/flaim/src/fdbcopy.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

1098 lines
25 KiB
C++

//-------------------------------------------------------------------------
// Desc: Copy database.
// Tabs: 3
//
// Copyright (c) 1998-2001,2003-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: fdbcopy.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $
//-------------------------------------------------------------------------
#include "flaimsys.h"
typedef struct Copied_Name
{
char szPath[ F_PATH_MAX_SIZE];
Copied_Name * pNext;
} COPIED_NAME;
FSTATIC RCODE flmCopyDb(
FLMUINT uiDbVersion,
const char * pszSrcDbName,
const char * pszSrcDataDir,
const char * pszSrcRflDir,
const char * pszDestDbName,
const char * pszDestDataDir,
const char * pszDestRflDir,
STATUS_HOOK fnStatusCallback,
void * UserData);
FSTATIC RCODE flmCopyFile(
IF_FileSystem * pFileSystem,
FLMUINT uiStartOffset,
FLMUINT uiEndOffset,
DB_COPY_INFO * pDbCopyInfo,
COPIED_NAME ** ppCopiedListRV,
FLMBYTE * pucInMemLogHdr,
FLMBOOL bOkToTruncate,
STATUS_HOOK fnStatusCallback,
void * UserData);
/*******************************************************************************
Desc: Copies a database, including roll-forward log files.
*******************************************************************************/
FLMEXP RCODE FLMAPI FlmDbCopy(
const char * pszSrcDbName,
const char * pszSrcDataDir,
const char * pszSrcRflDir,
const char * pszDestDbName,
const char * pszDestDataDir,
const char * pszDestRflDir,
STATUS_HOOK fnStatusCallback,
void * UserData)
{
RCODE rc = FERR_OK;
FLMBYTE * pucLastCommittedLogHdr;
HFDB hDb = HFDB_NULL;
FDB * pDb;
FLMBOOL bDbLocked = FALSE;
FLMUINT uiDbVersion;
// Make sure the destination database is closed
if (RC_BAD( rc = FlmConfig( FLM_CLOSE_FILE,
(void *)pszDestDbName, (void *)pszDestDataDir)))
{
goto Exit;
}
// Open the database so we can force a checkpoint.
if (RC_BAD( rc = FlmDbOpen( pszSrcDbName, pszSrcDataDir, pszSrcRflDir,
0, NULL, &hDb)))
{
goto Exit;
}
pDb = (FDB *)hDb;
// Need to lock the database, because we want to do a checkpoint
// and then the copy immediately after without letting other
// threads have the opportunity to get in and update the
// database.
if (RC_BAD( rc = FlmDbLock( hDb, FLM_LOCK_EXCLUSIVE, 0, FLM_NO_TIMEOUT)))
{
goto Exit;
}
bDbLocked = TRUE;
// Force a checkpoint
if (RC_BAD( rc = FlmDbCheckpoint( hDb, FLM_NO_TIMEOUT)))
{
goto Exit;
}
pucLastCommittedLogHdr = &pDb->pFile->ucLastCommittedLogHdr[ 0];
// Get the low and high RFL log file numbers from the log
// header.
uiDbVersion = pDb->pFile->FileHdr.uiVersionNum;
// Once we get this far, we have exclusive access to the database
// and we have forced a checkpoint. The database's contents are
// guaranteed to be on disk at this point, and they will not
// change.
rc = flmCopyDb( uiDbVersion, pszSrcDbName, pszSrcDataDir, pszSrcRflDir,
pszDestDbName, pszDestDataDir, pszDestRflDir,
fnStatusCallback, UserData);
Exit:
// Unlock and close the database
if (bDbLocked)
{
FlmDbUnlock( hDb);
}
if (hDb != HFDB_NULL)
{
(void)FlmDbClose( &hDb);
(void)FlmConfig( FLM_CLOSE_FILE, (void *)pszSrcDbName,
(void *)pszSrcDataDir);
}
return( rc);
}
/****************************************************************************
Desc: Copy a database's files, including roll-forward log files.
*****************************************************************************/
FSTATIC RCODE flmCopyDb(
FLMUINT uiDbVersion,
const char * pszSrcDbName,
const char * pszSrcDataDir,
const char * pszSrcRflDir,
const char * pszDestDbName,
const char * pszDestDataDir,
const char * pszDestRflDir,
STATUS_HOOK fnStatusCallback,
void * UserData)
{
RCODE rc = FERR_OK;
DB_COPY_INFO DbCopyInfo;
F_SuperFileHdl SrcSFileHdl;
F_SuperFileHdl DestSFileHdl;
FLMUINT uiFileNumber;
FLMUINT uiHighFileNumber;
FLMUINT uiHighLogFileNumber;
FLMUINT64 ui64FileSize;
FFILE * pFile = NULL;
FLMBOOL bMutexLocked = FALSE;
IF_FileHdl * pLockFileHdl = NULL;
IF_FileHdl * pTmpFileHdl = NULL;
IF_DirHdl * pDirHdl = NULL;
FLMBOOL bFileLocked = FALSE;
FLMBOOL bWriteLocked = FALSE;
IF_LockObject * pWriteLockObj = NULL;
IF_LockObject * pFileLockObj = NULL;
COPIED_NAME * pCopiedList = NULL;
FLMBOOL bUsedFFile = FALSE;
FLMBYTE * pucInMemLogHdr = NULL;
eLockType currLockType;
FLMUINT uiLockThreadId;
char * pszActualSrcRflPath = NULL;
char * pszSrcPrefix = NULL;
char * pszActualDestRflPath = NULL;
char * pszDestPrefix = NULL;
FLMBOOL bCreatedDestRflDir = FALSE;
F_SEM hWaitSem = F_SEM_NULL;
f_memset( &DbCopyInfo, 0, sizeof( DbCopyInfo));
// Should not do anything if the source and destination names
// are the same.
if (f_stricmp( pszSrcDbName, pszDestDbName) == 0)
{
goto Exit;
}
// Allocate a semaphore
if( RC_BAD( rc = f_semCreate( &hWaitSem)))
{
goto Exit;
}
// Allocate memory for paths we don't want to push onto the stack.
if (RC_BAD( rc = f_calloc(
(F_PATH_MAX_SIZE + F_FILENAME_SIZE) * 2, &pszActualSrcRflPath)))
{
goto Exit;
}
pszSrcPrefix = &pszActualSrcRflPath[ F_PATH_MAX_SIZE];
pszActualDestRflPath = &pszSrcPrefix[ F_FILENAME_SIZE];
pszDestPrefix = &pszActualDestRflPath[ F_PATH_MAX_SIZE];
// Set up the super file object for the source database.
// Must at least open the control file.
if (RC_BAD( rc = SrcSFileHdl.setup( pszSrcDbName, pszSrcDataDir,
uiDbVersion)))
{
goto Exit;
}
// Lock the destination database, if not already locked.
// This is so we can overwrite it without necessarily
// deleting it. May unlock and re-lock the global mutex.
f_mutexLock( gv_FlmSysData.hShareMutex);
bMutexLocked = TRUE;
if (RC_BAD( rc = flmFindFile( pszDestDbName, pszDestDataDir, &pFile)))
{
goto Exit;
}
// If we didn't find an FFILE structure, get an
// exclusive lock on the file.
if (!pFile)
{
f_mutexUnlock( gv_FlmSysData.hShareMutex);
bMutexLocked = FALSE;
// Attempt to get an exclusive lock on the file.
if (RC_BAD( rc = flmCreateLckFile( pszDestDbName, &pLockFileHdl)))
{
goto Exit;
}
}
else
{
// The call to flmVerifyFileUse will wait if the file is in
// the process of being opened by another thread.
if (RC_BAD( rc = flmVerifyFileUse( gv_FlmSysData.hShareMutex, &pFile)))
{
goto Exit;
}
// Increment the use count on the FFILE so it will not
// disappear while we are copying the file.
if (++pFile->uiUseCount == 1)
{
flmUnlinkFileFromNUList( pFile);
}
bUsedFFile = TRUE;
f_mutexUnlock( gv_FlmSysData.hShareMutex);
bMutexLocked = FALSE;
pucInMemLogHdr = &pFile->ucLastCommittedLogHdr [0];
// Lock the destination file object and transaction
// object, if not already locked.
pFile->pFileLockObj->getLockInfo( 0, &currLockType, &uiLockThreadId, NULL);
if (currLockType != FLM_LOCK_EXCLUSIVE || uiLockThreadId != f_threadId())
{
pFileLockObj = pFile->pFileLockObj;
pFileLockObj->AddRef();
if (RC_BAD( rc = pFileLockObj->lock( hWaitSem,
TRUE, FLM_NO_TIMEOUT, 0)))
{
goto Exit;
}
bFileLocked = TRUE;
}
// Lock the write object, if not already locked
pFile->pWriteLockObj->getLockInfo( 0, &currLockType, &uiLockThreadId, NULL);
if( currLockType != FLM_LOCK_EXCLUSIVE ||
uiLockThreadId != f_threadId())
{
pWriteLockObj = pFile->pWriteLockObj;
pWriteLockObj->AddRef();
// Only contention here is with the checkpoint thread - wait
// forever until the checkpoint thread gives it up.
if( RC_BAD( rc = pWriteLockObj->lock( hWaitSem,
TRUE, FLM_NO_TIMEOUT, 0)))
{
goto Exit;
}
bWriteLocked = TRUE;
}
}
// Set up the super file object for the destination database.
if (RC_BAD( rc = DestSFileHdl.setup( pszDestDbName, pszDestDataDir,
uiDbVersion)))
{
goto Exit;
}
// See how many files we have and calculate the total size.
uiHighFileNumber = 0;
for (;;)
{
if ((RC_BAD( rc = SrcSFileHdl.getFileSize(
uiHighFileNumber, &ui64FileSize))) || !ui64FileSize )
{
if (rc == FERR_IO_PATH_NOT_FOUND ||
rc == FERR_IO_INVALID_PATH ||
!ui64FileSize)
{
// If the control file doesn't exist, we will return
// path not found.
if (!uiHighFileNumber)
{
goto Exit;
}
uiHighFileNumber--;
rc = FERR_OK;
break;
}
goto Exit;
}
DbCopyInfo.ui64BytesToCopy += ui64FileSize;
if (uiHighFileNumber == MAX_DATA_BLOCK_FILE_NUMBER( uiDbVersion))
{
break;
}
uiHighFileNumber++;
}
// See how many rollback log files we have, and calculate
// their total size.
uiHighLogFileNumber = FIRST_LOG_BLOCK_FILE_NUMBER( uiDbVersion);
for (;;)
{
if ((RC_BAD( rc = SrcSFileHdl.getFileSize(
uiHighLogFileNumber, &ui64FileSize))) || !ui64FileSize)
{
if (rc == FERR_IO_PATH_NOT_FOUND ||
rc == FERR_IO_INVALID_PATH ||
!ui64FileSize)
{
if (uiHighLogFileNumber ==
FIRST_LOG_BLOCK_FILE_NUMBER( uiDbVersion))
{
uiHighLogFileNumber = 0;
}
else
{
uiHighLogFileNumber--;
}
rc = FERR_OK;
break;
}
goto Exit;
}
DbCopyInfo.ui64BytesToCopy += ui64FileSize;
if (uiHighLogFileNumber == MAX_LOG_BLOCK_FILE_NUMBER( uiDbVersion))
{
break;
}
uiHighLogFileNumber++;
}
// Get the sizes of the roll-forward log files
if( uiDbVersion < FLM_FILE_FORMAT_VER_4_3)
{
// For pre-4.3 versions, only need to copy one RFL file.
if (RC_BAD( rc = rflGetFileName( uiDbVersion,
pszSrcDbName, pszSrcRflDir, 1, pszActualSrcRflPath)))
{
goto Exit;
}
if (RC_BAD( rc = gv_FlmSysData.pFileSystem->openFile(
pszActualSrcRflPath,
FLM_IO_RDWR | FLM_IO_SH_DENYNONE | FLM_IO_DIRECT,
&pTmpFileHdl)))
{
if (rc == FERR_IO_PATH_NOT_FOUND ||
rc == FERR_IO_INVALID_PATH)
{
rc = FERR_OK;
}
else
{
goto Exit;
}
}
else
{
if (RC_BAD( rc = pTmpFileHdl->size( &ui64FileSize)))
{
goto Exit;
}
DbCopyInfo.ui64BytesToCopy += ui64FileSize;
pTmpFileHdl->Release();
pTmpFileHdl = NULL;
}
}
else
{
if( RC_BAD( rc = rflGetDirAndPrefix( uiDbVersion, pszSrcDbName,
pszSrcRflDir, pszActualSrcRflPath, pszSrcPrefix)))
{
goto Exit;
}
if (RC_BAD( rc = gv_FlmSysData.pFileSystem->openDir(
pszActualSrcRflPath, (char *)"*", &pDirHdl)))
{
goto Exit;
}
for (;;)
{
if( RC_BAD( rc = pDirHdl->next()))
{
if (rc == FERR_IO_NO_MORE_FILES)
{
rc = FERR_OK;
break;
}
else
{
goto Exit;
}
}
// If the current file is an RFL file, increment ui64BytesToCopy
if( rflGetFileNum( uiDbVersion, pszSrcPrefix,
pDirHdl->currentItemName(), &uiFileNumber))
{
DbCopyInfo.ui64BytesToCopy += pDirHdl->currentItemSize();
}
}
pDirHdl->Release();
pDirHdl = NULL;
}
// Close all file handles in the source and destination
SrcSFileHdl.releaseFiles( TRUE);
DestSFileHdl.releaseFiles( TRUE);
// Copy the database files.
for (uiFileNumber = 0; uiFileNumber <= uiHighFileNumber; uiFileNumber++)
{
// Get the source file path and destination file path.
if( RC_BAD( rc = SrcSFileHdl.getFilePath(
uiFileNumber, DbCopyInfo.szSrcFileName)))
{
goto Exit;
}
if( RC_BAD( rc = DestSFileHdl.getFilePath(
uiFileNumber, DbCopyInfo.szDestFileName)))
{
goto Exit;
}
// For file #0, don't copy first 2K - it will be set up to show
// maintenance in progress. Then the first 2K will be copied
// later.
if (!uiFileNumber)
{
DbCopyInfo.bNewSrcFile = TRUE;
if (RC_BAD( rc = flmCopyFile( gv_FlmSysData.pFileSystem,
2048, 0xFFFFFFFF,
&DbCopyInfo, &pCopiedList, pucInMemLogHdr, TRUE,
fnStatusCallback, UserData)))
{
goto Exit;
}
}
else
{
DbCopyInfo.bNewSrcFile = TRUE;
if (RC_BAD( rc = flmCopyFile( gv_FlmSysData.pFileSystem,
0, 0xFFFFFFFF,
&DbCopyInfo, &pCopiedList, NULL, TRUE,
fnStatusCallback, UserData)))
{
goto Exit;
}
}
}
// Copy the additional rollback log files, if any.
for (uiFileNumber = FIRST_LOG_BLOCK_FILE_NUMBER( uiDbVersion);
uiFileNumber <= uiHighLogFileNumber; uiFileNumber++)
{
// Get the source file path and destination file path.
if (RC_BAD( rc = SrcSFileHdl.getFilePath( uiFileNumber,
DbCopyInfo.szSrcFileName)))
{
goto Exit;
}
if (RC_BAD( rc = DestSFileHdl.getFilePath( uiFileNumber,
DbCopyInfo.szDestFileName)))
{
goto Exit;
}
DbCopyInfo.bNewSrcFile = TRUE;
if (RC_BAD( rc = flmCopyFile( gv_FlmSysData.pFileSystem,
0, 0xFFFFFFFF,
&DbCopyInfo, &pCopiedList, NULL, TRUE,
fnStatusCallback, UserData)))
{
goto Exit;
}
}
// Copy the RFL files
if( uiDbVersion < FLM_FILE_FORMAT_VER_4_3)
{
// Get the source file path and the destination file path.
if (RC_BAD( rc = rflGetFileName( uiDbVersion, pszSrcDbName,
pszSrcRflDir, 1,
DbCopyInfo.szSrcFileName)))
{
goto Exit;
}
if (RC_BAD( rc = rflGetFileName( uiDbVersion, pszDestDbName,
pszDestRflDir, 1,
DbCopyInfo.szDestFileName)))
{
goto Exit;
}
DbCopyInfo.bNewSrcFile = TRUE;
if (RC_BAD( rc = flmCopyFile( gv_FlmSysData.pFileSystem,
0, 0xFFFFFFFF,
&DbCopyInfo, &pCopiedList, NULL, TRUE,
fnStatusCallback, UserData)))
{
goto Exit;
}
}
else
{
// Create the destination RFL directory, if needed. The purpose of this
// code is two-fold: 1) We want to keep track of the fact that we tried
// to create the destination RFL directory so we can try to remove it
// if the copy fails; 2) If the destination RFL directory path specifies
// a directory with existing files, we want to remove them.
if( RC_BAD( rc = rflGetDirAndPrefix( uiDbVersion, pszDestDbName,
pszDestRflDir, pszActualDestRflPath, pszDestPrefix)))
{
goto Exit;
}
if( RC_OK( gv_FlmSysData.pFileSystem->doesFileExist(
pszActualDestRflPath)))
{
if( gv_FlmSysData.pFileSystem->isDir( pszActualDestRflPath))
{
// Remove the existing directory and all files, etc.
(void)gv_FlmSysData.pFileSystem->removeDir(
pszActualDestRflPath, TRUE);
}
else
{
(void)gv_FlmSysData.pFileSystem->deleteFile( pszActualDestRflPath);
}
}
// Try to create the destination RFL directory. This might fail if
// another process was accessing the directory for some reason
// (i.e., from a command prompt), when we tried to remove it above.
// We really don't care if the call to CreateDir is sucessful, because
// when we try to create the destination files (below), the FLAIM file
// file system code will try to create any necessary directories.
(void)gv_FlmSysData.pFileSystem->createDir( pszActualDestRflPath);
bCreatedDestRflDir = TRUE;
// Copy the RFL files. NOTE: We need to copy all of the RFL files
// in the source RFL directory so that they will be available
// when performing a database restore operation.
if (RC_BAD( rc = gv_FlmSysData.pFileSystem->openDir(
pszActualSrcRflPath, (char *)"*", &pDirHdl)))
{
goto Exit;
}
for (;;)
{
if( RC_BAD( rc = pDirHdl->next()))
{
if (rc == FERR_IO_NO_MORE_FILES)
{
rc = FERR_OK;
break;
}
else
{
goto Exit;
}
}
// If the current file is an RFL file, copy it to the destination
if( rflGetFileNum( uiDbVersion, pszSrcPrefix,
pDirHdl->currentItemName(), &uiFileNumber))
{
// Get the source file path and the destination file path.
if (RC_BAD( rc = rflGetFileName( uiDbVersion, pszSrcDbName,
pszSrcRflDir, uiFileNumber,
DbCopyInfo.szSrcFileName)))
{
goto Exit;
}
if (RC_BAD( rc = rflGetFileName( uiDbVersion, pszDestDbName,
pszDestRflDir, uiFileNumber,
DbCopyInfo.szDestFileName)))
{
goto Exit;
}
DbCopyInfo.bNewSrcFile = TRUE;
if (RC_BAD( rc = flmCopyFile( gv_FlmSysData.pFileSystem,
0, 0xFFFFFFFF,
&DbCopyInfo, &pCopiedList, NULL, TRUE,
fnStatusCallback, UserData)))
{
goto Exit;
}
}
}
pDirHdl->Release();
pDirHdl = NULL;
}
// Do one final copy on the control file to copy just the first 2K
if (RC_BAD( rc = SrcSFileHdl.getFilePath( 0, DbCopyInfo.szSrcFileName)))
{
goto Exit;
}
if (RC_BAD( rc = DestSFileHdl.getFilePath( 0, DbCopyInfo.szDestFileName)))
{
goto Exit;
}
DbCopyInfo.bNewSrcFile = FALSE;
if (RC_BAD( rc = flmCopyFile( gv_FlmSysData.pFileSystem,
0, 2048,
&DbCopyInfo, NULL, pucInMemLogHdr, FALSE,
fnStatusCallback, UserData)))
{
goto Exit;
}
Exit:
if (bUsedFFile)
{
if (!bMutexLocked)
{
f_mutexLock( gv_FlmSysData.hShareMutex);
bMutexLocked = TRUE;
}
if (!(--pFile->uiUseCount))
{
flmLinkFileToNUList( pFile);
}
}
if (bMutexLocked)
{
f_mutexUnlock( gv_FlmSysData.hShareMutex);
bMutexLocked = FALSE;
}
if (bWriteLocked)
{
if( RC_BAD( rc = pWriteLockObj->unlock()))
{
goto Exit;
}
bWriteLocked = FALSE;
}
if (bFileLocked)
{
RCODE rc3;
if (RC_BAD( rc3 = pFileLockObj->unlock()))
{
if (RC_OK( rc))
rc = rc3;
}
bFileLocked = FALSE;
}
if (pWriteLockObj)
{
pWriteLockObj->Release();
pWriteLockObj = NULL;
}
if (pFileLockObj)
{
pFileLockObj->Release();
pFileLockObj = NULL;
}
if (pLockFileHdl)
{
pLockFileHdl->Release();
pLockFileHdl = NULL;
}
if( pTmpFileHdl)
{
pTmpFileHdl->Release();
}
if( pDirHdl)
{
pDirHdl->Release();
}
// Free all the names of files that were copied.
// If the copy didn't finish, try to delete any files
// that were copied.
while (pCopiedList)
{
COPIED_NAME * pNext = pCopiedList->pNext;
// If the overall copy failed, delete the copied file.
if (RC_BAD( rc))
{
(void)gv_FlmSysData.pFileSystem->deleteFile( pCopiedList->szPath);
}
f_free( &pCopiedList);
pCopiedList = pNext;
}
if( RC_BAD( rc) && bCreatedDestRflDir)
{
(void)gv_FlmSysData.pFileSystem->removeDir( pszActualDestRflPath);
}
if( pszActualSrcRflPath)
{
f_free( &pszActualSrcRflPath);
}
if( hWaitSem != F_SEM_NULL)
{
f_semDestroy( &hWaitSem);
}
return( rc);
}
/****************************************************************************
Desc: Copy a file that is one of the files of the database.
*****************************************************************************/
FSTATIC RCODE flmCopyFile(
IF_FileSystem * pFileSystem,
FLMUINT uiStartOffset,
FLMUINT uiEndOffset,
DB_COPY_INFO * pDbCopyInfo,
COPIED_NAME ** ppCopiedListRV,
FLMBYTE * pucInMemLogHdr,
FLMBOOL bOkToTruncate,
STATUS_HOOK fnStatusCallback,
void * UserData)
{
RCODE rc = FERR_OK;
FLMBYTE * pucBuffer = NULL;
IF_FileHdl * pSrcFileHdl = NULL;
IF_FileHdl * pDestFileHdl = NULL;
FLMUINT uiBufferSize = 32768;
FLMUINT uiBytesToRead;
FLMUINT uiBytesRead;
FLMUINT uiBytesWritten;
FLMUINT uiOffset;
FLMBYTE ucLogHdr [LOG_HEADER_SIZE];
FLMUINT uiNewChecksum;
FLMBOOL bCreatedDestFile = FALSE;
// Open the source file.
if( RC_BAD( rc = gv_FlmSysData.pFileSystem->openFile(
pDbCopyInfo->szSrcFileName,
FLM_IO_RDWR | FLM_IO_SH_DENYNONE | FLM_IO_DIRECT,
&pSrcFileHdl)))
{
goto Exit;
}
// First attempt to open the destination file. If it does
// not exist, attempt to create it.
if (RC_BAD( rc = gv_FlmSysData.pFileSystem->openFile(
pDbCopyInfo->szDestFileName,
FLM_IO_RDWR | FLM_IO_SH_DENYNONE | FLM_IO_DIRECT,
&pDestFileHdl)))
{
if (rc != FERR_IO_PATH_NOT_FOUND &&
rc != FERR_IO_INVALID_PATH)
{
goto Exit;
}
if( RC_BAD( rc = gv_FlmSysData.pFileSystem->createFile(
pDbCopyInfo->szDestFileName,
FLM_IO_RDWR | FLM_IO_EXCL | FLM_IO_SH_DENYNONE |
FLM_IO_CREATE_DIR | FLM_IO_DIRECT,
&pDestFileHdl)))
{
goto Exit;
}
bCreatedDestFile = TRUE;
}
// Allocate a buffer for reading and writing.
if (RC_BAD( rc = f_alloc( uiBufferSize, &pucBuffer)))
{
goto Exit;
}
// If uiStartOffset is 2048, it is the special case of
// the control file, and we are not copying the first 2K.
// However, we need to set up the first 2K so that if
// someone reads the first 2K, it will return them a
// maintenance in progress error.
if (uiStartOffset == 2048)
{
// Read the first 2K of the source file.
if (RC_BAD( rc = pSrcFileHdl->sectorRead( 0L, 2048,
pucBuffer, &uiBytesRead)))
{
if (rc == FERR_IO_END_OF_FILE)
{
rc = FERR_OK;
}
else
{
goto Exit;
}
}
// Zero out whatever part of the 2K we didn't get on the read.
if (uiBytesRead < 2048)
{
f_memset( &pucBuffer [uiBytesRead], 0, (int)(2048 - uiBytesRead));
}
// Attempt to read the log header from the destination file.
// It is OK if we can't read it, because if we created the
// destination file, these bytes may not be present.
if ((bCreatedDestFile) ||
(RC_BAD( pDestFileHdl->read( 16L, LOG_HEADER_SIZE,
ucLogHdr, &uiBytesRead))))
{
f_memset( ucLogHdr, 0, sizeof(ucLogHdr));
}
/*
Set the transaction ID to zero. MUST ALSO SET THE TRANS ACTIVE FLAG
TO FALSE - OTHERWISE READERS WILL ATTEMPT TO DECREMENT THE
TRANSACTION ID AND WILL END UP WITH 0xFFFFFFFF - very bad!
We must use zero, because it is the only transaction ID that will not
appear on ANY block.
*/
UD2FBA( 0L, &ucLogHdr [LOG_CURR_TRANS_ID]);
/*
Recalculate the log header checksum so that readers will not get a
checksum error.
*/
uiNewChecksum = lgHdrCheckSum( ucLogHdr, FALSE);
UW2FBA( (FLMUINT16)uiNewChecksum, &ucLogHdr [LOG_HDR_CHECKSUM]);
f_memcpy( &pucBuffer [16], ucLogHdr, LOG_HEADER_SIZE);
// Write this "special" first 2K into the destination file.
// The real first 2K from the source file will be copied in
// at a later time.
if (RC_BAD( rc = pDestFileHdl->write( 0L, 2048,
pucBuffer, &uiBytesWritten)))
{
goto Exit;
}
// Save the log header to the in-memory version of the log
// header as well - if pucInMemLogHdr is NULL, it is pointing
// to the pFile->ucLastCommittedLogHdr buffer.
if (pucInMemLogHdr)
{
f_memcpy( pucInMemLogHdr, ucLogHdr, LOG_HEADER_SIZE);
}
}
// Read from source file until we hit EOF in the file or
// we hit the end offset.
uiOffset = uiStartOffset;
for (;;)
{
uiBytesToRead = (FLMUINT)((uiEndOffset - uiOffset >=
uiBufferSize)
? uiBufferSize
: (FLMUINT)(uiEndOffset - uiOffset));
// Read data from source file.
if (RC_BAD( rc = pSrcFileHdl->sectorRead( uiOffset, uiBytesToRead,
pucBuffer, &uiBytesRead)))
{
if (rc == FERR_IO_END_OF_FILE)
{
rc = FERR_OK;
if (!uiBytesRead)
{
break;
}
}
else
{
goto Exit;
}
}
// Write data to destination file.
if (RC_BAD( rc = pDestFileHdl->write( uiOffset,
uiBytesRead, pucBuffer, &uiBytesWritten)))
{
goto Exit;
}
// See if we wrote out the buffer that has the log header
// If so, we need to copy it back to the in-memory log
// header.
if ((pucInMemLogHdr) &&
(uiOffset <= 16) &&
(uiOffset + uiBytesWritten >= 16 + LOG_HEADER_SIZE))
{
f_memcpy( pucInMemLogHdr, &pucBuffer [16 - uiOffset],
LOG_HEADER_SIZE);
}
uiOffset += uiBytesWritten;
// Do callback to report progress.
if (fnStatusCallback)
{
pDbCopyInfo->ui64BytesCopied += (FLMUINT64)uiBytesWritten;
if (RC_BAD( rc = (*fnStatusCallback)( FLM_DB_COPY_STATUS,
(void *)pDbCopyInfo,
(void *)0, UserData)))
{
goto Exit;
}
pDbCopyInfo->bNewSrcFile = FALSE;
}
// Quit once we reach the end offset or we read fewer bytes
// than we asked for.
if (uiOffset >= uiEndOffset || uiBytesRead < uiBytesToRead)
break;
}
// If we overwrote the destination file, as opposed to creating
// it, truncate it in case it was larger than the number of
// bytes we actually copied.
if (!bCreatedDestFile && bOkToTruncate)
{
if (RC_BAD( rc = pDestFileHdl->truncate( uiOffset)))
{
goto Exit;
}
}
// If the copy succeeded, add the destination name to a list
// of destination files. This is done so we can clean up
// copied files if we fail somewhere in the overall database
// copy.
if (ppCopiedListRV)
{
COPIED_NAME * pCopyName;
if( RC_BAD( rc = f_alloc(
(FLMUINT)sizeof( COPIED_NAME), &pCopyName)))
{
goto Exit;
}
f_strcpy( pCopyName->szPath, pDbCopyInfo->szDestFileName);
pCopyName->pNext = *ppCopiedListRV;
*ppCopiedListRV = pCopyName;
}
Exit:
if (pucBuffer)
{
f_free( &pucBuffer);
}
if (pSrcFileHdl)
{
pSrcFileHdl->Release();
}
if (pDestFileHdl)
{
pDestFileHdl->flush();
pDestFileHdl->Release();
}
// Attempt to delete the destination file if
// we didn't successfully copy it.
if (RC_BAD( rc))
{
(void)pFileSystem->deleteFile( pDbCopyInfo->szDestFileName);
}
return( rc);
}