git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@7 0109f412-320b-0410-ab79-c3e0c5ffbbe6
1633 lines
35 KiB
C++
1633 lines
35 KiB
C++
//-------------------------------------------------------------------------
|
|
// Desc: Posix File I/O
|
|
// Tabs: 3
|
|
//
|
|
// Copyright (c) 1999-2006 Novell, Inc. All Rights Reserved.
|
|
//
|
|
// This program is free software; you can redistribute it and/or
|
|
// modify it under the terms of version 2 of the GNU General Public
|
|
// License as published by the Free Software Foundation.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program; if not, contact Novell, Inc.
|
|
//
|
|
// To contact Novell about this file by physical or electronic mail,
|
|
// you may find current contact information at www.novell.com
|
|
//
|
|
// $Id: fposix.cpp 12331 2006-01-23 10:19:55 -0700 (Mon, 23 Jan 2006) ahodgkinson $
|
|
//-------------------------------------------------------------------------
|
|
|
|
#include "flaimsys.h"
|
|
|
|
#if defined( FLM_UNIX)
|
|
|
|
#ifdef FLM_AIX
|
|
#ifndef _LARGE_FILES
|
|
#define _LARGE_FILES
|
|
#endif
|
|
#include <stdio.h>
|
|
#endif
|
|
|
|
#include <sys/types.h>
|
|
#include <fcntl.h>
|
|
|
|
#if !defined( O_SYNC)
|
|
#define O_SYNC 0
|
|
#endif
|
|
|
|
#if !defined( O_DSYNC) // some systems don't have this
|
|
#define O_DSYNC O_SYNC
|
|
#endif
|
|
|
|
extern RCODE gv_CriticalFSError;
|
|
|
|
#define MAX_CREATION_TRIES 10
|
|
|
|
#if defined( FLM_SOLARIS)
|
|
#include <sys/statvfs.h>
|
|
#elif defined( FLM_LINUX)
|
|
#include <sys/vfs.h>
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
F_FileHdlImp::F_FileHdlImp()
|
|
{
|
|
m_fd = INVALID_HANDLE_VALUE;
|
|
m_uiMaxAutoExtendSize = gv_FlmSysData.uiMaxFileSize;
|
|
m_uiBlockSize = 0;
|
|
m_uiBytesPerSector = 0;
|
|
m_uiNotOnSectorBoundMask = 0;
|
|
m_uiGetSectorBoundMask = 0;
|
|
m_uiExtendSize = 0;
|
|
m_uiCurrentPos = 0;
|
|
m_bDoDirectIO = FALSE;
|
|
m_bCanDoAsync = FALSE;
|
|
m_pucAlignedBuff = NULL;
|
|
m_uiAlignedBuffSize = 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
F_FileHdlImp::~F_FileHdlImp()
|
|
{
|
|
if( m_bFileOpened)
|
|
{
|
|
Close();
|
|
}
|
|
|
|
if( m_pucAlignedBuff)
|
|
{
|
|
free( m_pucAlignedBuff);
|
|
}
|
|
}
|
|
|
|
/***************************************************************************
|
|
Desc: Open or create a file.
|
|
***************************************************************************/
|
|
RCODE F_FileHdlImp::OpenOrCreate(
|
|
const char * pFileName,
|
|
FLMUINT uiAccess,
|
|
FLMBOOL bCreateFlag)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FLMBOOL bDoDirectIO = FALSE;
|
|
char szSaveFileName[ F_PATH_MAX_SIZE];
|
|
int openFlags = O_RDONLY;
|
|
|
|
if( RC_BAD( rc = GET_FS_ERROR()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
#if !defined( FLM_UNIX)
|
|
bDoDirectIO = (uiAccess & F_IO_DIRECT) ? TRUE : FALSE;
|
|
#endif
|
|
|
|
// HPUX needs this defined to access files larger than 2 GB. The Linux
|
|
// man pages *say* it's needed although as of Suse 9.1 it actually
|
|
// isn't. Including this flag on Linux anyway just it case...
|
|
|
|
#if defined( FLM_HPUX) || defined( FLM_LINUX)
|
|
openFlags |= O_LARGEFILE;
|
|
#endif
|
|
if( uiAccess & F_IO_DELETE_ON_CLOSE)
|
|
{
|
|
if( !m_pszIoPath)
|
|
{
|
|
if( RC_BAD( rc = f_alloc( F_PATH_MAX_SIZE, &m_pszIoPath)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
f_strcpy( m_pszIoPath, pFileName);
|
|
m_bDeleteOnClose = TRUE;
|
|
}
|
|
else
|
|
{
|
|
m_bDeleteOnClose = FALSE;
|
|
}
|
|
|
|
// Save the file name in case we have to create the directory
|
|
|
|
if( bCreateFlag && (uiAccess & F_IO_CREATE_DIR))
|
|
{
|
|
f_strcpy( szSaveFileName, pFileName);
|
|
}
|
|
|
|
if( bCreateFlag)
|
|
{
|
|
openFlags |= O_CREAT;
|
|
|
|
if( uiAccess & F_IO_EXCL)
|
|
{
|
|
openFlags |= O_EXCL;
|
|
}
|
|
}
|
|
|
|
if( uiAccess & F_IO_TRUNC)
|
|
{
|
|
openFlags |= O_TRUNC;
|
|
}
|
|
|
|
if( !(uiAccess & F_IO_RDONLY))
|
|
{
|
|
openFlags |= O_RDWR;
|
|
}
|
|
|
|
// If doing direct IO, need to get the sector size.
|
|
|
|
if( bDoDirectIO)
|
|
{
|
|
if( !m_uiBlockSize)
|
|
{
|
|
bDoDirectIO = FALSE;
|
|
}
|
|
else
|
|
{
|
|
if( RC_BAD( rc = gv_FlmSysData.pFileSystem->GetSectorSize(
|
|
pFileName, &m_uiBytesPerSector)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
m_uiNotOnSectorBoundMask = m_uiBytesPerSector - 1;
|
|
m_uiGetSectorBoundMask = ~m_uiNotOnSectorBoundMask;
|
|
|
|
// Can't do direct IO if the block size isn't a multiple of
|
|
// the sector size.
|
|
|
|
if( m_uiBlockSize < m_uiBytesPerSector ||
|
|
m_uiBlockSize % m_uiBytesPerSector != 0)
|
|
{
|
|
bDoDirectIO = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
Retry_Create:
|
|
|
|
// Try to create or open the file
|
|
|
|
if( (m_fd = open( pFileName, openFlags, 0600)) == INVALID_HANDLE_VALUE)
|
|
{
|
|
if( (errno == ENOENT) && (uiAccess & F_IO_CREATE_DIR))
|
|
{
|
|
char szTemp[ F_PATH_MAX_SIZE];
|
|
char ioDirPath[ F_PATH_MAX_SIZE];
|
|
|
|
uiAccess &= ~F_IO_CREATE_DIR;
|
|
|
|
// Remove the file name for which we are creating the directory
|
|
|
|
if( RC_OK( f_pathReduce( szSaveFileName, ioDirPath, szTemp)))
|
|
{
|
|
F_FileSystemImp FileSystem;
|
|
|
|
if( RC_BAD( rc = FileSystem.CreateDir( ioDirPath)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
goto Retry_Create;
|
|
}
|
|
}
|
|
|
|
rc = MapErrnoToFlaimErr( errno, FERR_OPENING_FILE);
|
|
goto Exit;
|
|
}
|
|
|
|
#if defined( FLM_SOLARIS)
|
|
if( bDoDirectIO)
|
|
{
|
|
directio( m_fd, DIRECTIO_ON);
|
|
}
|
|
#endif
|
|
|
|
m_bDoDirectIO = bDoDirectIO;
|
|
|
|
Exit:
|
|
|
|
if( RC_BAD( rc))
|
|
{
|
|
m_fd = INVALID_HANDLE_VALUE;
|
|
m_bDoDirectIO = FALSE;
|
|
m_bCanDoAsync = FALSE;
|
|
}
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Create a file
|
|
****************************************************************************/
|
|
RCODE F_FileHdlImp::Create(
|
|
const char * pIoPath,
|
|
FLMUINT uiIoFlags)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
|
|
flmAssert( !m_bFileOpened);
|
|
|
|
if( RC_BAD( rc = GET_FS_ERROR()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( RC_BAD( rc = OpenOrCreate( pIoPath, uiIoFlags, TRUE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
m_bFileOpened = TRUE;
|
|
m_uiCurrentPos = 0;
|
|
m_bOpenedExclusive = (uiIoFlags & F_IO_SH_DENYRW) ? TRUE : FALSE;
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
RCODE F_FileHdlImp::CreateUnique(
|
|
char * pIoPath,
|
|
const char * pszFileExtension,
|
|
FLMUINT uiIoFlags)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
char * pszTmp;
|
|
FLMBOOL bModext = TRUE;
|
|
FLMUINT uiBaseTime = 0;
|
|
char ucHighByte = 0;
|
|
char ucFileName[ F_FILENAME_SIZE];
|
|
char * pDirPath;
|
|
char szDirPath[ F_PATH_MAX_SIZE];
|
|
char szTmpPath[ F_PATH_MAX_SIZE];
|
|
FLMUINT uiCount;
|
|
|
|
flmAssert( !m_bFileOpened);
|
|
f_memset( ucFileName, 0, sizeof( ucFileName));
|
|
|
|
if( RC_BAD( rc = GET_FS_ERROR()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( !pIoPath || pIoPath[ 0] == '\0')
|
|
{
|
|
f_strcpy( szDirPath, "./");
|
|
}
|
|
else
|
|
{
|
|
f_strcpy( szDirPath, pIoPath);
|
|
}
|
|
|
|
pDirPath = szDirPath;
|
|
|
|
// Search backwards replacing trailing spaces with NULLs.
|
|
|
|
pszTmp = pDirPath;
|
|
pszTmp += (f_strlen( pszTmp) - 1);
|
|
|
|
while ((*pszTmp == ' ') && pszTmp >= pDirPath)
|
|
{
|
|
*pszTmp = 0;
|
|
pszTmp--;
|
|
}
|
|
|
|
// Append a slash if one isn't already there
|
|
|
|
if( pszTmp >= pDirPath && *pszTmp != '/')
|
|
{
|
|
pszTmp++;
|
|
*pszTmp++ = '/';
|
|
}
|
|
else
|
|
{
|
|
pszTmp++;
|
|
}
|
|
|
|
*pszTmp = 0;
|
|
|
|
if( (pszFileExtension) && (f_strlen( pszFileExtension) >= 3))
|
|
{
|
|
bModext = FALSE;
|
|
}
|
|
|
|
uiCount = 0;
|
|
do
|
|
{
|
|
f_pathCreateUniqueName( &uiBaseTime, ucFileName, pszFileExtension,
|
|
&ucHighByte, bModext);
|
|
|
|
f_strcpy( szTmpPath, pDirPath);
|
|
f_pathAppend( szTmpPath, ucFileName);
|
|
|
|
rc = Create( szTmpPath, uiIoFlags | F_IO_EXCL);
|
|
|
|
if( rc == FERR_IO_DISK_FULL)
|
|
{
|
|
F_FileSystemImp FileSystem;
|
|
|
|
FileSystem.Delete( pDirPath);
|
|
goto Exit;
|
|
}
|
|
|
|
if( rc == FERR_IO_PATH_NOT_FOUND || rc == FERR_IO_INVALID_PASSWORD)
|
|
{
|
|
goto Exit;
|
|
}
|
|
} while( rc != FERR_OK && (uiCount++ < MAX_CREATION_TRIES));
|
|
|
|
// Check if the path was created
|
|
|
|
if( uiCount >= MAX_CREATION_TRIES && rc != FERR_OK)
|
|
{
|
|
rc = RC_SET( FERR_IO_PATH_CREATE_FAILURE);
|
|
goto Exit;
|
|
}
|
|
|
|
m_bFileOpened = TRUE;
|
|
m_bOpenedExclusive = (uiIoFlags & F_IO_SH_DENYRW) ? TRUE : FALSE;
|
|
|
|
// Created file name needs to be returned.
|
|
|
|
f_strcpy( pIoPath, szTmpPath);
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Open a file
|
|
****************************************************************************/
|
|
RCODE F_FileHdlImp::Open(
|
|
const char * pIoPath,
|
|
FLMUINT uiIoFlags)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
|
|
flmAssert( !m_bFileOpened);
|
|
|
|
if( RC_BAD( rc = GET_FS_ERROR()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Loop on error open conditions.
|
|
|
|
for( ;;)
|
|
{
|
|
if( RC_BAD( rc = OpenOrCreate( pIoPath, uiIoFlags, FALSE)))
|
|
{
|
|
if( rc != FERR_IO_TOO_MANY_OPEN_FILES)
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// If for some reason we cannot open the file, then
|
|
// try to close some other file handle in the list.
|
|
|
|
if( RC_BAD( rc = gv_FlmSysData.pFileHdlMgr->ReleaseOneAvail()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
m_bFileOpened = TRUE;
|
|
m_uiCurrentPos = 0;
|
|
m_bOpenedReadOnly = (uiIoFlags & F_IO_RDONLY) ? TRUE : FALSE;
|
|
m_bOpenedExclusive = (uiIoFlags & F_IO_SH_DENYRW) ? TRUE : FALSE;
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
Desc: Close a file
|
|
****************************************************************************/
|
|
RCODE F_FileHdlImp::Close( void)
|
|
{
|
|
FLMBOOL bDeleteAllowed = TRUE;
|
|
RCODE rc = FERR_OK;
|
|
|
|
if( !m_bFileOpened)
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
flmAssert( m_fd != INVALID_HANDLE_VALUE);
|
|
close( m_fd);
|
|
|
|
m_fd = INVALID_HANDLE_VALUE;
|
|
m_bFileOpened = m_bOpenedReadOnly = m_bOpenedExclusive = FALSE;
|
|
|
|
if( m_bDeleteOnClose)
|
|
{
|
|
flmAssert( m_pszIoPath);
|
|
|
|
if( bDeleteAllowed)
|
|
{
|
|
F_FileSystemImp FileSystem;
|
|
|
|
FileSystem.Delete( m_pszIoPath);
|
|
}
|
|
|
|
m_bDeleteOnClose = FALSE;
|
|
}
|
|
|
|
if( m_pszIoPath)
|
|
{
|
|
f_free( &m_pszIoPath);
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Make sure all file data is safely on disk
|
|
****************************************************************************/
|
|
RCODE F_FileHdlImp::Flush( void)
|
|
{
|
|
#ifdef FLM_SOLARIS
|
|
|
|
// Direct I/O on Solaris is ADVISORY, meaning that the
|
|
// operating system may or may not actually honor the
|
|
// option for some or all operations on a given file.
|
|
// Thus, the only way to guarantee that writes are on disk
|
|
// is to call fdatasync.
|
|
//
|
|
// If a process is killed (with SIGKILL or SIGTERM), the
|
|
// dirty cache buffers associated with open files will be discarded unless
|
|
// the process intercepts the signal and properly closes the files.
|
|
//
|
|
// NOTES FROM THE UNIX MAN PAGES ON SIGNALS
|
|
//
|
|
// When killing a process or series of processes, it is common sense
|
|
// to start trying with the least dangerous signal, SIGTERM. That way,
|
|
// programs that care about an orderly shutdown get the chance to follow
|
|
// the procedures that they have been designed to execute when getting
|
|
// the SIGTERM signal, such as cleaning up and closing open files. If you
|
|
// send a SIGKILL to a process, you remove any chance for the process
|
|
// to do a tidy cleanup and shutdown, which might have unfortunate
|
|
// consequences.
|
|
|
|
if( fdatasync( m_fd) != 0)
|
|
{
|
|
return( MapErrnoToFlaimErr( errno, FERR_FLUSHING_FILE));
|
|
}
|
|
|
|
#else
|
|
|
|
if( !m_bDoDirectIO)
|
|
{
|
|
#ifdef FLM_OSX
|
|
if( fsync( m_fd) != 0)
|
|
#else
|
|
if( fdatasync( m_fd) != 0)
|
|
#endif
|
|
{
|
|
return( MapErrnoToFlaimErr( errno, FERR_FLUSHING_FILE));
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
return( FERR_OK);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Read from a file
|
|
****************************************************************************/
|
|
RCODE F_FileHdlImp::DirectRead(
|
|
FLMUINT uiReadOffset,
|
|
FLMUINT uiBytesToRead,
|
|
void * pvBuffer,
|
|
FLMBOOL bBuffHasFullSectors,
|
|
FLMUINT * puiBytesRead)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FLMUINT uiBytesRead;
|
|
FLMBYTE * pucReadBuffer;
|
|
FLMBYTE * pucDestBuffer;
|
|
FLMUINT uiMaxBytesToRead;
|
|
FLMINT iTmp;
|
|
FLMBOOL bHitEOF;
|
|
|
|
flmAssert( m_bFileOpened);
|
|
flmAssert( m_bDoDirectIO);
|
|
|
|
if( puiBytesRead)
|
|
{
|
|
*puiBytesRead = 0;
|
|
}
|
|
|
|
if( RC_BAD( rc = GET_FS_ERROR()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( uiReadOffset == F_IO_CURRENT_POS)
|
|
{
|
|
uiReadOffset = m_uiCurrentPos;
|
|
}
|
|
|
|
// This loop does multiple reads (if necessary) to get all of the
|
|
// data. It uses aligned buffers and reads at sector offsets.
|
|
|
|
pucDestBuffer = (FLMBYTE *)pvBuffer;
|
|
for (;;)
|
|
{
|
|
// See if we are using an aligned buffer. If not, allocate
|
|
// one (if not already allocated), and use it.
|
|
|
|
if( (uiReadOffset & m_uiNotOnSectorBoundMask) ||
|
|
(((FLMUINT)pucDestBuffer) & m_uiNotOnSectorBoundMask) ||
|
|
((uiBytesToRead & m_uiNotOnSectorBoundMask) &&
|
|
(!bBuffHasFullSectors)))
|
|
{
|
|
if( !m_pucAlignedBuff)
|
|
{
|
|
if( RC_BAD( rc = AllocAlignBuffer()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
pucReadBuffer = m_pucAlignedBuff;
|
|
|
|
// Must read enough bytes to cover all of the sectors that
|
|
// contain the data we are trying to read. The value of
|
|
// (uiReadOffset & m_uiNotOnSectorBoundMask) will give us the
|
|
// number of additional bytes that are in the sector prior to
|
|
// the read offset. We then round that up to the next sector
|
|
// to get the total number of bytes we are going to read.
|
|
|
|
uiMaxBytesToRead = RoundUpToSectorMultiple( uiBytesToRead +
|
|
(uiReadOffset & m_uiNotOnSectorBoundMask));
|
|
|
|
// Can't read more than the aligned buffer will hold.
|
|
|
|
if( uiMaxBytesToRead > m_uiAlignedBuffSize)
|
|
{
|
|
uiMaxBytesToRead = m_uiAlignedBuffSize;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
uiMaxBytesToRead = RoundUpToSectorMultiple( uiBytesToRead);
|
|
flmAssert( uiMaxBytesToRead >= uiBytesToRead);
|
|
pucReadBuffer = pucDestBuffer;
|
|
}
|
|
|
|
bHitEOF = FALSE;
|
|
|
|
#ifdef HAVE_PREAD
|
|
if( (iTmp = pread( m_fd, pucReadBuffer,
|
|
uiMaxBytesToRead, GetSectorStartOffset( uiReadOffset))) == -1)
|
|
{
|
|
rc = MapErrnoToFlaimErr( errno, FERR_READING_FILE);
|
|
goto Exit;
|
|
}
|
|
#else
|
|
if( lseek( m_fd, GetSectorStartOffset( uiReadOffset), SEEK_SET) == -1)
|
|
{
|
|
rc = MapErrnoToFlaimErr( errno, FERR_POSITIONING_IN_FILE);
|
|
goto Exit;
|
|
}
|
|
|
|
if( (iTmp = read( m_fd, pucReadBuffer, uiMaxBytesToRead)) == -1)
|
|
{
|
|
rc = MapErrnoToFlaimErr(errno, FERR_READING_FILE);
|
|
goto Exit;
|
|
}
|
|
#endif
|
|
uiBytesRead = (FLMUINT)iTmp;
|
|
|
|
if( uiBytesRead < uiMaxBytesToRead)
|
|
{
|
|
bHitEOF = TRUE;
|
|
}
|
|
|
|
// If the offset we want to read from is not on a sector
|
|
// boundary, increment the read buffer pointer to the
|
|
// offset where the data we need starts and decrement the
|
|
// bytes read by the difference between the start of the
|
|
// sector and the actual read offset.
|
|
|
|
if( uiReadOffset & m_uiNotOnSectorBoundMask)
|
|
{
|
|
pucReadBuffer += (uiReadOffset & m_uiNotOnSectorBoundMask);
|
|
flmAssert( uiBytesRead >= m_uiBytesPerSector);
|
|
uiBytesRead -= (uiReadOffset & m_uiNotOnSectorBoundMask);
|
|
}
|
|
|
|
// If bytes read is more than we actually need, truncate it back
|
|
// so that we only copy what we actually need.
|
|
|
|
if( uiBytesRead > uiBytesToRead)
|
|
{
|
|
uiBytesRead = uiBytesToRead;
|
|
}
|
|
|
|
uiBytesToRead -= uiBytesRead;
|
|
|
|
if( puiBytesRead)
|
|
{
|
|
(*puiBytesRead) += uiBytesRead;
|
|
}
|
|
|
|
m_uiCurrentPos = uiReadOffset + uiBytesRead;
|
|
|
|
// If using a different buffer for reading, copy the
|
|
// data read into the destination buffer.
|
|
|
|
if( pucDestBuffer != pucReadBuffer)
|
|
{
|
|
f_memcpy( pucDestBuffer, pucReadBuffer, uiBytesRead);
|
|
}
|
|
|
|
if( !uiBytesToRead)
|
|
{
|
|
break;
|
|
}
|
|
|
|
// Still more to read - did we hit EOF above?
|
|
|
|
if( bHitEOF)
|
|
{
|
|
rc = RC_SET( FERR_IO_END_OF_FILE);
|
|
break;
|
|
}
|
|
|
|
pucDestBuffer += uiBytesRead;
|
|
uiReadOffset += uiBytesRead;
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Read from a file
|
|
****************************************************************************/
|
|
RCODE F_FileHdlImp::Read(
|
|
FLMUINT uiReadOffset,
|
|
FLMUINT uiBytesToRead,
|
|
void * pvBuffer,
|
|
FLMUINT * puiBytesRead)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FLMINT iBytesRead;
|
|
|
|
flmAssert( m_bFileOpened);
|
|
|
|
if( m_bDoDirectIO)
|
|
{
|
|
rc = DirectRead( uiReadOffset, uiBytesToRead,
|
|
pvBuffer, FALSE, puiBytesRead);
|
|
goto Exit;
|
|
}
|
|
|
|
if( RC_BAD( rc = GET_FS_ERROR()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( uiReadOffset == F_IO_CURRENT_POS)
|
|
{
|
|
uiReadOffset = m_uiCurrentPos;
|
|
}
|
|
|
|
#ifdef HAVE_PREAD
|
|
if( (iBytesRead = pread( m_fd, pvBuffer, uiBytesToRead, uiReadOffset)) == -1)
|
|
{
|
|
rc = MapErrnoToFlaimErr(errno, FERR_READING_FILE);
|
|
goto Exit;
|
|
}
|
|
#else
|
|
if( m_uiCurrentPos != uiReadOffset)
|
|
{
|
|
if( lseek( m_fd, uiReadOffset, SEEK_SET) == -1)
|
|
{
|
|
rc = MapErrnoToFlaimErr( errno, FERR_POSITIONING_IN_FILE);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
if( (iBytesRead = read( m_fd, pvBuffer, uiBytesToRead)) == -1)
|
|
{
|
|
rc = MapErrnoToFlaimErr(errno, FERR_READING_FILE);
|
|
goto Exit;
|
|
}
|
|
#endif
|
|
|
|
if( puiBytesRead)
|
|
{
|
|
*puiBytesRead = (FLMUINT)iBytesRead;
|
|
}
|
|
|
|
m_uiCurrentPos = uiReadOffset + (FLMUINT)iBytesRead;
|
|
|
|
if( (FLMUINT)iBytesRead < uiBytesToRead)
|
|
{
|
|
rc = RC_SET( FERR_IO_END_OF_FILE);
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
Note: This function assumes that the pvBuffer that is passed in is
|
|
a multiple of a the sector size.
|
|
****************************************************************************/
|
|
RCODE F_FileHdlImp::SectorRead(
|
|
FLMUINT uiReadOffset,
|
|
FLMUINT uiBytesToRead,
|
|
void * pvBuffer,
|
|
FLMUINT * puiBytesRead)
|
|
{
|
|
if( m_bDoDirectIO)
|
|
{
|
|
return( DirectRead( uiReadOffset, uiBytesToRead,
|
|
pvBuffer, TRUE, puiBytesRead));
|
|
}
|
|
else
|
|
{
|
|
return( Read( uiReadOffset, uiBytesToRead, pvBuffer, puiBytesRead));
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Sets current position of file.
|
|
Note: F_IO_SEEK_END is not supported.
|
|
****************************************************************************/
|
|
RCODE F_FileHdlImp::Seek(
|
|
FLMUINT uiOffset,
|
|
FLMINT iWhence,
|
|
FLMUINT * puiNewOffset)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
|
|
if( RC_BAD( rc = GET_FS_ERROR()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
switch( iWhence)
|
|
{
|
|
case F_IO_SEEK_CUR:
|
|
{
|
|
m_uiCurrentPos += uiOffset;
|
|
break;
|
|
}
|
|
|
|
case F_IO_SEEK_SET:
|
|
{
|
|
m_uiCurrentPos = uiOffset;
|
|
break;
|
|
}
|
|
|
|
case F_IO_SEEK_END:
|
|
{
|
|
if( RC_BAD( rc = Size( &m_uiCurrentPos)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
rc = RC_SET( FERR_NOT_IMPLEMENTED);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
#ifndef HAVE_PREAD
|
|
if( lseek( m_fd, m_uiCurrentPos, SEEK_SET) == -1)
|
|
{
|
|
rc = MapErrnoToFlaimErr( errno, FERR_POSITIONING_IN_FILE);
|
|
goto Exit;
|
|
}
|
|
#endif
|
|
|
|
*puiNewOffset = m_uiCurrentPos;
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Return the size of the file
|
|
****************************************************************************/
|
|
RCODE F_FileHdlImp::Size(
|
|
FLMUINT * puiSize)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
struct stat statBuf;
|
|
|
|
if( RC_BAD( rc = GET_FS_ERROR()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( fstat( m_fd, &statBuf) == -1)
|
|
{
|
|
rc = MapErrnoToFlaimErr( errno, FERR_GETTING_FILE_SIZE);
|
|
goto Exit;
|
|
}
|
|
|
|
// VISIT: We need to change the file handle interface to return 64-bit
|
|
// sizes and offsets. When this is done, remove the FLMUINT typecast below.
|
|
|
|
*puiSize = (FLMUINT)statBuf.st_size;
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Returns m_uiCurrentPos
|
|
****************************************************************************/
|
|
RCODE F_FileHdlImp::Tell(
|
|
FLMUINT * puiOffset)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
|
|
if( RC_BAD( rc = GET_FS_ERROR()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
*puiOffset = m_uiCurrentPos;
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Truncate the file to the indicated size
|
|
WARNING: Direct IO methods are calling this method. Make sure that all
|
|
changes to this method work in direct IO mode.
|
|
****************************************************************************/
|
|
RCODE F_FileHdlImp::Truncate(
|
|
FLMUINT uiSize)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
|
|
flmAssert( m_bFileOpened);
|
|
|
|
if( RC_BAD( rc = GET_FS_ERROR()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( ftruncate( m_fd, uiSize) == -1)
|
|
{
|
|
rc = MapErrnoToFlaimErr( errno, FERR_TRUNCATING_FILE);
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Write to a file
|
|
****************************************************************************/
|
|
RCODE F_FileHdlImp::Write(
|
|
FLMUINT uiWriteOffset,
|
|
FLMUINT uiBytesToWrite,
|
|
const void * pvBuffer,
|
|
FLMUINT * puiBytesWrittenRV)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FLMINT iBytesWritten = 0;
|
|
|
|
flmAssert( m_bFileOpened);
|
|
|
|
if( m_bDoDirectIO)
|
|
{
|
|
rc = DirectWrite( uiWriteOffset, uiBytesToWrite, pvBuffer,
|
|
uiBytesToWrite, NULL, puiBytesWrittenRV, FALSE, TRUE);
|
|
|
|
goto Exit;
|
|
}
|
|
|
|
if( RC_BAD( rc = GET_FS_ERROR()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( uiWriteOffset == F_IO_CURRENT_POS)
|
|
{
|
|
uiWriteOffset = m_uiCurrentPos;
|
|
}
|
|
|
|
#ifdef HAVE_PREAD
|
|
if( (iBytesWritten = pwrite(m_fd, pvBuffer, uiBytesToWrite,
|
|
uiWriteOffset)) == -1)
|
|
{
|
|
rc = MapErrnoToFlaimErr( errno, FERR_WRITING_FILE);
|
|
goto Exit;
|
|
}
|
|
#else
|
|
if( m_uiCurrentPos != uiWriteOffset)
|
|
{
|
|
if( lseek(m_fd, uiWriteOffset, SEEK_SET) == -1)
|
|
{
|
|
rc = MapErrnoToFlaimErr( errno, FERR_POSITIONING_IN_FILE);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
if( (iBytesWritten = write( m_fd, pvBuffer, uiBytesToWrite)) == -1)
|
|
{
|
|
rc = MapErrnoToFlaimErr(errno, FERR_WRITING_FILE);
|
|
goto Exit;
|
|
}
|
|
#endif
|
|
|
|
if( puiBytesWrittenRV)
|
|
{
|
|
*puiBytesWrittenRV = (FLMUINT)iBytesWritten;
|
|
}
|
|
|
|
m_uiCurrentPos = uiWriteOffset + (FLMUINT)iBytesWritten;
|
|
|
|
if( (FLMUINT)iBytesWritten < uiBytesToWrite)
|
|
{
|
|
rc = RC_SET( FERR_IO_DISK_FULL);
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Allocate an aligned buffer.
|
|
****************************************************************************/
|
|
RCODE F_FileHdlImp::AllocAlignBuffer( void)
|
|
{
|
|
#if !defined( FLM_LINUX) && !defined( FLM_SOLARIS)
|
|
return( RC_SET( FERR_NOT_IMPLEMENTED));
|
|
#else
|
|
RCODE rc = FERR_OK;
|
|
|
|
if( m_pucAlignedBuff)
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
// Allocate at least 64K - this will handle most read and write
|
|
// operations and will also be a multiple of the sector size most of
|
|
// the time. The calculation below rounds it up to the next sector
|
|
// boundary if it is not already on one.
|
|
|
|
m_uiAlignedBuffSize = RoundUpToSectorMultiple( 64 * 1024);
|
|
|
|
if( (m_pucAlignedBuff = (FLMBYTE *)memalign(
|
|
sysconf(_SC_PAGESIZE), m_uiAlignedBuffSize)) == NULL)
|
|
{
|
|
m_uiAlignedBuffSize = 0;
|
|
rc = MapErrnoToFlaimErr( errno, FERR_MEM);
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
#endif
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
Note: This routine assumes that the size of pvBuffer is a multiple of
|
|
sector size and can be used to write out full sectors. Even if
|
|
uiBytesToWrite does not account for full sectors, data from the
|
|
buffer will still be written out - a partial sector on disk will
|
|
not be preserved.
|
|
****************************************************************************/
|
|
RCODE F_FileHdlImp::DirectWrite(
|
|
FLMUINT uiWriteOffset,
|
|
FLMUINT uiBytesToWrite,
|
|
const void * pvBuffer,
|
|
FLMUINT,
|
|
F_IOBuffer * pBufferObj,
|
|
FLMUINT * puiBytesWrittenRV,
|
|
FLMBOOL bBuffHasFullSectors,
|
|
FLMBOOL bZeroFill)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
FLMUINT uiBytesRead;
|
|
FLMUINT uiMaxBytesToWrite;
|
|
FLMUINT uiBytesBeingOutput;
|
|
FLMBYTE * pucWriteBuffer;
|
|
FLMBYTE * pucSrcBuffer;
|
|
FLMBOOL bDoAsync = (pBufferObj != NULL)
|
|
? TRUE
|
|
: FALSE;
|
|
FLMBOOL bDidAsync = FALSE;
|
|
FLMUINT uiLastWriteOffset;
|
|
FLMUINT uiLastWriteSize;
|
|
|
|
flmAssert( m_bFileOpened);
|
|
|
|
#ifdef FLM_DEBUG
|
|
if( bDoAsync)
|
|
{
|
|
flmAssert( m_bCanDoAsync);
|
|
}
|
|
#else
|
|
(void)bDoAsync;
|
|
#endif
|
|
|
|
if( puiBytesWrittenRV)
|
|
{
|
|
*puiBytesWrittenRV = 0;
|
|
}
|
|
|
|
if( RC_BAD( rc = GET_FS_ERROR()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( uiWriteOffset == F_IO_CURRENT_POS)
|
|
{
|
|
uiWriteOffset = m_uiCurrentPos;
|
|
}
|
|
|
|
// This loop is for direct IO - must make sure we use
|
|
// aligned buffers.
|
|
|
|
pucSrcBuffer = (FLMBYTE *)pvBuffer;
|
|
for (;;)
|
|
{
|
|
// See if we are using an aligned buffer. If not, allocate
|
|
// one (if not already allocated), and use it.
|
|
|
|
if( (uiWriteOffset & m_uiNotOnSectorBoundMask) ||
|
|
(((FLMUINT)pucSrcBuffer) & m_uiNotOnSectorBoundMask) ||
|
|
((uiBytesToWrite & m_uiNotOnSectorBoundMask) && !bBuffHasFullSectors))
|
|
{
|
|
// Cannot be using a temporary write buffer if we are doing
|
|
// asynchronous writes!
|
|
|
|
flmAssert( !bDoAsync || !m_bCanDoAsync);
|
|
|
|
if( !m_pucAlignedBuff)
|
|
{
|
|
if( RC_BAD( rc = AllocAlignBuffer()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
pucWriteBuffer = m_pucAlignedBuff;
|
|
|
|
// Must write enough bytes to cover all of the sectors that
|
|
// contain the data we are trying to write out. The value of
|
|
// (uiWriteOffset & m_uiNotOnSectorBoundMask) will give us the
|
|
// number of additional bytes that are in the sector prior to
|
|
// the read offset. We then round to the next sector to get the
|
|
// total number of bytes we are going to write.
|
|
|
|
uiMaxBytesToWrite = RoundUpToSectorMultiple( uiBytesToWrite +
|
|
(uiWriteOffset & m_uiNotOnSectorBoundMask));
|
|
|
|
// Can't write more than the aligned buffer will hold.
|
|
|
|
if( uiMaxBytesToWrite > m_uiAlignedBuffSize)
|
|
{
|
|
uiMaxBytesToWrite = m_uiAlignedBuffSize;
|
|
uiBytesBeingOutput = uiMaxBytesToWrite -
|
|
(uiWriteOffset & m_uiNotOnSectorBoundMask);
|
|
}
|
|
else
|
|
{
|
|
uiBytesBeingOutput = uiBytesToWrite;
|
|
}
|
|
|
|
// If the write offset is not on a sector boundary, or if
|
|
// we are writing a partial sector, we must read the
|
|
// sector into the buffer.
|
|
|
|
if( (uiWriteOffset & m_uiNotOnSectorBoundMask) ||
|
|
(uiBytesBeingOutput < m_uiBytesPerSector && !bBuffHasFullSectors))
|
|
{
|
|
// Read the first sector that is to be written out.
|
|
// Read one sector's worth of data - so that we will
|
|
// preserve what is already in the sector before
|
|
// writing it back out again.
|
|
|
|
if( RC_BAD( rc = Read( GetSectorStartOffset( uiWriteOffset),
|
|
m_uiBytesPerSector, pucWriteBuffer, &uiBytesRead)))
|
|
{
|
|
if( rc != FERR_IO_END_OF_FILE)
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
rc = FERR_OK;
|
|
f_memset( &pucWriteBuffer[ uiBytesRead], 0,
|
|
m_uiBytesPerSector - uiBytesRead);
|
|
}
|
|
}
|
|
|
|
// Finally, copy the data from the source buffer into the
|
|
// write buffer.
|
|
|
|
f_memcpy( &pucWriteBuffer[ uiWriteOffset & m_uiNotOnSectorBoundMask],
|
|
pucSrcBuffer, uiBytesBeingOutput);
|
|
}
|
|
else
|
|
{
|
|
uiMaxBytesToWrite = RoundUpToSectorMultiple( uiBytesToWrite);
|
|
uiBytesBeingOutput = uiBytesToWrite;
|
|
pucWriteBuffer = pucSrcBuffer;
|
|
|
|
if( bZeroFill && uiMaxBytesToWrite > uiBytesToWrite)
|
|
{
|
|
f_memset( &pucWriteBuffer[ uiBytesToWrite], 0,
|
|
uiMaxBytesToWrite - uiBytesToWrite);
|
|
}
|
|
}
|
|
|
|
// Position the file to the nearest sector below the write offset.
|
|
|
|
uiLastWriteOffset = GetSectorStartOffset( uiWriteOffset);
|
|
uiLastWriteSize = uiMaxBytesToWrite;
|
|
|
|
if( !m_bCanDoAsync || !pBufferObj)
|
|
{
|
|
FLMINT iBytesWritten;
|
|
|
|
#ifdef HAVE_PREAD
|
|
if( (iBytesWritten = pwrite( m_fd,
|
|
pucWriteBuffer, uiMaxBytesToWrite, uiLastWriteOffset)) == -1)
|
|
{
|
|
rc = MapErrnoToFlaimErr( errno, FERR_WRITING_FILE);
|
|
goto Exit;
|
|
}
|
|
#else
|
|
if( lseek( m_fd, uiLastWriteOffset, SEEK_SET) == -1)
|
|
{
|
|
rc = MapErrnoToFlaimErr( errno, FERR_POSITIONING_IN_FILE);
|
|
goto Exit;
|
|
}
|
|
|
|
if( (iBytesWritten = write( m_fd, pucWriteBuffer, uiMaxBytesToWrite)) == -1)
|
|
{
|
|
rc = MapErrnoToFlaimErr( errno, FERR_WRITING_FILE);
|
|
goto Exit;
|
|
}
|
|
#endif
|
|
|
|
if( (FLMUINT)iBytesWritten < uiMaxBytesToWrite)
|
|
{
|
|
rc = RC_SET( FERR_IO_DISK_FULL);
|
|
goto Exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
flmAssert( 0);
|
|
}
|
|
|
|
uiBytesToWrite -= uiBytesBeingOutput;
|
|
if( puiBytesWrittenRV)
|
|
{
|
|
(*puiBytesWrittenRV) += uiBytesBeingOutput;
|
|
}
|
|
|
|
m_uiCurrentPos = uiWriteOffset + uiBytesBeingOutput;
|
|
|
|
if( !uiBytesToWrite)
|
|
{
|
|
break;
|
|
}
|
|
|
|
flmAssert( !pBufferObj);
|
|
|
|
pucSrcBuffer += uiBytesBeingOutput;
|
|
uiWriteOffset += uiBytesBeingOutput;
|
|
}
|
|
|
|
Exit:
|
|
|
|
if( !bDidAsync && pBufferObj)
|
|
{
|
|
pBufferObj->notifyComplete( rc);
|
|
}
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Returns flag indicating whether or not we can do async writes.
|
|
****************************************************************************/
|
|
FLMBOOL F_FileHdlImp::CanDoAsync( void)
|
|
{
|
|
return( m_bCanDoAsync);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Attempts to lock byte 0 of the file. This method is used to
|
|
lock byte 0 of the .lck file to ensure that only one process
|
|
has access to a database.
|
|
****************************************************************************/
|
|
RCODE F_FileHdlImp::Lock( void)
|
|
{
|
|
RCODE rc = FERR_OK;
|
|
struct flock LockStruct;
|
|
|
|
// Lock first byte in file
|
|
|
|
f_memset( &LockStruct, 0, sizeof( LockStruct));
|
|
LockStruct.l_type = F_WRLCK;
|
|
LockStruct.l_whence = SEEK_SET;
|
|
LockStruct.l_start = 0;
|
|
LockStruct.l_len = 1;
|
|
|
|
if( fcntl( m_fd, F_SETLK, &LockStruct) == -1)
|
|
{
|
|
rc = RC_SET( FERR_IO_FILE_LOCK_ERR);
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Attempts to unlock byte 0 of the file.
|
|
****************************************************************************/
|
|
RCODE F_FileHdlImp::Unlock( void)
|
|
{
|
|
struct flock LockStruct;
|
|
RCODE rc = FERR_OK;
|
|
|
|
// Lock first byte in file
|
|
|
|
f_memset( &LockStruct, 0, sizeof( LockStruct));
|
|
LockStruct.l_type = F_UNLCK;
|
|
LockStruct.l_whence = SEEK_SET;
|
|
LockStruct.l_start = 0;
|
|
LockStruct.l_len = 1;
|
|
|
|
if( fcntl( m_fd, F_SETLK, &LockStruct) == -1)
|
|
{
|
|
rc = RC_SET( FERR_IO_FILE_UNLOCK_ERR);
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/***************************************************************************
|
|
Desc: Determines the kernel version of a linux system
|
|
***************************************************************************/
|
|
#ifdef FLM_LINUX
|
|
void flmGetLinuxKernelVersion(
|
|
FLMUINT * puiMajor,
|
|
FLMUINT * puiMinor,
|
|
FLMUINT * puiRevision)
|
|
{
|
|
int fd = INVALID_HANDLE_VALUE;
|
|
int iBytesRead;
|
|
char szBuffer [80];
|
|
char * pszVer;
|
|
FLMUINT uiMajorVer = 0;
|
|
FLMUINT uiMinorVer = 0;
|
|
FLMUINT uiRevision = 0;
|
|
|
|
if( (fd = open( "/proc/version", O_RDONLY, 0600)) == INVALID_HANDLE_VALUE)
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( (iBytesRead = read( fd, szBuffer, sizeof( szBuffer))) == -1)
|
|
{
|
|
goto Exit;
|
|
}
|
|
if( (pszVer = f_strstr( szBuffer, "version ")) == NULL)
|
|
{
|
|
goto Exit;
|
|
}
|
|
pszVer += 8;
|
|
|
|
while( *pszVer >= '0' && *pszVer <= '9')
|
|
{
|
|
uiMajorVer *= 10;
|
|
uiMajorVer += (FLMUINT)(*pszVer - '0');
|
|
pszVer++;
|
|
}
|
|
|
|
if( *pszVer == '.')
|
|
{
|
|
pszVer++;
|
|
while (*pszVer >= '0' && *pszVer <= '9')
|
|
{
|
|
uiMinorVer *= 10;
|
|
uiMinorVer += (FLMUINT)(*pszVer - '0');
|
|
pszVer++;
|
|
}
|
|
}
|
|
|
|
if( *pszVer == '.')
|
|
{
|
|
pszVer++;
|
|
while (*pszVer >= '0' && *pszVer <= '9')
|
|
{
|
|
uiRevision *= 10;
|
|
uiRevision += (FLMUINT)(*pszVer - '0');
|
|
pszVer++;
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
if( fd != INVALID_HANDLE_VALUE)
|
|
{
|
|
close( fd);
|
|
}
|
|
|
|
if( puiMajor)
|
|
{
|
|
*puiMajor = uiMajorVer;
|
|
}
|
|
|
|
if( puiMinor)
|
|
{
|
|
*puiMinor = uiMinorVer;
|
|
}
|
|
|
|
if( puiRevision)
|
|
{
|
|
*puiRevision = uiRevision;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/***************************************************************************
|
|
Desc: Determines if the linux system we are running on is 2.4 or greater.
|
|
***************************************************************************/
|
|
#ifdef FLM_LINUX
|
|
FLMUINT flmGetLinuxMaxFileSize(
|
|
FLMUINT uiSizeofFLMUINT)
|
|
{
|
|
FLMUINT uiMaxFileSize = MAX_FILE_SIZE_VER40;
|
|
|
|
// Determine if we are on 32 or 64 bit platform. If on 64 bit, we can
|
|
// support the larger file size. Otherwise, we have to open up the
|
|
// /proc/version file and see if we are linux version 2.4 or greater.
|
|
|
|
// NOTE: The only reason we are passing uiSizeofFLMUINT in as a parameter is
|
|
// to spoof the compiler so it won't give us a warning. If we did
|
|
// if (sizeof( FLMUINT) > 4) the compiler would give us a warning that the
|
|
// condition is either always TRUE or FALSE (depending on whether we are
|
|
// on a 32 bit or 64 bit platform.
|
|
|
|
if( uiSizeofFLMUINT > 4)
|
|
{
|
|
uiMaxFileSize = F_MAXIMUM_FILE_SIZE;
|
|
goto Exit;
|
|
}
|
|
|
|
flmAssert( gv_FlmSysData.uiLinuxMajorVer);
|
|
|
|
// Is version 2.4 or greater?
|
|
|
|
if( gv_FlmSysData.uiLinuxMajorVer > 2 ||
|
|
(gv_FlmSysData.uiLinuxMajorVer == 2 &&
|
|
gv_FlmSysData.uiLinuxMinorVer >= 4))
|
|
{
|
|
uiMaxFileSize = F_MAXIMUM_FILE_SIZE;
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( uiMaxFileSize);
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
Desc: This routine gets the block size for the file system a file belongs to.
|
|
****************************************************************************/
|
|
FLMUINT flmGetFSBlockSize(
|
|
const char * pszFileName)
|
|
{
|
|
#define DEFAULT_FS_BSIZE 1024
|
|
FLMUINT uiFSBlkSize = DEFAULT_FS_BSIZE;
|
|
char szTmpBuf[ F_PATH_MAX_SIZE];
|
|
char * pszTmp;
|
|
const char * pszDir;
|
|
|
|
f_strcpy( szTmpBuf, pszFileName);
|
|
pszTmp = szTmpBuf + f_strlen( szTmpBuf) - 1;
|
|
|
|
while( pszTmp != &szTmpBuf[ 0] && *pszTmp != '/')
|
|
{
|
|
pszTmp--;
|
|
}
|
|
|
|
if( *pszTmp == '/')
|
|
{
|
|
if( pszTmp == &szTmpBuf[ 0])
|
|
{
|
|
pszTmp++;
|
|
}
|
|
|
|
*pszTmp = 0;
|
|
pszDir = szTmpBuf;
|
|
}
|
|
else
|
|
{
|
|
pszDir = ".";
|
|
}
|
|
|
|
#if defined( FLM_SOLARIS)
|
|
|
|
struct statvfs statfsbuf;
|
|
|
|
if( statvfs( pszDir, &statfsbuf) == 0)
|
|
{
|
|
uiFSBlkSize = (FLMUINT)statfsbuf.f_bsize;
|
|
}
|
|
|
|
#elif defined( FLM_LINUX)
|
|
|
|
struct statfs statfsbuf;
|
|
|
|
if( statfs( pszDir, &statfsbuf) == 0)
|
|
{
|
|
uiFSBlkSize = (FLMUINT)statfsbuf.f_bsize;
|
|
}
|
|
|
|
#endif
|
|
|
|
return( uiFSBlkSize);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
#if defined(__GNUC__) && defined(__i386__)
|
|
__attribute__((always_inline))
|
|
static FINLINE unsigned int atomic_inc(
|
|
volatile unsigned int * p)
|
|
{
|
|
unsigned int rv;
|
|
|
|
__asm__ __volatile__(
|
|
"movl $1, %%eax\n\t"
|
|
"lock\n\t"
|
|
"xaddl %%eax, (%%ecx)\n\t"
|
|
"incl %%eax"
|
|
: "=a" (rv)
|
|
: "c" (p)
|
|
);
|
|
|
|
return rv;
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
#if defined(__GNUC__) && defined(__i386__)
|
|
FLMUINT32 ftkAtomicIncrement(
|
|
FLMUINT32 * pui32Target)
|
|
{
|
|
return( atomic_inc( pui32Target));
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
#if defined(__GNUC__) && defined(__i386__)
|
|
__attribute__((always_inline))
|
|
static FINLINE unsigned int atomic_dec(
|
|
volatile unsigned int * p)
|
|
{
|
|
unsigned int rv;
|
|
|
|
__asm__ __volatile__(
|
|
"movl $-1, %%eax\n\t"
|
|
"lock\n\t"
|
|
"xaddl %%eax, (%%ecx)\n\t"
|
|
"decl %%eax"
|
|
: "=a" (rv)
|
|
: "c" (p)
|
|
); // result left in eax
|
|
|
|
return rv;
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
#if defined(__GNUC__) && defined(__i386__)
|
|
FLMUINT32 ftkAtomicDecrement(
|
|
FLMUINT32 * pui32Target)
|
|
{
|
|
return( atomic_dec( pui32Target));
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
#if defined(__GNUC__) && defined(__i386__)
|
|
__attribute__((always_inline))
|
|
static FINLINE unsigned int atomic_xchg(
|
|
volatile unsigned int * p,
|
|
unsigned int i)
|
|
{
|
|
unsigned int rv;
|
|
|
|
__asm__ __volatile__(
|
|
"xchgl %%eax, (%%ecx)"
|
|
: "=a" (rv)
|
|
: "c" (p), "a" (i)
|
|
); // result left in eax, no buslock required for xchgl
|
|
|
|
return rv;
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
#if defined(__GNUC__) && defined(__i386__)
|
|
FLMUINT32 ftkAtomicExchange(
|
|
FLMUINT32 * puiTarget,
|
|
FLMUINT32 ui32Value)
|
|
{
|
|
return( atomic_xchg( puiTarget, ui32Value));
|
|
}
|
|
#endif
|
|
|
|
#elif defined( FLM_WATCOM_NLM)
|
|
int fposixDummy(void)
|
|
{
|
|
return( 0);
|
|
}
|
|
#endif
|