git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@486 0109f412-320b-0410-ab79-c3e0c5ffbbe6
886 lines
20 KiB
C++
886 lines
20 KiB
C++
//------------------------------------------------------------------------------
|
|
// Desc: This file contains the F_DbSystem::dbCopy method.
|
|
//
|
|
// 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: fdbcopy.cpp 3112 2006-01-19 13:12:40 -0700 (Thu, 19 Jan 2006) dsanders $
|
|
//------------------------------------------------------------------------------
|
|
|
|
#include "flaimsys.h"
|
|
|
|
// Local prototypes
|
|
|
|
typedef struct Copied_Name * COPIED_NAME_p;
|
|
|
|
typedef struct Copied_Name
|
|
{
|
|
char szPath[ F_PATH_MAX_SIZE];
|
|
COPIED_NAME_p pNext;
|
|
} COPIED_NAME;
|
|
|
|
typedef struct
|
|
{
|
|
FLMUINT64 ui64BytesToCopy;
|
|
FLMUINT64 ui64BytesCopied;
|
|
FLMBOOL bNewSrcFile;
|
|
char szSrcFileName[ F_PATH_MAX_SIZE];
|
|
char szDestFileName[ F_PATH_MAX_SIZE];
|
|
} DB_COPY_INFO, * DB_COPY_INFO_p;
|
|
|
|
|
|
FSTATIC RCODE flmCopyFile(
|
|
DB_COPY_INFO * pDbCopyInfo,
|
|
COPIED_NAME ** ppCopiedListRV,
|
|
FLMBOOL bOkToTruncate,
|
|
IF_DbCopyStatus * ifpStatus);
|
|
|
|
/****************************************************************************
|
|
Desc: Copies a database, including roll-forward log files.
|
|
****************************************************************************/
|
|
RCODE F_DbSystem::dbCopy(
|
|
const char * pszSrcDbName,
|
|
// [IN] Name of source database to be copied.
|
|
const char * pszSrcDataDir,
|
|
// [IN] Name of source data directory.
|
|
const char * pszSrcRflDir,
|
|
// [IN] RFL directory of source database. NULL can be
|
|
// passed to indicate that the log files are located
|
|
// in the same directory as the other database files.
|
|
const char * pszDestDbName,
|
|
// [IN] Destination name of database - will be overwritten if it
|
|
// already exists.
|
|
const char * pszDestDataDir,
|
|
// [IN] Name of destination data directory.
|
|
const char * pszDestRflDir,
|
|
// [IN] RFL directory of destination database. NULL can be
|
|
// passed to indicate that the log files are to be located
|
|
// in the same directory as the other database files.
|
|
IF_DbCopyStatus * ifpStatus)
|
|
// [IN] Status callback interface.
|
|
{
|
|
RCODE rc = NE_XFLM_OK;
|
|
IF_Db * pDb = NULL;
|
|
FLMBOOL bDbLocked = FALSE;
|
|
|
|
// Make sure the destination database is closed
|
|
|
|
if (RC_BAD( rc = checkDatabaseClosed( pszDestDbName, pszDestDataDir)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Open the source database so we can force a checkpoint.
|
|
|
|
if (RC_BAD( rc = openDb( pszSrcDbName, pszSrcDataDir, pszSrcRflDir,
|
|
NULL, 0, &pDb)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// 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 = pDb->dbLock( FLM_LOCK_EXCLUSIVE, 0, FLM_NO_TIMEOUT)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
bDbLocked = TRUE;
|
|
|
|
// Force a checkpoint
|
|
|
|
if (RC_BAD( rc = pDb->doCheckpoint( FLM_NO_TIMEOUT)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// 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 = copyDb( pszSrcDbName, pszSrcDataDir, pszSrcRflDir,
|
|
pszDestDbName, pszDestDataDir, pszDestRflDir,
|
|
ifpStatus);
|
|
|
|
Exit:
|
|
|
|
// Unlock and close the database
|
|
|
|
if (bDbLocked)
|
|
{
|
|
pDb->dbUnlock();
|
|
}
|
|
|
|
if (pDb)
|
|
{
|
|
pDb->Release();
|
|
}
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Copy a database's files, including roll-forward log files.
|
|
*****************************************************************************/
|
|
RCODE F_DbSystem::copyDb(
|
|
const char * pszSrcDbName,
|
|
const char * pszSrcDataDir,
|
|
const char * pszSrcRflDir,
|
|
const char * pszDestDbName,
|
|
const char * pszDestDataDir,
|
|
const char * pszDestRflDir,
|
|
IF_DbCopyStatus * ifpStatus)
|
|
{
|
|
RCODE rc = NE_XFLM_OK;
|
|
DB_COPY_INFO DbCopyInfo;
|
|
F_SuperFileHdl SrcSFileHdl;
|
|
F_SuperFileHdl DestSFileHdl;
|
|
FLMUINT uiFileNumber;
|
|
FLMUINT uiHighFileNumber;
|
|
FLMUINT uiHighLogFileNumber;
|
|
FLMUINT64 ui64FileSize;
|
|
F_Database * pDatabase = NULL;
|
|
FLMBOOL bMutexLocked = FALSE;
|
|
IF_FileHdl * pLockFileHdl = NULL;
|
|
IF_FileHdl * pTmpFileHdl = NULL;
|
|
IF_DirHdl * pDirHdl = NULL;
|
|
FLMBOOL bDatabaseLocked = FALSE;
|
|
FLMBOOL bWriteLocked = FALSE;
|
|
IF_LockObject * pWriteLockObj = NULL;
|
|
IF_LockObject * pDatabaseLockObj = NULL;
|
|
COPIED_NAME * pCopiedList = NULL;
|
|
FLMBOOL bUsedDatabase = FALSE;
|
|
eLockType currLockType;
|
|
FLMUINT uiThreadId;
|
|
FLMUINT uiNumExclQueued;
|
|
FLMUINT uiNumSharedQueued;
|
|
FLMUINT uiPriorityCount;
|
|
char * pszActualSrcRflPath = NULL;
|
|
char * pszActualDestRflPath = NULL;
|
|
FLMBOOL bCreatedDestRflDir = FALSE;
|
|
FLMBOOL bWaited;
|
|
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;
|
|
}
|
|
|
|
// Create a "wait" 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 * 2,
|
|
&pszActualSrcRflPath)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
pszActualDestRflPath = &pszActualSrcRflPath[ 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)))
|
|
{
|
|
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_XFlmSysData.hShareMutex);
|
|
bMutexLocked = TRUE;
|
|
|
|
retry:
|
|
|
|
if (RC_BAD( rc = F_DbSystem::findDatabase( pszDestDbName,
|
|
pszDestDataDir, &pDatabase)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// If we didn't find an FFILE structure, get an
|
|
// exclusive lock on the file.
|
|
|
|
if (!pDatabase)
|
|
{
|
|
f_mutexUnlock( gv_XFlmSysData.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 verifyOkToUse will wait if the database is in
|
|
// the process of being opened by another thread.
|
|
|
|
if (RC_BAD( rc = pDatabase->verifyOkToUse( &bWaited)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (bWaited)
|
|
{
|
|
goto retry;
|
|
}
|
|
|
|
// Increment the open count on the F_Database object so it will not
|
|
// disappear while we are copying the database.
|
|
|
|
pDatabase->incrOpenCount();
|
|
bUsedDatabase = TRUE;
|
|
|
|
f_mutexUnlock( gv_XFlmSysData.hShareMutex);
|
|
bMutexLocked = FALSE;
|
|
|
|
// Lock the destination file object and transaction
|
|
// object, if not already locked.
|
|
|
|
pDatabase->m_pDatabaseLockObj->getLockInfo( (FLMINT)0,
|
|
&currLockType,
|
|
&uiThreadId, &uiNumExclQueued,
|
|
&uiNumSharedQueued,
|
|
&uiPriorityCount);
|
|
if (currLockType != FLM_LOCK_EXCLUSIVE ||
|
|
uiThreadId != f_threadId())
|
|
{
|
|
pDatabaseLockObj = pDatabase->m_pDatabaseLockObj;
|
|
pDatabaseLockObj->AddRef();
|
|
|
|
if (RC_BAD( rc = pDatabaseLockObj->lock(
|
|
hWaitSem, TRUE, FLM_NO_TIMEOUT, 0)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
bDatabaseLocked = TRUE;
|
|
}
|
|
|
|
// Lock the write object, if not already locked
|
|
|
|
pDatabase->m_pWriteLockObj->getLockInfo( (FLMINT)0,
|
|
&currLockType,
|
|
&uiThreadId, &uiNumExclQueued,
|
|
&uiNumSharedQueued,
|
|
&uiPriorityCount);
|
|
if (currLockType != FLM_LOCK_EXCLUSIVE ||
|
|
uiThreadId != (FLMUINT)f_threadId())
|
|
{
|
|
pWriteLockObj = pDatabase->m_pWriteLockObj;
|
|
pWriteLockObj->AddRef();
|
|
|
|
// Only contention here is with the checkpoint thread - wait
|
|
// forever until the checkpoint thread gives it up.
|
|
|
|
if (RC_BAD( rc = pDatabase->dbWriteLock( hWaitSem)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
bWriteLocked = TRUE;
|
|
}
|
|
}
|
|
|
|
// Set up the super file object for the destination database.
|
|
|
|
if (RC_BAD( rc = DestSFileHdl.setup( pszDestDbName, pszDestDataDir)))
|
|
{
|
|
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 == NE_FLM_IO_PATH_NOT_FOUND ||
|
|
rc == NE_FLM_IO_INVALID_FILENAME ||
|
|
!ui64FileSize)
|
|
{
|
|
// If the control file doesn't exist, we will return
|
|
// path not found.
|
|
|
|
if (!uiHighFileNumber)
|
|
{
|
|
goto Exit;
|
|
}
|
|
uiHighFileNumber--;
|
|
rc = NE_XFLM_OK;
|
|
break;
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
DbCopyInfo.ui64BytesToCopy += ui64FileSize;
|
|
if (uiHighFileNumber == MAX_DATA_BLOCK_FILE_NUMBER)
|
|
{
|
|
break;
|
|
}
|
|
uiHighFileNumber++;
|
|
}
|
|
|
|
// See how many rollback log files we have, and calculate
|
|
// their total size.
|
|
|
|
uiHighLogFileNumber = FIRST_LOG_BLOCK_FILE_NUMBER;
|
|
for (;;)
|
|
{
|
|
if ((RC_BAD( rc = SrcSFileHdl.getFileSize(
|
|
uiHighLogFileNumber, &ui64FileSize))) || !ui64FileSize)
|
|
{
|
|
if (rc == NE_FLM_IO_PATH_NOT_FOUND ||
|
|
rc == NE_FLM_IO_INVALID_FILENAME ||
|
|
!ui64FileSize)
|
|
{
|
|
if (uiHighLogFileNumber ==
|
|
FIRST_LOG_BLOCK_FILE_NUMBER)
|
|
{
|
|
uiHighLogFileNumber = 0;
|
|
}
|
|
else
|
|
{
|
|
uiHighLogFileNumber--;
|
|
}
|
|
rc = NE_XFLM_OK;
|
|
break;
|
|
}
|
|
goto Exit;
|
|
}
|
|
|
|
DbCopyInfo.ui64BytesToCopy += ui64FileSize;
|
|
if (uiHighLogFileNumber == MAX_LOG_BLOCK_FILE_NUMBER)
|
|
{
|
|
break;
|
|
}
|
|
uiHighLogFileNumber++;
|
|
}
|
|
|
|
// Get the sizes of the roll-forward log files
|
|
|
|
if (RC_BAD( rc = rflGetDirAndPrefix( pszSrcDbName,
|
|
pszSrcRflDir, pszActualSrcRflPath)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (RC_BAD( rc = gv_XFlmSysData.pFileSystem->openDir(
|
|
pszActualSrcRflPath, (char *)"*", &pDirHdl)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
for (;;)
|
|
{
|
|
if (RC_BAD( rc = pDirHdl->next()))
|
|
{
|
|
if (rc == NE_FLM_IO_NO_MORE_FILES)
|
|
{
|
|
rc = NE_XFLM_OK;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// If the current file is an RFL file, increment ui64BytesToCopy
|
|
|
|
if (rflGetFileNum( pDirHdl->currentItemName(), &uiFileNumber))
|
|
{
|
|
DbCopyInfo.ui64BytesToCopy += (FLMUINT64)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;
|
|
}
|
|
|
|
DbCopyInfo.bNewSrcFile = TRUE;
|
|
if (RC_BAD( rc = flmCopyFile(
|
|
&DbCopyInfo, &pCopiedList, TRUE, ifpStatus)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Copy the additional rollback log files, if any.
|
|
|
|
for (uiFileNumber = FIRST_LOG_BLOCK_FILE_NUMBER;
|
|
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(
|
|
&DbCopyInfo, &pCopiedList, TRUE, ifpStatus)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Copy the RFL files
|
|
|
|
// 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( pszDestDbName,
|
|
pszDestRflDir, pszActualDestRflPath)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( RC_OK( gv_XFlmSysData.pFileSystem->doesFileExist( pszActualDestRflPath)))
|
|
{
|
|
if( gv_XFlmSysData.pFileSystem->isDir( pszActualDestRflPath))
|
|
{
|
|
// Remove the existing directory and all files, etc.
|
|
|
|
(void)gv_XFlmSysData.pFileSystem->removeDir(
|
|
pszActualDestRflPath, TRUE);
|
|
}
|
|
else
|
|
{
|
|
(void)gv_XFlmSysData.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_XFlmSysData.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_XFlmSysData.pFileSystem->openDir(
|
|
pszActualSrcRflPath, (char *)"*", &pDirHdl)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
for (;;)
|
|
{
|
|
if( RC_BAD( rc = pDirHdl->next()))
|
|
{
|
|
if (rc == NE_FLM_IO_NO_MORE_FILES)
|
|
{
|
|
rc = NE_XFLM_OK;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// If the current file is an RFL file, copy it to the destination
|
|
|
|
if( rflGetFileNum( pDirHdl->currentItemName(), &uiFileNumber))
|
|
{
|
|
// Get the source file path and the destination file path.
|
|
|
|
if (RC_BAD( rc = rflGetFileName( pszSrcDbName,
|
|
pszSrcRflDir, uiFileNumber,
|
|
DbCopyInfo.szSrcFileName)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (RC_BAD( rc = rflGetFileName( pszDestDbName,
|
|
pszDestRflDir, uiFileNumber,
|
|
DbCopyInfo.szDestFileName)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
DbCopyInfo.bNewSrcFile = TRUE;
|
|
if (RC_BAD( rc = flmCopyFile(
|
|
&DbCopyInfo, &pCopiedList, TRUE, ifpStatus)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
pDirHdl->Release();
|
|
pDirHdl = NULL;
|
|
|
|
Exit:
|
|
|
|
if (bUsedDatabase)
|
|
{
|
|
if (!bMutexLocked)
|
|
{
|
|
f_mutexLock( gv_XFlmSysData.hShareMutex);
|
|
bMutexLocked = TRUE;
|
|
}
|
|
pDatabase->decrOpenCount();
|
|
}
|
|
|
|
if (bMutexLocked)
|
|
{
|
|
f_mutexUnlock( gv_XFlmSysData.hShareMutex);
|
|
bMutexLocked = FALSE;
|
|
}
|
|
|
|
// Unlock the database, if it is locked.
|
|
|
|
if (bWriteLocked)
|
|
{
|
|
pDatabase->dbWriteUnlock();
|
|
bWriteLocked = FALSE;
|
|
}
|
|
|
|
if (bDatabaseLocked)
|
|
{
|
|
RCODE rc3;
|
|
|
|
if (RC_BAD( rc3 = pDatabaseLockObj->unlock()))
|
|
{
|
|
if (RC_OK( rc))
|
|
rc = rc3;
|
|
}
|
|
|
|
bDatabaseLocked = FALSE;
|
|
}
|
|
|
|
if (pWriteLockObj)
|
|
{
|
|
pWriteLockObj->Release();
|
|
pWriteLockObj = NULL;
|
|
}
|
|
|
|
if (pDatabaseLockObj)
|
|
{
|
|
pDatabaseLockObj->Release();
|
|
pDatabaseLockObj = NULL;
|
|
}
|
|
|
|
if (pLockFileHdl)
|
|
{
|
|
(void)pLockFileHdl->close();
|
|
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_p pNext = pCopiedList->pNext;
|
|
|
|
// If the overall copy failed, delete the copied file.
|
|
|
|
if (RC_BAD( rc))
|
|
{
|
|
(void)gv_XFlmSysData.pFileSystem->deleteFile( pCopiedList->szPath);
|
|
}
|
|
|
|
f_free( &pCopiedList);
|
|
pCopiedList = pNext;
|
|
}
|
|
|
|
if (RC_BAD( rc) && bCreatedDestRflDir)
|
|
{
|
|
(void)gv_XFlmSysData.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(
|
|
DB_COPY_INFO * pDbCopyInfo,
|
|
COPIED_NAME ** ppCopiedListRV,
|
|
FLMBOOL bOkToTruncate,
|
|
IF_DbCopyStatus * ifpStatus)
|
|
{
|
|
RCODE rc = NE_XFLM_OK;
|
|
FLMBYTE * pucBuffer = NULL;
|
|
IF_FileHdl * pSrcFileHdl = NULL;
|
|
IF_FileHdl * pDestFileHdl = NULL;
|
|
FLMUINT uiBufferSize = 32768;
|
|
FLMUINT uiBytesToRead;
|
|
FLMUINT uiBytesRead;
|
|
FLMUINT uiBytesWritten;
|
|
FLMUINT uiOffset;
|
|
FLMBOOL bCreatedDestFile = FALSE;
|
|
|
|
// Open the source file.
|
|
|
|
if( RC_BAD( rc = gv_XFlmSysData.pFileSystem->openFile(
|
|
pDbCopyInfo->szSrcFileName,
|
|
FLM_IO_RDWR | FLM_IO_SH_DENYNONE | FLM_IO_DIRECT,
|
|
&pSrcFileHdl)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Get a file handle for the destination file.
|
|
// First attempt to open the destination file. If it does
|
|
// not exist, attempt to create it.
|
|
|
|
if (RC_BAD( rc = gv_XFlmSysData.pFileSystem->openFile( pDbCopyInfo->szDestFileName,
|
|
FLM_IO_RDWR | FLM_IO_SH_DENYNONE | FLM_IO_DIRECT,
|
|
&pDestFileHdl)))
|
|
{
|
|
if (rc != NE_FLM_IO_PATH_NOT_FOUND &&
|
|
rc != NE_FLM_IO_INVALID_FILENAME)
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( RC_BAD( rc = gv_XFlmSysData.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;
|
|
}
|
|
|
|
// Read from source file until we hit EOF in the file or
|
|
// we hit the end offset.
|
|
|
|
uiOffset = 0;
|
|
for (;;)
|
|
{
|
|
uiBytesToRead = (FLMUINT)((0xFFFFFFFF - uiOffset >=
|
|
uiBufferSize)
|
|
? uiBufferSize
|
|
: (FLMUINT)(0xFFFFFFFF - uiOffset));
|
|
|
|
// Read data from source file.
|
|
|
|
if (RC_BAD( rc = pSrcFileHdl->sectorRead( uiOffset, uiBytesToRead,
|
|
pucBuffer, &uiBytesRead)))
|
|
{
|
|
if (rc == NE_FLM_IO_END_OF_FILE)
|
|
{
|
|
rc = NE_XFLM_OK;
|
|
if (!uiBytesRead)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Write data to destination file.
|
|
|
|
if (RC_BAD( rc = pDestFileHdl->write( uiOffset,
|
|
uiBytesRead, pucBuffer, &uiBytesWritten)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Do callback to report progress.
|
|
|
|
if (ifpStatus)
|
|
{
|
|
pDbCopyInfo->ui64BytesCopied += (FLMUINT64)uiBytesWritten;
|
|
if (RC_BAD( rc = ifpStatus->dbCopyStatus(
|
|
pDbCopyInfo->ui64BytesToCopy,
|
|
pDbCopyInfo->ui64BytesCopied,
|
|
pDbCopyInfo->bNewSrcFile,
|
|
pDbCopyInfo->szSrcFileName,
|
|
pDbCopyInfo->szDestFileName)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
pDbCopyInfo->bNewSrcFile = FALSE;
|
|
}
|
|
|
|
if (0xFFFFFFFF - uiBytesWritten < uiOffset)
|
|
{
|
|
uiOffset = 0xFFFFFFFF;
|
|
break;
|
|
}
|
|
|
|
uiOffset += uiBytesWritten;
|
|
|
|
// Quit once we reach the end offset or we read fewer bytes
|
|
// than we asked for.
|
|
|
|
if (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_p 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)gv_XFlmSysData.pFileSystem->deleteFile( pDbCopyInfo->szDestFileName);
|
|
}
|
|
|
|
return( rc);
|
|
}
|