Files
mars-flaim/flaim/src/fsuperfl.cpp

1326 lines
31 KiB
C++

//-------------------------------------------------------------------------
// Desc: Super-file class implementation.
// Tabs: 3
//
// Copyright (c) 1998-2003,2005-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: fsuperfl.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $
//-------------------------------------------------------------------------
#include "flaimsys.h"
FSTATIC FLMBYTE base24ToDigit(
FLMUINT uiBaseValue);
/****************************************************************************
Public: F_FileIdList
Desc: Constructor
****************************************************************************/
F_FileIdList::F_FileIdList()
{
m_hMutex = F_MUTEX_NULL;
m_uiFileIdTblSize = 0;
m_puiFileIdTbl = NULL;
}
/****************************************************************************
Public: ~F_FileIdList
Desc: Destructor
****************************************************************************/
F_FileIdList::~F_FileIdList()
{
if( m_hMutex != F_MUTEX_NULL)
{
f_mutexDestroy( &m_hMutex);
}
if( m_puiFileIdTbl)
{
for( FLMUINT uiLoop = 0; uiLoop < m_uiFileIdTblSize; uiLoop++)
{
if( m_puiFileIdTbl[ uiLoop])
{
(void)gv_FlmSysData.pFileHdlMgr->Remove(
m_puiFileIdTbl[ uiLoop]);
}
}
f_free( &m_puiFileIdTbl);
}
}
/****************************************************************************
Public: setup
Desc: Allocates the mutex used by the file ID list object
****************************************************************************/
RCODE F_FileIdList::setup( void)
{
RCODE rc = FERR_OK;
flmAssert( m_hMutex == F_MUTEX_NULL);
if( RC_BAD( rc = f_mutexCreate( &m_hMutex)))
{
goto Exit;
}
Exit:
return( rc);
}
/****************************************************************************
Public: getFileId
Desc: Translates a database file number into a file ID.
****************************************************************************/
RCODE F_FileIdList::getFileId(
FLMUINT uiFileNumber,
FLMUINT * puiFileId)
{
RCODE rc = FERR_OK;
FLMBOOL bMutexLocked = TRUE;
FLMUINT uiLoop = 0;
f_mutexLock( m_hMutex);
bMutexLocked = TRUE;
if( uiFileNumber >= m_uiFileIdTblSize)
{
FLMUINT uiOldTableSize = m_uiFileIdTblSize;
/*
Re-size the table
*/
if( RC_BAD( rc = f_recalloc( (uiFileNumber + 1) * sizeof( FLMUINT),
&m_puiFileIdTbl)))
{
goto Exit;
}
m_uiFileIdTblSize = uiFileNumber + 1;
for( uiLoop = uiOldTableSize; uiLoop < m_uiFileIdTblSize; uiLoop++)
{
m_puiFileIdTbl[ uiLoop] = gv_FlmSysData.pFileHdlMgr->GetUniqueId();
}
}
*puiFileId = m_puiFileIdTbl[ uiFileNumber];
Exit:
if( bMutexLocked)
{
f_mutexUnlock( m_hMutex);
}
return( rc);
}
/****************************************************************************
Public: F_SuperFileHdl
Desc: Constructor
****************************************************************************/
F_SuperFileHdl::F_SuperFileHdl( void)
{
m_pFileIdList = NULL;
m_pszDbFileName = NULL;
m_pszDataFileNameBase = NULL;
f_memset( &m_CheckedOutFileHdls[ 0], 0, sizeof( m_CheckedOutFileHdls));
m_pCheckedOutFileHdls = &m_CheckedOutFileHdls [0];
m_uiCkoArraySize = MAX_CHECKED_OUT_FILE_HDLS + 1;
m_uiBlockSize = 0;
m_uiExtendSize = DEFAULT_FILE_EXTEND_SIZE;
m_uiMaxAutoExtendSize = gv_FlmSysData.uiMaxFileSize;
m_uiDbVersion = 0;
m_uiLowestDirtySlot = 1;
m_uiHighestDirtySlot = 0;
m_uiHighestUsedSlot = 0;
m_uiHighestFileNumber = 0;
m_pECacheMgr = NULL;
m_bMinimizeFlushes = FALSE;
m_bSetupCalled = FALSE;
}
/****************************************************************************
Public: F_SuperFileHdl
Desc: Destructor
****************************************************************************/
F_SuperFileHdl::~F_SuperFileHdl()
{
/*
Release any file handles still being held and close the files.
*/
if( m_bSetupCalled)
{
(void)ReleaseFiles( TRUE);
}
/*
Release the ID list
*/
if( m_pFileIdList)
{
m_pFileIdList->Release();
}
if (m_pszDbFileName)
{
f_free( &m_pszDbFileName);
}
}
/****************************************************************************
Public: Setup
Desc: Configures the super file object
****************************************************************************/
RCODE F_SuperFileHdl::Setup(
F_FileIdList * pFileIdList,
const char * pszDbFileName,
const char * pszDataDir)
{
RCODE rc = FERR_OK;
FLMUINT uiNameLen;
FLMUINT uiDataNameLen;
char szDir[ F_PATH_MAX_SIZE];
char szBaseName[ F_FILENAME_SIZE];
flmAssert( !m_bSetupCalled);
if( !pszDbFileName && *pszDbFileName == 0)
{
rc = RC_SET( FERR_IO_INVALID_PATH);
goto Exit;
}
if( !pFileIdList)
{
if( (m_pFileIdList = f_new F_FileIdList) == NULL)
{
rc = RC_SET( FERR_MEM);
goto Exit;
}
if( RC_BAD( rc = m_pFileIdList->setup()))
{
FLMINT iRefCnt;
iRefCnt = m_pFileIdList->Release();
flmAssert( !iRefCnt);
m_pFileIdList = NULL;
goto Exit;
}
}
else
{
pFileIdList->AddRef();
m_pFileIdList = pFileIdList;
}
uiNameLen = f_strlen( pszDbFileName);
if (pszDataDir && *pszDataDir)
{
if (RC_BAD( rc = f_pathReduce( pszDbFileName, szDir, szBaseName)))
{
goto Exit;
}
f_strcpy( szDir, pszDataDir);
if (RC_BAD( rc = f_pathAppend( szDir, szBaseName)))
{
goto Exit;
}
uiDataNameLen = f_strlen( szDir);
if (RC_BAD( rc = f_alloc( (uiNameLen + 1) + (uiDataNameLen + 1),
&m_pszDbFileName)))
{
goto Exit;
}
f_memcpy( m_pszDbFileName, pszDbFileName, uiNameLen + 1);
m_pszDataFileNameBase = m_pszDbFileName + uiNameLen + 1;
flmGetDbBasePath( m_pszDataFileNameBase, szDir, &m_uiDataExtOffset);
m_uiExtOffset = uiNameLen - (uiDataNameLen - m_uiDataExtOffset);
}
else
{
if (RC_BAD( rc = f_alloc( (uiNameLen + 1) * 2, &m_pszDbFileName)))
{
goto Exit;
}
f_memcpy( m_pszDbFileName, pszDbFileName, uiNameLen + 1);
m_pszDataFileNameBase = m_pszDbFileName + uiNameLen + 1;
flmGetDbBasePath( m_pszDataFileNameBase,
m_pszDbFileName, &m_uiDataExtOffset);
m_uiExtOffset = m_uiDataExtOffset;
}
m_bSetupCalled = TRUE;
Exit:
return( rc);
}
/****************************************************************************
Public: CreateFile
Desc: Creates a file
****************************************************************************/
RCODE F_SuperFileHdl::CreateFile(
FLMUINT uiFileNumber)
{
RCODE rc = FERR_OK;
char szFilePath[ F_PATH_MAX_SIZE];
F_FileHdlImp * pFileHdl = NULL;
FLMUINT uiFileId;
flmAssert( m_bSetupCalled && m_uiDbVersion && m_uiBlockSize);
flmAssert( uiFileNumber <= MAX_LOG_BLOCK_FILE_NUMBER ( m_uiDbVersion));
// See if we already have an open file handle (or if we can open the file).
// If so, truncate the file and use it.
if( RC_OK( rc = GetFileHdl( uiFileNumber, TRUE, &pFileHdl)))
{
rc = pFileHdl->Truncate( 0);
pFileHdl = NULL;
goto Exit;
}
else if( rc != FERR_IO_PATH_NOT_FOUND)
{
goto Exit;
}
// The file was not found above. Allocate a new file handle.
if( (pFileHdl = f_new F_FileHdlImp) == NULL)
{
rc = RC_SET( FERR_MEM);
goto Exit;
}
#ifdef FLM_WIN
pFileHdl->SetBlockSize( m_uiBlockSize);
#endif
// Configure the file handle.
if( RC_BAD( rc = m_pFileIdList->getFileId( uiFileNumber, &uiFileId)))
{
goto Exit;
}
flmAssert( uiFileId); // File ID should always be non-zero
if( RC_BAD( rc = pFileHdl->Setup( uiFileId)))
{
goto Exit;
}
// Build the file path
if( RC_BAD( rc = GetFilePath( uiFileNumber, szFilePath)))
{
goto Exit;
}
if( RC_BAD( rc = pFileHdl->Create( szFilePath,
F_IO_RDWR | F_IO_EXCL | F_IO_SH_DENYNONE | F_IO_DIRECT)))
{
goto Exit;
}
// Insert into the file handle manager
if( RC_BAD( rc = gv_FlmSysData.pFileHdlMgr->InsertNew( pFileHdl)))
{
goto Exit;
}
Exit:
if( pFileHdl)
{
pFileHdl->Release();
}
return( rc);
}
/****************************************************************************
Public: ReadBlock
Desc: Reads a database block into a buffer
****************************************************************************/
RCODE F_SuperFileHdl::ReadBlock(
FLMUINT uiBlkAddress,
FLMUINT uiBytesToRead,
void * pvBuffer,
FLMUINT * puiBytesRead)
{
RCODE rc = FERR_OK;
F_FileHdlImp * pFileHdl = NULL;
flmAssert( m_bSetupCalled && m_uiDbVersion && m_uiBlockSize);
if( m_pECacheMgr)
{
flmAssert( uiBytesToRead <= m_uiBlockSize);
if( RC_OK( rc = m_pECacheMgr->getBlock( uiBlkAddress,
(FLMBYTE *)pvBuffer, uiBytesToRead)))
{
if( puiBytesRead)
{
*puiBytesRead = uiBytesToRead;
}
goto Exit;
}
else if( rc != FERR_NOT_FOUND)
{
goto Exit;
}
else
{
// Drop through and read the block from disk.
rc = FERR_OK;
}
}
if( RC_BAD( rc = GetFileHdl(
FSGetFileNumber( uiBlkAddress), FALSE, &pFileHdl)))
{
goto Exit;
}
if( RC_BAD( rc = pFileHdl->SectorRead(
FSGetFileOffset( uiBlkAddress), uiBytesToRead,
pvBuffer, puiBytesRead)))
{
if (rc != FERR_IO_END_OF_FILE && rc != FERR_MEM)
{
ReleaseFile( FSGetFileNumber( uiBlkAddress), TRUE);
}
goto Exit;
}
if( m_pECacheMgr)
{
m_pECacheMgr->putBlock( uiBlkAddress, (FLMBYTE *)pvBuffer);
}
Exit:
return( rc);
}
/****************************************************************************
Public: WriteBlock
Desc: Writes a block to the database
****************************************************************************/
RCODE F_SuperFileHdl::WriteBlock(
FLMUINT uiBlkAddress,
FLMUINT uiBytesToWrite,
void * pvBuffer,
FLMUINT uiBufferSize,
F_IOBuffer * pIOBuffer,
FLMUINT * puiBytesWritten)
{
RCODE rc = FERR_OK;
FLMUINT uiLoop;
F_FileHdlImp * pFileHdl = NULL;
FLMBYTE * pucBlk;
flmAssert( m_bSetupCalled && m_uiDbVersion && m_uiBlockSize);
if( RC_BAD( rc = GetFileHdl(
FSGetFileNumber( uiBlkAddress), TRUE, &pFileHdl)))
{
goto Exit;
}
pFileHdl->setExtendSize( m_uiExtendSize);
pFileHdl->setMaxAutoExtendSize( m_uiMaxAutoExtendSize);
if( RC_BAD( rc = pFileHdl->SectorWrite(
FSGetFileOffset( uiBlkAddress), uiBytesToWrite,
pvBuffer, uiBufferSize, pIOBuffer, puiBytesWritten)))
{
if (rc != FERR_IO_DISK_FULL && rc != FERR_MEM)
{
ReleaseFile( FSGetFileNumber( uiBlkAddress), TRUE);
}
goto Exit;
}
Exit:
if( m_pECacheMgr)
{
if( RC_OK( rc) && !pIOBuffer)
{
for( uiLoop = 0; uiLoop < uiBytesToWrite; uiLoop += m_uiBlockSize)
{
pucBlk = &(((FLMBYTE *)pvBuffer)[ uiLoop]);
m_pECacheMgr->putBlock( uiBlkAddress + uiLoop, pucBlk);
}
}
else
{
for( uiLoop = 0; uiLoop < uiBytesToWrite; uiLoop += m_uiBlockSize)
{
(void)m_pECacheMgr->invalidateBlock( uiBlkAddress + uiLoop);
}
}
}
return( rc);
}
/****************************************************************************
Public: ReadHeader
Desc: Reads data from the database header
****************************************************************************/
RCODE F_SuperFileHdl::ReadHeader(
FLMUINT uiOffset,
FLMUINT uiBytesToRead,
void * pvBuffer,
FLMUINT * puiBytesRead)
{
RCODE rc = FERR_OK;
F_FileHdlImp * pFileHdl;
#ifdef FLM_DEBUG
if( m_uiBlockSize)
{
/*
Note: Block size may not be set because we are in the process of
opening the file for the first time and we don't know the block
size until after the header has been read.
*/
flmAssert( (FLMUINT)(uiOffset + uiBytesToRead) <= m_uiBlockSize);
}
#endif
if( RC_BAD( rc = GetFileHdl( 0, TRUE, &pFileHdl)))
{
goto Exit;
}
if( RC_BAD( rc = pFileHdl->Read( uiOffset,
uiBytesToRead, pvBuffer, puiBytesRead)))
{
if (rc != FERR_IO_END_OF_FILE && rc != FERR_MEM)
{
ReleaseFile( (FLMUINT)0, TRUE);
}
goto Exit;
}
Exit:
return( rc);
}
/****************************************************************************
Public: WriteHeader
Desc: Writes data to the database header
****************************************************************************/
RCODE F_SuperFileHdl::WriteHeader(
FLMUINT uiOffset,
FLMUINT uiBytesToWrite,
void * pvBuffer,
FLMUINT * puiBytesWritten)
{
RCODE rc = FERR_OK;
F_FileHdlImp * pFileHdl;
#ifdef FLM_DEBUG
if( m_uiBlockSize)
{
flmAssert( (FLMUINT)(uiOffset + uiBytesToWrite) <= m_uiBlockSize);
}
#endif
if( RC_BAD( rc = GetFileHdl( 0, TRUE, &pFileHdl)))
{
goto Exit;
}
if( RC_BAD( rc = pFileHdl->Write( uiOffset,
uiBytesToWrite, pvBuffer, puiBytesWritten)))
{
if (rc != FERR_IO_DISK_FULL && rc != FERR_MEM)
{
ReleaseFile( (FLMUINT)0, TRUE);
}
goto Exit;
}
Exit:
return( rc);
}
/****************************************************************************
Public: ReleaseFile
Desc: Releases all file handle objects and optionally closes the files
****************************************************************************/
RCODE F_SuperFileHdl::ReleaseFile(
FLMUINT uiFileNum,
FLMBOOL bCloseFile)
{
RCODE rc = FERR_OK;
CHECKED_OUT_FILE_HDL * pCkoFileHdl;
FLMUINT uiSlot;
pCkoFileHdl = getCkoFileHdlPtr( uiFileNum, &uiSlot);
if( pCkoFileHdl->uiFileNumber == uiFileNum)
{
if( RC_BAD( rc = ReleaseFile( pCkoFileHdl, bCloseFile)))
{
goto Exit;
}
}
Exit:
return( rc);
}
/****************************************************************************
Public: ReleaseFiles
Desc: Releases all file handle objects and optionally closes the files
****************************************************************************/
RCODE F_SuperFileHdl::ReleaseFiles(
FLMBOOL bCloseFiles)
{
RCODE rc = FERR_OK;
FLMUINT uiLoop;
flmAssert( m_bSetupCalled);
for( uiLoop = 0; uiLoop <= m_uiHighestUsedSlot; uiLoop++)
{
if( RC_BAD( rc = ReleaseFile(
&m_CheckedOutFileHdls[ uiLoop], bCloseFiles)))
{
goto Exit;
}
}
Exit:
return( rc);
}
/****************************************************************************
Public: ReleaseFile
Desc: Releases a file handle object
****************************************************************************/
RCODE F_SuperFileHdl::ReleaseFile(
CHECKED_OUT_FILE_HDL * pCkoFileHdl,
FLMBOOL bCloseFile)
{
RCODE rc = FERR_OK;
F_FileHdlImp * pFileHdl = pCkoFileHdl->pFileHdl;
if( pFileHdl)
{
flmAssert( pFileHdl->GetFileId());
if( pCkoFileHdl->bDirty)
{
(void)pFileHdl->Flush();
}
if( bCloseFile)
{
FLMINT iRefCnt;
/*
We must remove this handle from all lists and release
the file handle.
*/
rc = gv_FlmSysData.pFileHdlMgr->Remove( pFileHdl);
iRefCnt = pFileHdl->Release();
flmAssert( iRefCnt == 0); // pFileHdl should have been freed.
}
else
{
/*
Link out of the used list and move to the available list.
*/
rc = gv_FlmSysData.pFileHdlMgr->MakeAvailAndRelease( pFileHdl);
/*
NOTE: MakeAvailAndRelease will perform a release on the
file handle object for the caller.
*/
}
clearCkoFileHdl( pCkoFileHdl);
}
return( rc);
}
/****************************************************************************
Desc: Copy one CKO array into another.
****************************************************************************/
void F_SuperFileHdl::copyCkoFileHdls(
CHECKED_OUT_FILE_HDL * pSrcCkoArray,
FLMUINT uiSrcHighestUsedSlot
)
{
FLMUINT uiNewSlot;
FLMUINT uiSrcSlot;
// Zeroeth element is always copied.
f_memcpy( m_pCheckedOutFileHdls, pSrcCkoArray,
sizeof( CHECKED_OUT_FILE_HDL));
// Memset the rest of the destination array to zero.
f_memset( &m_pCheckedOutFileHdls[1], 0, sizeof( CHECKED_OUT_FILE_HDL) *
(m_uiCkoArraySize - 1));
m_uiHighestUsedSlot = 0;
m_uiLowestDirtySlot = 1;
m_uiHighestDirtySlot = 0;
for (uiSrcSlot = 1, pSrcCkoArray++;
uiSrcSlot <= uiSrcHighestUsedSlot;
uiSrcSlot++, pSrcCkoArray++)
{
if (pSrcCkoArray->pFileHdl && pSrcCkoArray->uiFileNumber)
{
uiNewSlot = pSrcCkoArray->uiFileNumber % (m_uiCkoArraySize - 1) + 1;
// Only overwrite the destination one if the file number is
// lower than the one already there
if (pSrcCkoArray->uiFileNumber <
m_pCheckedOutFileHdls [uiNewSlot].uiFileNumber ||
!m_pCheckedOutFileHdls [uiNewSlot].uiFileNumber)
{
if (m_pCheckedOutFileHdls [uiNewSlot].uiFileNumber)
{
ReleaseFile( &m_pCheckedOutFileHdls [uiNewSlot], FALSE);
}
f_memcpy( &m_pCheckedOutFileHdls [uiNewSlot], pSrcCkoArray,
sizeof( CHECKED_OUT_FILE_HDL));
if (uiNewSlot > m_uiHighestUsedSlot)
{
m_uiHighestUsedSlot = uiNewSlot;
}
if (m_uiHighestFileNumber < pSrcCkoArray->uiFileNumber)
{
m_uiHighestFileNumber = pSrcCkoArray->uiFileNumber;
}
if (pSrcCkoArray->bDirty)
{
if (m_uiLowestDirtySlot > m_uiHighestDirtySlot)
{
m_uiLowestDirtySlot =
m_uiHighestDirtySlot = uiNewSlot;
}
else if( m_uiHighestDirtySlot < uiNewSlot)
{
m_uiHighestDirtySlot = uiNewSlot;
}
else if (m_uiLowestDirtySlot < uiNewSlot)
{
m_uiLowestDirtySlot = uiNewSlot;
}
}
}
else
{
ReleaseFile( pSrcCkoArray, FALSE);
}
}
}
}
/****************************************************************************
Desc: Disable flush minimizing.
****************************************************************************/
void F_SuperFileHdl::disableFlushMinimize( void)
{
// Copy the allocated array back into the fixed array.
// This doesn't necessarily copy all of the file handles.
if (m_pCheckedOutFileHdls != &m_CheckedOutFileHdls [0])
{
CHECKED_OUT_FILE_HDL * pOldCkoArray = m_pCheckedOutFileHdls;
FLMUINT uiOldHighestUsedSlot = m_uiHighestUsedSlot;
m_pCheckedOutFileHdls = &m_CheckedOutFileHdls [0];
m_uiCkoArraySize = MAX_CHECKED_OUT_FILE_HDLS + 1;
copyCkoFileHdls( pOldCkoArray, uiOldHighestUsedSlot);
f_free( &pOldCkoArray);
}
m_bMinimizeFlushes = FALSE;
}
/****************************************************************************
Desc: Flush dirty files to disk.
****************************************************************************/
RCODE F_SuperFileHdl::Flush( void)
{
RCODE rc = FERR_OK;
FLMUINT uiLoop;
// Flush all dirty files
for (uiLoop = m_uiLowestDirtySlot;
uiLoop <= m_uiHighestDirtySlot;
uiLoop++)
{
if( m_pCheckedOutFileHdls[ uiLoop].bDirty)
{
RCODE tmpRc;
if (RC_BAD( tmpRc =
m_pCheckedOutFileHdls[ uiLoop].pFileHdl->Flush()))
{
rc = tmpRc;
ReleaseFile( &m_pCheckedOutFileHdls [uiLoop], TRUE);
}
m_pCheckedOutFileHdls[ uiLoop].bDirty = FALSE;
}
}
m_uiLowestDirtySlot = 1;
m_uiHighestDirtySlot = 0;
return( rc);
}
/****************************************************************************
Public: TruncateFile
Desc: Truncates back to an end of file block address.
This may only be called from reduce() because there cannot
be any other cases to reduce a 3x block file.
****************************************************************************/
RCODE F_SuperFileHdl::TruncateFile(
FLMUINT uiEOFBlkAddress)
{
RCODE rc = FERR_OK;
FLMUINT uiFileNumber = (FLMUINT)FSGetFileNumber( uiEOFBlkAddress);
FLMUINT uiBlockOffset = (FLMUINT)FSGetFileOffset( uiEOFBlkAddress);
F_FileHdlImp * pFileHdl;
/*
Truncate the current block file.
*/
if( RC_BAD( rc = GetFileHdl( uiFileNumber, TRUE, &pFileHdl)))
{
goto Exit;
}
if( RC_BAD( rc = pFileHdl->Truncate( uiBlockOffset)))
{
ReleaseFile( uiFileNumber, TRUE);
goto Exit;
}
/*
Visit the rest of the high block files until a NULL file hdl is hit.
*/
for( ;;)
{
if( RC_BAD( GetFileHdl( ++uiFileNumber, TRUE, &pFileHdl)))
{
break;
}
if( RC_BAD( rc = pFileHdl->Truncate( (FLMUINT)0 )))
{
ReleaseFile( uiFileNumber, TRUE);
goto Exit;
}
if( RC_BAD( rc = ReleaseFile( uiFileNumber, TRUE)))
{
goto Exit;
}
}
Exit:
return( rc);
}
/****************************************************************************
Public: TruncateFiles
Desc: Truncate to zero length any files between the specified start
and end files.
****************************************************************************/
void F_SuperFileHdl::TruncateFiles(
FLMUINT uiStartFileNum,
FLMUINT uiEndFileNum)
{
FLMUINT uiFileNumber;
F_FileHdlImp * pFileHdl;
for( uiFileNumber = uiStartFileNum;
uiFileNumber <= uiEndFileNum;
uiFileNumber++ )
{
if( RC_OK( GetFileHdl( uiFileNumber, TRUE, &pFileHdl )))
{
(void)pFileHdl->Truncate( (FLMUINT)0 );
(void)ReleaseFile( uiFileNumber, TRUE);
}
}
}
/****************************************************************************
Public: GetFileSize
Desc: Returns the physical size of a file
****************************************************************************/
RCODE F_SuperFileHdl::GetFileSize(
FLMUINT uiFileNumber,
FLMUINT * puiFileSize)
{
F_FileHdlImp * pFileHdl = NULL;
RCODE rc = FERR_OK;
flmAssert( m_bSetupCalled);
flmAssert( puiFileSize);
/*
Initialize the size to zero.
*/
*puiFileSize = 0;
/*
Get the file handle.
*/
if( RC_BAD( rc = GetFileHdl( uiFileNumber, FALSE, &pFileHdl)))
{
goto Exit;
}
if( RC_BAD( rc = pFileHdl->Size( puiFileSize)))
{
ReleaseFile( uiFileNumber, TRUE);
goto Exit;
}
Exit:
return( rc);
}
/****************************************************************************
Public: GetFilePath
Desc: Returns the path of a file given its file number
****************************************************************************/
RCODE F_SuperFileHdl::GetFilePath(
FLMUINT uiFileNumber,
char * pszIoPath)
{
RCODE rc = FERR_OK;
FLMUINT uiExtOffset;
// Sanity checks
flmAssert( m_bSetupCalled);
if (!uiFileNumber)
{
f_strcpy( pszIoPath, m_pszDbFileName);
goto Exit;
}
if ((m_uiDbVersion >= FLM_FILE_FORMAT_VER_4_3 &&
uiFileNumber <= MAX_DATA_FILE_NUM_VER43) ||
uiFileNumber <= MAX_DATA_FILE_NUM_VER40)
{
f_memcpy( pszIoPath, m_pszDataFileNameBase, m_uiDataExtOffset);
uiExtOffset = m_uiDataExtOffset;
}
else
{
f_memcpy( pszIoPath, m_pszDbFileName, m_uiExtOffset);
uiExtOffset = m_uiExtOffset;
}
// Modify the file's extension.
bldSuperFileExtension( m_uiDbVersion,
uiFileNumber, &pszIoPath[ uiExtOffset]);
Exit:
return( rc);
}
/****************************************************************************
Desc: Reallocates the checked out file handle array.
****************************************************************************/
RCODE F_SuperFileHdl::reallocCkoArray(
FLMUINT uiFileNum
)
{
RCODE rc = FERR_OK;
FLMUINT uiNewSize;
CHECKED_OUT_FILE_HDL * pNewCkoArray;
CHECKED_OUT_FILE_HDL * pOldCkoArray;
FLMUINT uiOldHighestUsedSlot;
if (uiFileNum < m_uiHighestFileNumber)
{
uiFileNum = m_uiHighestFileNumber;
}
uiNewSize = uiFileNum + 128;
// Reallocate so we can guarantee that all of the current file
// numbers will copy and there is room for this new one as well.
if (uiNewSize > MAX_LOG_BLOCK_FILE_NUMBER( m_uiDbVersion) + 1)
{
flmAssert( uiFileNum <= MAX_LOG_BLOCK_FILE_NUMBER( m_uiDbVersion));
uiNewSize = MAX_LOG_BLOCK_FILE_NUMBER( m_uiDbVersion) + 1;
}
// No need to call f_calloc, because copyCkoFileHdls will initialize
// it below.
if (RC_BAD( rc = f_alloc( sizeof( CHECKED_OUT_FILE_HDL) * uiNewSize,
&pNewCkoArray)))
{
goto Exit;
}
pOldCkoArray = m_pCheckedOutFileHdls;
uiOldHighestUsedSlot = m_uiHighestUsedSlot;
m_pCheckedOutFileHdls = pNewCkoArray;
m_uiCkoArraySize = uiNewSize;
copyCkoFileHdls( pOldCkoArray, uiOldHighestUsedSlot);
// Can't free the old one until after the copy!
if (pOldCkoArray != &m_CheckedOutFileHdls [0])
{
f_free( &pOldCkoArray);
}
Exit:
return( rc);
}
/****************************************************************************
Public: GetFileHdl
Desc: Returns a file handle given the file's number
****************************************************************************/
RCODE F_SuperFileHdl::GetFileHdl(
FLMUINT uiFileNum,
FLMBOOL bGetForUpdate,
F_FileHdlImp ** ppFileHdl)
{
RCODE rc = FERR_OK;
F_FileHdlImp * pFileHdl = NULL;
FLMUINT uiFileId;
CHECKED_OUT_FILE_HDL * pCkoFileHdl;
char szFilePath[ F_PATH_MAX_SIZE];
FLMUINT uiSlot;
// Get the file handle
pCkoFileHdl = getCkoFileHdlPtr( uiFileNum, &uiSlot);
if( pCkoFileHdl->uiFileNumber != uiFileNum &&
pCkoFileHdl->pFileHdl)
{
if (pCkoFileHdl->bDirty && m_bMinimizeFlushes)
{
flmAssert( pCkoFileHdl->uiFileNumber);
if (RC_BAD( reallocCkoArray( uiFileNum)))
{
goto Exit;
}
pCkoFileHdl = getCkoFileHdlPtr( uiFileNum, &uiSlot);
// Better have reallocated so that the new slot for
// the file number has nothing in it.
flmAssert( !pCkoFileHdl->uiFileNumber &&
!pCkoFileHdl->pFileHdl);
}
else
{
if( RC_BAD( rc = ReleaseFile( pCkoFileHdl, FALSE)))
{
goto Exit;
}
}
}
if( !pCkoFileHdl->pFileHdl)
{
// Get the file ID
if( RC_BAD( rc = m_pFileIdList->getFileId( uiFileNum, &uiFileId)))
{
goto Exit;
}
// Look for an available file handle if not opening exclusive.
// NOTE: AddRef() performed for caller by FindAvail if a file
// handle is found.
if( RC_BAD( gv_FlmSysData.pFileHdlMgr->FindAvail(
uiFileId, FALSE, &pFileHdl)))
{
// Allocate a new file handle, open the file and
// link into the used directory.
if( (pFileHdl = f_new F_FileHdlImp) == NULL)
{
rc = RC_SET( FERR_MEM);
goto Exit;
}
#ifdef FLM_WIN
/*
If m_uiBlockSize is 0, direct I/O will not be used
*/
pFileHdl->SetBlockSize( m_uiBlockSize);
#endif
flmAssert( uiFileId); // File ID must be non-zero
if( RC_BAD( rc = pFileHdl->Setup( uiFileId)))
{
goto Exit;
}
// Build the file path
if( RC_BAD( rc = GetFilePath( uiFileNum, szFilePath)))
{
goto Exit;
}
// Open the file
if( RC_BAD( rc = pFileHdl->Open( szFilePath,
F_IO_RDWR | F_IO_SH_DENYNONE | F_IO_DIRECT)))
{
goto Exit;
}
// Insert into the manager
if( RC_BAD( rc = gv_FlmSysData.pFileHdlMgr->InsertNew( pFileHdl)))
{
goto Exit;
}
}
pCkoFileHdl->pFileHdl = pFileHdl;
pFileHdl = NULL; // Set to NULL so the handle won't be released below
pCkoFileHdl->uiFileNumber = uiFileNum;
pCkoFileHdl->bDirty = FALSE;
if( m_uiHighestUsedSlot < uiSlot)
{
m_uiHighestUsedSlot = uiSlot;
}
if (m_uiHighestFileNumber < uiFileNum)
{
m_uiHighestFileNumber = uiFileNum;
}
}
*ppFileHdl = pCkoFileHdl->pFileHdl;
if( bGetForUpdate)
{
pCkoFileHdl->bDirty = TRUE;
if (m_uiLowestDirtySlot > m_uiHighestDirtySlot)
{
m_uiLowestDirtySlot =
m_uiHighestDirtySlot = uiSlot;
}
else if( m_uiHighestDirtySlot < uiSlot)
{
m_uiHighestDirtySlot = uiSlot;
}
else if (m_uiLowestDirtySlot < uiSlot)
{
m_uiLowestDirtySlot = uiSlot;
}
}
Exit:
if( pFileHdl)
{
pFileHdl->Release();
}
return( rc);
}
/****************************************************************************
Desc: Generates a file name given a super file number.
Adds ".xx" to pFileExtension. Use lower case characters.
Notes: This is a base 24 alphanumeric value where
{ a, b, c, d, e, f, i, l, o, r, u, v } values are removed.
****************************************************************************/
void bldSuperFileExtension(
FLMUINT uiDbVersion,
FLMUINT uiFileNum,
char * pszFileExtension)
{
FLMBYTE ucLetter;
flmAssert( uiDbVersion); // Make sure the database version is valid
if (uiDbVersion >= FLM_FILE_FORMAT_VER_4_3)
{
if (uiFileNum <= MAX_DATA_FILE_NUM_VER43 - 1536)
{
// No additional letter - File numbers 1 to 511
// This is just like pre-4.3 numbering.
ucLetter = 0;
}
else if (uiFileNum <= MAX_DATA_FILE_NUM_VER43 - 1024)
{
// File numbers 512 to 1023
ucLetter = 'r';
}
else if (uiFileNum <= MAX_DATA_FILE_NUM_VER43 - 512)
{
// File numbers 1024 to 1535
ucLetter = 's';
}
else if (uiFileNum <= MAX_DATA_FILE_NUM_VER43)
{
// File numbers 1536 to 2047
ucLetter = 't';
}
else if (uiFileNum <= MAX_LOG_FILE_NUM_VER43 - 1536)
{
// File numbers 2048 to 2559
ucLetter = 'v';
}
else if (uiFileNum <= MAX_LOG_FILE_NUM_VER43 - 1024)
{
// File numbers 2560 to 3071
ucLetter = 'w';
}
else if (uiFileNum <= MAX_LOG_FILE_NUM_VER43 - 512)
{
// File numbers 3072 to 3583
ucLetter = 'x';
}
else
{
flmAssert( uiFileNum <= MAX_LOG_FILE_NUM_VER43);
// File numbers 3584 to 4095
ucLetter = 'z';
}
}
else // Pre-4.3 versions
{
if (uiFileNum <= MAX_DATA_FILE_NUM_VER40)
{
// No additional letter - File numbers 1 to 511
// This is just like pre-4.3 numbering.
ucLetter = 0;
}
else
{
flmAssert( uiFileNum <= MAX_LOG_FILE_NUM_VER40);
// File numbers 512 to 1023
ucLetter = 'x';
}
}
*pszFileExtension++ = '.';
*pszFileExtension++ = base24ToDigit( (uiFileNum & 511) / 24);
*pszFileExtension++ = base24ToDigit( (uiFileNum & 511) % 24);
*pszFileExtension++ = ucLetter;
*pszFileExtension = 0;
}
/****************************************************************************
Desc: Turn a base 24 value into a native alphanumeric value.
Notes: This is a base 24 alphanumeric value where
{a, b, c, d, e, f, i, l, o, r, u, v } values are removed.
****************************************************************************/
FSTATIC FLMBYTE base24ToDigit(
FLMUINT uiValue)
{
flmAssert( uiValue <= 23);
if( uiValue <= 9)
{
uiValue += (FLMUINT) NATIVE_ZERO;
}
else
{
uiValue = f_toascii(uiValue) - 10 + (FLMUINT)f_toascii('g');
if( uiValue >= (FLMUINT)'i')
{
uiValue++;
if( uiValue >= (FLMUINT)'l')
{
uiValue++;
if( uiValue >= (FLMUINT)'o')
{
uiValue++;
if( uiValue >= (FLMUINT)'r')
{
uiValue++;
if( uiValue >= (FLMUINT)'u')
{
uiValue++;
if( uiValue >= (FLMUINT)'v')
{
uiValue++;
}
}
}
}
}
}
}
return (FLMBYTE)uiValue;
}