Files
mars-flaim/ftk/src/ftkfsys.cpp

3881 lines
83 KiB
C++

//------------------------------------------------------------------------------
// Desc: Contains the methods for the F_FileSystem class.
//
// 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: ffilesys.cpp 3112 2006-01-19 13:12:40 -0700 (Thu, 19 Jan 2006) dsanders $
//------------------------------------------------------------------------------
#include "ftksys.h"
class F_FileHdlCache;
FSTATIC FLMBOOL f_canReducePath(
const char * pszSource);
FSTATIC const char * f_findFileNameStart(
const char * pszPath);
FSTATIC char * f_getPathComponent(
char ** ppszPath,
FLMUINT * puiEndChar);
/****************************************************************************
Desc:
****************************************************************************/
class F_CachedFileHdl : public F_FileHdl, public F_HashObject
{
public:
F_CachedFileHdl()
{
m_pucKey = NULL;
m_uiKeyLen = 0;
m_bInAvailList = FALSE;
m_pFileHdlCache = NULL;
}
virtual ~F_CachedFileHdl()
{
if( m_pucKey)
{
f_free( &m_pucKey);
}
}
FLMINT FLMAPI AddRef( void)
{
return( ++m_refCnt);
}
FLMINT FLMAPI Release( void);
FINLINE const void * FLMAPI getKey( void)
{
return( m_pucKey);
}
FINLINE FLMUINT FLMAPI getKeyLength( void)
{
return( m_uiKeyLen);
}
FINLINE FLMUINT FLMAPI getObjectType( void)
{
return( 0);
}
private:
FLMBYTE * m_pucKey;
FLMUINT m_uiKeyLen;
FLMBOOL m_bInAvailList;
F_FileHdlCache * m_pFileHdlCache;
friend class F_FileHdlCache;
};
/****************************************************************************
Desc:
****************************************************************************/
class F_FileHdlCache : public IF_FileHdlCache
{
public:
F_FileHdlCache();
virtual ~F_FileHdlCache();
RCODE setup(
FLMUINT uiMaxCachedFiles,
FLMUINT uiIdleTimeoutSecs);
FINLINE RCODE FLMAPI openFile(
const char * pszFileName,
FLMUINT uiIoFlags,
IF_FileHdl ** ppFile)
{
return( openOrCreate( pszFileName, uiIoFlags, FALSE, ppFile));
}
FINLINE RCODE FLMAPI createFile(
const char * pszFileName,
FLMUINT uiIoFlags,
IF_FileHdl ** ppFile)
{
return( openOrCreate( pszFileName, uiIoFlags, TRUE, ppFile));
}
void FLMAPI closeUnusedFiles(
FLMUINT uiUnusedTime);
FLMUINT FLMAPI getOpenThreshold( void)
{
return( m_pAvailList->getMaxObjects());
}
RCODE FLMAPI setOpenThreshold(
FLMUINT uiMaxOpenFiles);
private:
RCODE openOrCreate(
const char * pszFileName,
FLMUINT uiIoFlags,
FLMBOOL bCreate,
IF_FileHdl ** ppFile);
static RCODE FLMAPI timeoutThread(
IF_Thread * pThread);
IF_Thread * m_pTimeoutThread;
F_HashTable * m_pAvailList;
FLMUINT m_uiMaxIdleTime;
friend class F_CachedFileHdl;
};
/****************************************************************************
Desc:
****************************************************************************/
FINLINE void f_hexToNative(
FLMBYTE ucHexVal,
char * pszNativeChar)
{
*pszNativeChar = (char)(ucHexVal < 10
? ucHexVal + NATIVE_ZERO
: (ucHexVal - 10) + NATIVE_LOWER_A);
}
/****************************************************************************
Desc:
****************************************************************************/
FINLINE void f_setupTime(
FLMUINT * puiBaseTime,
FLMBYTE * pbyHighByte)
{
FLMUINT uiSdTime = 0;
f_timeGetSeconds( &uiSdTime);
*pbyHighByte = (FLMBYTE)(uiSdTime >> 24);
uiSdTime = uiSdTime << 5;
if( *puiBaseTime < uiSdTime)
{
*puiBaseTime = uiSdTime;
}
}
/****************************************************************************
Desc: Returns TRUE if character is a "slash" separator
****************************************************************************/
FINLINE FLMBOOL f_isSlashSeparator(
char cChar)
{
#ifdef FLM_UNIX
return( cChar == '/' ? TRUE : FALSE);
#else
return( cChar == '/' || cChar == '\\' ? TRUE : FALSE);
#endif
}
/****************************************************************************
Desc: Return a pointer to the next path component in ppszPath.
****************************************************************************/
FSTATIC char * f_getPathComponent(
char ** ppszPath,
FLMUINT * puiEndChar)
{
char * pszComponent;
char * pszEnd;
pszComponent = pszEnd = *ppszPath;
if (f_isSlashSeparator( *pszEnd))
{
// handle the condition of sys:\system the colon would have terminated
// the previous token, to pComponent would now be pointing at the '\'.
// We need to move past the '\' to find the next token.
pszEnd++;
}
// Find the end of the path component
while (*pszEnd)
{
if (f_isSlashSeparator( *pszEnd)
#ifndef FLM_UNIX
|| *pszEnd == ':'
#endif
)
{
break;
}
pszEnd++;
}
if (*pszEnd)
{
// A delimiter was found, assume that there is another path component
// after this one.
// Return a pointer to the beginning of the next path component
*ppszPath = pszEnd + 1;
*puiEndChar = *pszEnd;
// NULL terminate the path component
*pszEnd = 0;
}
else
{
// There is no "next path component" so return a pointer to the
// NULL-terminator
*ppszPath = pszEnd;
*puiEndChar = 0;
}
// Return the path component
return( pszComponent);
}
/****************************************************************************
Desc: Will determine whether any format of (UNC, drive based, NetWare
UNC) path can be reduced any further.
****************************************************************************/
FSTATIC FLMBOOL f_canReducePath(
const char * pszSource)
{
#if defined FLM_UNIX
F_UNREFERENCED_PARM( pszSource);
return( TRUE);
#else
FLMBOOL bCanReduce;
const char * pszTemp = pszSource;
// Determine whether the passed path is UNC or not
// (UNC format is: \\FileServer\Volume\Path).
if (f_strncmp( "\\\\", pszSource, 2 ) == 0)
{
FLMUINT uiSlashCount = 0;
pszTemp += 2;
// Search forward for at least two slash separators
// If we find at least two, the path can be reduced.
bCanReduce = FALSE;
while (*pszTemp)
{
pszTemp++;
if (f_isSlashSeparator( *pszTemp))
{
++uiSlashCount;
if (uiSlashCount == 2)
{
bCanReduce = TRUE;
break;
}
}
}
}
else
{
bCanReduce = TRUE;
// Search forward for the colon.
while (*pszTemp)
{
if (*pszTemp == ':')
{
// If nothing comes after the colon,
// we can't reduce any more.
if (*(pszTemp + 1) == 0)
{
bCanReduce = FALSE;
}
break;
}
pszTemp++;
}
}
return( bCanReduce);
#endif
}
/****************************************************************************
Desc: Return pointer to start of filename part of path.
Search for the last slash separator.
****************************************************************************/
FSTATIC const char * f_findFileNameStart(
const char * pszPath)
{
const char * pszFileNameStart;
pszFileNameStart = pszPath;
while (*pszPath)
{
if (f_isSlashSeparator( *pszPath))
{
pszFileNameStart = pszPath + 1;
}
pszPath++;
}
return( pszFileNameStart);
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE f_allocFileSystem(
IF_FileSystem ** ppFileSystem)
{
RCODE rc = NE_FLM_OK;
F_FileSystem * pFileSystem = NULL;
if( (pFileSystem = f_new F_FileSystem) == NULL)
{
return( RC_SET( NE_FLM_MEM));
}
if( RC_BAD( rc = pFileSystem->setup()))
{
goto Exit;
}
*ppFileSystem = pFileSystem;
pFileSystem = NULL;
Exit:
if( pFileSystem)
{
pFileSystem->Release();
}
return( rc);
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE FLMAPI FlmGetFileSystem(
IF_FileSystem ** ppFileSystem)
{
*ppFileSystem = f_getFileSysPtr();
(*ppFileSystem)->AddRef();
return( NE_FLM_OK);
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE f_allocFileHdl(
F_FileHdl ** ppFileHdl)
{
if( (*ppFileHdl = f_new F_FileHdl) == NULL)
{
return( RC_SET( NE_FLM_MEM));
}
return( NE_FLM_OK);
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE F_FileSystem::setup( void)
{
#if defined( FLM_WIN)
OSVERSIONINFO versionInfo;
versionInfo.dwOSVersionInfoSize = sizeof( OSVERSIONINFO);
if( !GetVersionEx( &versionInfo))
{
return( f_mapPlatformError( GetLastError(), NE_FLM_FAILURE));
}
// Async writes are not supported on Win32s (3.1) or
// Win95, 98, ME, etc.
m_bCanDoAsync =
(versionInfo.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS &&
versionInfo.dwPlatformId != VER_PLATFORM_WIN32s)
? TRUE
: FALSE;
#else
m_bCanDoAsync = TRUE;
#endif
#if !defined( FLM_HAS_ASYNC_IO)
m_bCanDoAsync = FALSE;
#endif
return( NE_FLM_OK);
}
/****************************************************************************
Desc: Create a file, return a file handle to created file.
****************************************************************************/
RCODE FLMAPI F_FileSystem::createFile(
const char * pszFileName,
FLMUINT uiIoFlags,
IF_FileHdl ** ppFileHdl)
{
RCODE rc = NE_FLM_OK;
F_FileHdl * pFileHdl = NULL;
if( RC_BAD( rc = f_allocFileHdl( &pFileHdl)))
{
goto Exit;
}
if (RC_BAD( rc = pFileHdl->createFile( pszFileName, uiIoFlags)))
{
goto Exit;
}
*ppFileHdl = pFileHdl;
pFileHdl = NULL;
Exit:
if( pFileHdl)
{
pFileHdl->Release();
}
return( rc);
}
/****************************************************************************
Desc: Create a unique file, return a file handle to created file.
****************************************************************************/
RCODE FLMAPI F_FileSystem::createUniqueFile(
char * pszPath,
const char * pszFileExtension,
FLMUINT uiIoFlags,
IF_FileHdl ** ppFileHdl)
{
RCODE rc = NE_FLM_OK;
F_FileHdl * pFileHdl = NULL;
if( RC_BAD( rc = f_allocFileHdl( &pFileHdl)))
{
goto Exit;
}
if( RC_BAD( rc = pFileHdl->createUniqueFile( pszPath,
pszFileExtension, uiIoFlags)))
{
goto Exit;
}
*ppFileHdl = pFileHdl;
pFileHdl = NULL;
Exit:
if( pFileHdl)
{
pFileHdl->Release();
}
return( rc);
}
/****************************************************************************
Desc: Open a file, return a file handle to opened file.
****************************************************************************/
RCODE FLMAPI F_FileSystem::openFile(
const char * pszFileName,
FLMUINT uiIoFlags,
IF_FileHdl ** ppFileHdl)
{
RCODE rc = NE_FLM_OK;
F_FileHdl * pFileHdl = NULL;
if( RC_BAD( rc = f_allocFileHdl( &pFileHdl)))
{
goto Exit;
}
if (RC_BAD( rc = pFileHdl->openFile( pszFileName, uiIoFlags)))
{
goto Exit;
}
*ppFileHdl = pFileHdl;
pFileHdl = NULL;
Exit:
if( pFileHdl)
{
pFileHdl->Release();
}
return( rc);
}
/****************************************************************************
Desc: Open a directory, return a file handle to opened directory.
****************************************************************************/
RCODE FLMAPI F_FileSystem::openDir(
const char * pszDirName,
const char * pszPattern,
IF_DirHdl ** ppDirHdl)
{
RCODE rc = NE_FLM_OK;
F_DirHdl * pDirHdl = NULL;
if( RC_BAD( rc = f_allocDirHdl( &pDirHdl)))
{
goto Exit;
}
if( RC_BAD( rc = pDirHdl->openDir( pszDirName, pszPattern)))
{
goto Exit;
}
*ppDirHdl = (IF_DirHdl *)pDirHdl;
pDirHdl = NULL;
Exit:
if( pDirHdl)
{
pDirHdl->Release();
}
return( rc);
}
/****************************************************************************
Desc: Create a directory.
****************************************************************************/
RCODE FLMAPI F_FileSystem::createDir(
const char * pszDirName)
{
RCODE rc = NE_FLM_OK;
F_DirHdl * pDirHdl = NULL;
if( RC_BAD( rc = f_allocDirHdl( &pDirHdl)))
{
goto Exit;
}
if( RC_BAD( rc = pDirHdl->createDir( pszDirName)))
{
goto Exit;
}
Exit:
if (pDirHdl)
{
pDirHdl->Release();
}
return( rc);
}
/****************************************************************************
Desc: Remove a directory
****************************************************************************/
RCODE FLMAPI F_FileSystem::removeDir(
const char * pszDirName,
FLMBOOL bClear)
{
RCODE rc = NE_FLM_OK;
IF_DirHdl * pDirHdl = NULL;
char szFilePath[ F_PATH_MAX_SIZE];
if( bClear)
{
if( RC_BAD( rc = openDir( pszDirName, (char *)"*", &pDirHdl)))
{
goto Exit;
}
for( rc = pDirHdl->next(); RC_OK( rc) ; rc = pDirHdl->next())
{
pDirHdl->currentItemPath( szFilePath);
if( !pDirHdl->currentItemIsDir())
{
if( RC_BAD( rc = deleteFile( szFilePath)))
{
if( rc == NE_FLM_IO_PATH_NOT_FOUND ||
rc == NE_FLM_IO_INVALID_FILENAME)
{
rc = NE_FLM_OK;
}
else
{
goto Exit;
}
}
}
else
{
if( RC_BAD( rc = removeDir( szFilePath, bClear)))
{
if( rc == NE_FLM_IO_PATH_NOT_FOUND ||
rc == NE_FLM_IO_INVALID_FILENAME)
{
rc = NE_FLM_OK;
}
else
{
goto Exit;
}
}
}
}
// Need to release the directory handle so the
// directory will be closed when we try to delete it
// below.
pDirHdl->Release();
pDirHdl = NULL;
}
if( RC_BAD( rc = removeEmptyDir( pszDirName)))
{
goto Exit;
}
Exit:
if (pDirHdl)
{
pDirHdl->Release();
}
return( rc);
}
/****************************************************************************
Desc: Remove an empty directory
****************************************************************************/
RCODE F_FileSystem::removeEmptyDir(
const char * pszDirPath)
{
#if defined( FLM_WIN)
if( !RemoveDirectory( (LPTSTR)pszDirPath))
{
return( f_mapPlatformError( GetLastError(), NE_FLM_IO_DELETING_FILE));
}
return( NE_FLM_OK);
#elif defined( FLM_UNIX) || defined( FLM_LIBC_NLM)
if( rmdir( pszDirPath) == -1 )
{
return( f_mapPlatformError( errno, NE_FLM_IO_DELETING_FILE));
}
return( NE_FLM_OK);
#elif defined( FLM_RING_ZERO_NLM)
return( f_netwareRemoveDir( pszDirPath));
#endif
}
/****************************************************************************
Desc: Determine if a file or directory exists.
****************************************************************************/
RCODE FLMAPI F_FileSystem::doesFileExist(
const char * pszPath)
{
#if defined( FLM_WIN)
DWORD dwFileAttr = GetFileAttributes( (LPTSTR)pszPath);
if( dwFileAttr == (DWORD)-1)
return RC_SET( NE_FLM_IO_PATH_NOT_FOUND);
return NE_FLM_OK;
#elif defined( FLM_UNIX) || defined( FLM_LIBC_NLM)
if( access( pszPath, F_OK) == -1)
{
return( f_mapPlatformError( errno, NE_FLM_CHECKING_FILE_EXISTENCE));
}
return( NE_FLM_OK);
#elif defined( FLM_RING_ZERO_NLM)
return( f_netwareTestIfFileExists( pszPath));
#endif
}
/****************************************************************************
Desc: Get the time stamp of the last modification to this file.
Notes:
puiTimeStamp is assumed to point to a DWORD.
NLM Notes:
We could call MapPathToDirectoryNumber and GetDirectoryEntry directly.
This works, providing that the high byte of the directory entry (returned
by MapPathToDirectoryNumber) is masked off. Otherwise, GetDirectoryEntry
will generate an abend.
We have opted to call a higher level function, GetEntryFromPathStringBase,
which calls the lower level functions for us.
****************************************************************************/
RCODE FLMAPI F_FileSystem::getFileTimeStamp(
const char * pszPath,
FLMUINT * puiTimeStamp)
{
#if defined( FLM_WIN)
WIN32_FIND_DATA find_data;
FILETIME ftLocalFileTime;
SYSTEMTIME stLastFileWriteTime;
HANDLE hSearch = INVALID_HANDLE_VALUE;
RCODE rc = NE_FLM_OK;
F_TMSTAMP tmstamp;
hSearch = FindFirstFile( (LPTSTR)pszPath, &find_data);
if( hSearch == INVALID_HANDLE_VALUE)
{
rc = f_mapPlatformError( GetLastError(), NE_FLM_OPENING_FILE);
switch( rc)
{
case NE_FLM_IO_NO_MORE_FILES:
rc = RC_SET( NE_FLM_IO_PATH_NOT_FOUND);
goto Exit;
default:
goto Exit;
}
}
// Convert it to a local time
if( FileTimeToLocalFileTime( &(find_data.ftLastWriteTime),
&ftLocalFileTime) == FALSE)
{
rc = f_mapPlatformError( GetLastError(), NE_FLM_OPENING_FILE);
goto Exit;
}
// Convert the local time to a system time
if( FileTimeToSystemTime( &ftLocalFileTime,
&stLastFileWriteTime) == FALSE)
{
rc = f_mapPlatformError( GetLastError(), NE_FLM_OPENING_FILE);
goto Exit;
}
// Fill the time stamp structure
f_memset( &tmstamp, 0, sizeof( F_TMSTAMP));
tmstamp.hour = (FLMBYTE)stLastFileWriteTime.wHour;
tmstamp.minute = (FLMBYTE)stLastFileWriteTime.wMinute;
tmstamp.second = (FLMBYTE)stLastFileWriteTime.wSecond;
tmstamp.hundredth = (FLMBYTE)stLastFileWriteTime.wMilliseconds;
tmstamp.year = (FLMUINT16)stLastFileWriteTime.wYear;
tmstamp.month = (FLMBYTE)(stLastFileWriteTime.wMonth - 1);
tmstamp.day = (FLMBYTE)stLastFileWriteTime.wDay;
// Convert and return the file time stamp as seconds since January 1, 1970
f_timeDateToSeconds( &tmstamp, puiTimeStamp);
Exit:
if( hSearch != INVALID_HANDLE_VALUE)
{
FindClose( hSearch);
}
if( RC_OK(rc))
{
*puiTimeStamp = f_localTimeToUTC( *puiTimeStamp);
}
return( rc);
#elif defined( FLM_UNIX) || defined( FLM_LIBC_NLM)
struct stat filestatus;
if( stat( pszPath, &filestatus) == -1)
{
return( f_mapPlatformError( errno, NE_FLM_GETTING_FILE_INFO));
}
// Return the UTC time
*puiTimeStamp = (FLMUINT)filestatus.st_mtime;
return NE_FLM_OK;
#elif defined( FLM_RING_ZERO_NLM)
RCODE rc = NE_FLM_OK;
FLMUINT uiTmp;
FLMBYTE ucPseudoLNamePath[ F_PATH_MAX_SIZE + 1];
FLMBYTE ucLNamePath[ F_PATH_MAX_SIZE];
LONG lVolumeID;
LONG lPathID;
LONG lLNamePathCount;
LONG lDirectoryID;
LONG lErrorCode;
struct DirectoryStructure * pFileInfo = NULL;
flmAssert( puiTimeStamp);
*puiTimeStamp = 0;
f_strcpy( (char *)&ucPseudoLNamePath[1], pszPath);
ucPseudoLNamePath[ 0] = (FLMBYTE)f_strlen( pszPath );
if( (lErrorCode = ConvertPathString( 0, 0, ucPseudoLNamePath, &lVolumeID,
&lPathID, ucLNamePath, &lLNamePathCount)) != 0)
{
goto Exit;
}
if( (lErrorCode = GetEntryFromPathStringBase( 0, lVolumeID, 0, ucLNamePath,
lLNamePathCount, LONGNameSpace, LONGNameSpace, &pFileInfo,
&lDirectoryID)) != 0)
{
goto Exit;
}
if( pFileInfo)
{
FLMUINT uiTime;
FLMUINT uiDate;
F_TMSTAMP dateTime;
LONG DayMask = 0x001F;
LONG MonthMask = 0x01E0;
LONG YearMask = 0xFE00;
LONG SecMask = 0x001F;
LONG MinMask = 0x07E0;
LONG HourMask = 0xF800;
// Get the low-order 16 bits
uiTime = (FLMUINT)pFileInfo->DLastUpdatedDateAndTime;
// Get the high-order 16 bits
uiDate = (FLMUINT)(pFileInfo->DLastUpdatedDateAndTime >> 16);
f_memset( &dateTime, 0, sizeof( dateTime));
dateTime.second = (FLMBYTE) ((uiTime & SecMask) * 2);
dateTime.minute = (FLMBYTE) ((uiTime & MinMask) >> 5);
dateTime.hour = (FLMBYTE) ((uiTime & HourMask) >> 11);
dateTime.day = (FLMBYTE) (uiDate & DayMask);
dateTime.month = (FLMBYTE) ((uiDate & MonthMask) >> 5)-1;
dateTime.year = (FLMUINT16)(((uiDate & YearMask) >> 9) + 1980);
f_timeDateToSeconds( &dateTime, &uiTmp);
*puiTimeStamp = uiTmp;
*puiTimeStamp = f_localTimeToUTC(*puiTimeStamp);
}
Exit:
if( lErrorCode != 0 )
{
rc = f_mapPlatformError( lErrorCode, NE_FLM_PARSING_FILE_NAME);
}
return( rc);
#endif
}
/****************************************************************************
Desc: Determine if a path is a directory.
****************************************************************************/
RCODE FLMAPI F_FileSystem::getFileSize(
const char * pszFileName,
FLMUINT64 * pui64FileSize)
{
RCODE rc = NE_FLM_OK;
IF_FileHdl * pFileHdl = NULL;
if( RC_BAD( rc = openFile( pszFileName,
FLM_IO_RDONLY | FLM_IO_SH_DENYNONE, &pFileHdl)))
{
goto Exit;
}
if( RC_BAD( rc = pFileHdl->size( pui64FileSize)))
{
goto Exit;
}
Exit:
if( pFileHdl)
{
pFileHdl->Release();
}
return( rc);
}
/****************************************************************************
Desc: Determine if a path is a directory.
****************************************************************************/
FLMBOOL FLMAPI F_FileSystem::isDir(
const char * pszDirName)
{
#if defined( FLM_WIN)
DWORD FileAttr = GetFileAttributes( (LPTSTR)pszDirName);
if( FileAttr == 0xFFFFFFFF)
{
return( FALSE);
}
return (FileAttr & FILE_ATTRIBUTE_DIRECTORY) ? TRUE : FALSE;
#elif defined( FLM_UNIX) || defined( FLM_LIBC_NLM)
struct stat filestatus;
if( stat( (char *)pszDirName, &filestatus) == -1)
{
return FALSE;
}
return ( S_ISDIR( filestatus.st_mode)) ? TRUE : FALSE;
#elif defined( FLM_RING_ZERO_NLM)
LONG lIsFile;
FLMBYTE ucPseudoLNamePath[ F_PATH_MAX_SIZE + 1];
FLMBYTE ucLNamePath[ F_PATH_MAX_SIZE];
LONG lVolumeID;
LONG lPathID;
LONG lLNamePathCount;
LONG lDirectoryID;
FLMBOOL bIsDir = FALSE;
f_strcpy( (char *)&ucPseudoLNamePath[1], pszDirName);
ucPseudoLNamePath[0] = (FLMBYTE)f_strlen( pszDirName);
if( ConvertPathString( 0, 0, ucPseudoLNamePath, &lVolumeID, &lPathID,
ucLNamePath, &lLNamePathCount) == 0)
{
if( MapPathToDirectoryNumber( 0, lVolumeID, 0, ucLNamePath,
lLNamePathCount, LONGNameSpace, &lDirectoryID, &lIsFile) == 0)
{
bIsDir = (FLMBOOL)((lIsFile == 0) ? (FLMBOOL)TRUE : (FLMBOOL)FALSE);
}
}
return( bIsDir);
#endif
}
/****************************************************************************
Desc: Delete a file or directory
****************************************************************************/
RCODE FLMAPI F_FileSystem::deleteFile(
const char * pszFileName)
{
#if defined( FLM_WIN)
if( DeleteFile( (LPTSTR)pszFileName) == FALSE)
{
return( f_mapPlatformError( GetLastError(), NE_FLM_IO_DELETING_FILE));
}
return( NE_FLM_OK);
#elif defined( FLM_UNIX) || defined( FLM_LIBC_NLM)
struct stat FileStat;
if( stat( (char *)pszFileName, &FileStat) == -1)
{
return( f_mapPlatformError( errno, NE_FLM_GETTING_FILE_INFO));
}
// Ensure that the path does NOT designate a directory for deletion
if( S_ISDIR(FileStat.st_mode))
{
return( RC_SET( NE_FLM_IO_ACCESS_DENIED));
}
// Delete the file
if( unlink( (char *)pszFileName) == -1)
{
return( f_mapPlatformError( errno, NE_FLM_IO_DELETING_FILE));
}
return( NE_FLM_OK);
#elif defined( FLM_RING_ZERO_NLM)
return( f_netwareDeleteFile( pszFileName));
#endif
}
/****************************************************************************
Desc: Copy a file.
****************************************************************************/
RCODE FLMAPI F_FileSystem::copyFile(
const char * pszSrcFileName,
const char * pszDestFileName,
FLMBOOL bOverwrite,
FLMUINT64 * pui64BytesCopied)
{
RCODE rc = NE_FLM_OK;
IF_FileHdl * pSrcFileHdl = NULL;
IF_FileHdl * pDestFileHdl = NULL;
FLMBOOL bCreatedDest = FALSE;
FLMUINT64 ui64SrcSize;
// See if the destination file exists. If it does, see if it is
// OK to overwrite it. If so, delete it.
if (doesFileExist( pszDestFileName) == NE_FLM_OK)
{
if (!bOverwrite)
{
rc = RC_SET( NE_FLM_IO_ACCESS_DENIED);
goto Exit;
}
if (RC_BAD( rc = deleteFile( pszDestFileName)))
{
goto Exit;
}
}
// Open the source file.
if( RC_BAD( rc = openFile( pszSrcFileName,
FLM_IO_RDONLY | FLM_IO_SH_DENYNONE, &pSrcFileHdl)))
{
goto Exit;
}
if (RC_BAD( rc = pSrcFileHdl->size( &ui64SrcSize)))
{
goto Exit;
}
// Create the destination file.
if( RC_BAD( rc = createFile( pszDestFileName,
FLM_IO_RDWR | FLM_IO_SH_DENYNONE, &pDestFileHdl)))
{
goto Exit;
}
bCreatedDest = TRUE;
// Do the copy.
if( RC_BAD( rc = copyPartialFile( pSrcFileHdl, 0, ui64SrcSize,
pDestFileHdl, 0, pui64BytesCopied)))
{
goto Exit;
}
Exit:
if( pSrcFileHdl)
{
pSrcFileHdl->closeFile();
pSrcFileHdl->Release();
}
if( pDestFileHdl)
{
pDestFileHdl->closeFile();
pDestFileHdl->Release();
}
if( RC_BAD( rc))
{
if( bCreatedDest)
{
(void)deleteFile( pszDestFileName);
}
*pui64BytesCopied = 0;
}
return( rc);
}
/****************************************************************************
Desc: Do a partial copy from one file into another file.
****************************************************************************/
RCODE FLMAPI F_FileSystem::copyPartialFile(
IF_FileHdl * pSrcFileHdl,
FLMUINT64 ui64SrcOffset,
FLMUINT64 ui64SrcSize,
IF_FileHdl * pDestFileHdl,
FLMUINT64 ui64DestOffset,
FLMUINT64 * pui64BytesCopiedRV)
{
RCODE rc = NE_FLM_OK;
FLMBYTE * pucBuffer = NULL;
FLMUINT uiAllocSize = 65536;
FLMUINT uiBytesToRead;
FLMUINT64 ui64CopySize;
FLMUINT64 ui64FileOffset;
FLMUINT uiBytesRead;
FLMUINT uiBytesWritten;
ui64CopySize = ui64SrcSize;
*pui64BytesCopiedRV = 0;
// Set the buffer size for use during the file copy
if( ui64CopySize < uiAllocSize)
{
uiAllocSize = (FLMUINT)ui64CopySize;
}
// Allocate a buffer
if( RC_BAD( rc = f_alloc( uiAllocSize, &pucBuffer)))
{
goto Exit;
}
// Position the file pointers
if( RC_BAD( rc = pSrcFileHdl->seek( ui64SrcOffset, FLM_IO_SEEK_SET,
&ui64FileOffset)))
{
goto Exit;
}
if( RC_BAD( rc = pDestFileHdl->seek( ui64DestOffset, FLM_IO_SEEK_SET,
&ui64FileOffset)))
{
goto Exit;
}
// Begin copying the data
while( ui64CopySize)
{
if( ui64CopySize > uiAllocSize)
{
uiBytesToRead = uiAllocSize;
}
else
{
uiBytesToRead = (FLMUINT)ui64CopySize;
}
rc = pSrcFileHdl->read( FLM_IO_CURRENT_POS, uiBytesToRead,
pucBuffer, &uiBytesRead);
if (rc == NE_FLM_IO_END_OF_FILE)
{
rc = NE_FLM_OK;
}
if (RC_BAD( rc))
{
rc = RC_SET( NE_FLM_IO_COPY_ERR);
goto Exit;
}
uiBytesWritten = 0;
if( RC_BAD( rc = pDestFileHdl->write( FLM_IO_CURRENT_POS, uiBytesRead,
pucBuffer, &uiBytesWritten)))
{
if (rc == NE_FLM_IO_DISK_FULL)
{
*pui64BytesCopiedRV += uiBytesWritten;
}
else
{
rc = RC_SET( NE_FLM_IO_COPY_ERR);
}
goto Exit;
}
*pui64BytesCopiedRV += uiBytesWritten;
if( uiBytesRead < uiBytesToRead)
{
rc = RC_SET( NE_FLM_IO_END_OF_FILE);
goto Exit;
}
ui64CopySize -= uiBytesRead;
}
Exit:
if (pucBuffer)
{
(void)f_free( &pucBuffer);
}
return( rc);
}
/****************************************************************************
Desc: Rename a file.
****************************************************************************/
RCODE FLMAPI F_FileSystem::renameFile(
const char * pszFileName,
const char * pszNewFileName)
{
#if defined( FLM_WIN)
DWORD error;
RCODE rc = NE_FLM_OK;
FLMUINT64 ui64BytesCopied;
// Try to move the file by doing a rename first, otherwise copy the file
if( (MoveFile( (LPTSTR)pszFileName, (LPTSTR)pszNewFileName)) != TRUE)
{
error = GetLastError();
switch( error)
{
case ERROR_NOT_SAME_DEVICE:
case ERROR_NO_MORE_FILES:
case NO_ERROR:
if( copyFile( pszFileName, pszNewFileName, TRUE, &ui64BytesCopied))
{
rc = RC_SET( NE_FLM_IO_COPY_ERR);
}
else
{
rc = F_FileSystem::deleteFile( pszFileName);
}
break;
default:
rc = f_mapPlatformError( error, NE_FLM_RENAMING_FILE);
break;
}
}
return( rc);
#elif defined( FLM_UNIX) || defined( FLM_LIBC_NLM)
RCODE rc;
FLMBOOL bSrcIsDir;
FLMUINT64 ui64BytesCopied;
if( RC_BAD( rc = targetIsDir( (char*)pszFileName, &bSrcIsDir)))
{
return( rc);
}
errno = 0;
if( RC_BAD( renameSafe( pszFileName, pszNewFileName)))
{
switch( errno)
{
case EXDEV:
{
if( bSrcIsDir)
{
return( RC_SET( NE_FLM_IO_PATH_CREATE_FAILURE));
}
else
{
if( copyFile( pszFileName, pszNewFileName, TRUE, &ui64BytesCopied))
{
return( RC_SET( NE_FLM_IO_COPY_ERR));
}
F_FileSystem::deleteFile( pszFileName);
return( NE_FLM_OK);
}
}
default:
{
if( errno == ENOENT)
{
return( RC_SET( NE_FLM_IO_RENAME_FAILURE));
}
else
{
return( f_mapPlatformError( errno, NE_FLM_RENAMING_FILE));
}
}
}
}
return( NE_FLM_OK);
#elif defined( FLM_RING_ZERO_NLM)
return( f_netwareRenameFile( pszFileName, pszNewFileName));
#endif
}
/****************************************************************************
Desc: Get the sector size (not supported on all platforms).
****************************************************************************/
RCODE FLMAPI F_FileSystem::getSectorSize(
const char * pszFileName,
FLMUINT * puiSectorSize)
{
#ifdef FLM_NLM
F_UNREFERENCED_PARM( pszFileName);
*puiSectorSize = FLM_NLM_SECTOR_SIZE;
return( NE_FLM_OK);
#elif defined( FLM_WIN)
RCODE rc = NE_FLM_OK;
DWORD udSectorsPerCluster;
DWORD udBytesPerSector;
DWORD udNumberOfFreeClusters;
DWORD udTotalNumberOfClusters;
char szVolume [256];
char * pszVolume;
FLMUINT uiLen;
if (!pszFileName)
{
pszVolume = NULL;
}
else
{
pathParse( pszFileName, NULL, szVolume, NULL, NULL);
if (!szVolume [0])
{
pszVolume = NULL;
}
else
{
uiLen = f_strlen( szVolume);
if (szVolume [uiLen - 1] == ':')
{
szVolume [uiLen] = '\\';
szVolume [uiLen + 1] = 0;
}
pszVolume = &szVolume [0];
}
}
if( !GetDiskFreeSpace( (LPCTSTR)pszVolume, &udSectorsPerCluster,
&udBytesPerSector, &udNumberOfFreeClusters,
&udTotalNumberOfClusters))
{
f_assert( 0);
rc = f_mapPlatformError( GetLastError(), NE_FLM_INITIALIZING_IO_SYSTEM);
*puiSectorSize = 0;
goto Exit;
}
*puiSectorSize = (FLMUINT)udBytesPerSector;
Exit:
return( rc);
#elif defined( DEV_BSIZE)
F_UNREFERENCED_PARM( pszFileName);
*puiSectorSize = (FLMUINT)DEV_BSIZE;
return( NE_FLM_OK);
#else
int hFile = -1;
struct stat filestats;
if( (hFile = open( pszFileName, O_RDONLY, 0600)) == -1)
{
rc = f_mapPlatformError( errno, NE_FLM_OPENING_FILE);
goto Exit;
}
if( fstat( hFile, &filestats) != 0)
{
rc = f_mapPlatformError( errno, NE_FLM_OPENING_FILE);
goto Exit;
}
*puiSectorSize = (FLMUINT)filestats.st_blksize;
Exit:
if( hFile != -1)
{
close( hFile);
}
return( rc);
#endif
}
/****************************************************************************
Desc: Set the Read-Only Attribute (not supported on all platforms).
****************************************************************************/
RCODE FLMAPI F_FileSystem::setReadOnly(
const char * pszFileName,
FLMBOOL bReadOnly)
{
RCODE rc = NE_FLM_OK;
#if defined( FLM_WIN)
DWORD dwAttr;
dwAttr = GetFileAttributes( (LPTSTR)pszFileName);
if( dwAttr == (DWORD)-1)
{
rc = RC_SET( NE_FLM_IO_PATH_NOT_FOUND);
goto Exit;
}
if( bReadOnly)
{
dwAttr |= FILE_ATTRIBUTE_READONLY;
}
else
{
dwAttr &= ~FILE_ATTRIBUTE_READONLY;
}
if( !SetFileAttributes( (LPTSTR)pszFileName, dwAttr))
{
rc = RC_SET_AND_ASSERT( NE_FLM_FAILURE);
goto Exit;
}
#elif defined( FLM_UNIX) || defined( FLM_LIBC_NLM)
struct stat filestatus;
if( stat( (char *)pszFileName, &filestatus))
{
rc = RC_SET_AND_ASSERT( NE_FLM_FAILURE);
goto Exit;
}
if( bReadOnly)
{
filestatus.st_mode &= ~S_IWUSR;
}
else
{
filestatus.st_mode |= S_IWUSR;
}
if( chmod( (char *)pszFileName, filestatus.st_mode))
{
rc = RC_SET( NE_FLM_FAILURE);
goto Exit;
}
#else
F_UNREFERENCED_PARM( pszFileName);
F_UNREFERENCED_PARM( bReadOnly);
rc = RC_SET_AND_ASSERT( NE_FLM_NOT_IMPLEMENTED);
goto Exit;
#endif
Exit:
return( rc);
}
/****************************************************************************
Desc:
****************************************************************************/
FLMBOOL FLMAPI F_FileSystem::canDoAsync( void)
{
return( m_bCanDoAsync);
}
/****************************************************************************
Desc:
****************************************************************************/
#if defined( FLM_UNIX) || defined( FLM_LIBC_NLM)
RCODE F_FileSystem::targetIsDir(
const char * pszPath,
FLMBOOL * pbIsDir)
{
struct stat sbuf;
RCODE rc = NE_FLM_OK;
*pbIsDir = FALSE;
if( stat( pszPath, &sbuf) < 0)
{
rc = f_mapPlatformError( errno, NE_FLM_IO_ACCESS_DENIED);
}
else if( (sbuf.st_mode & S_IFMT) == S_IFDIR)
{
*pbIsDir = TRUE;
}
return( rc);
}
#endif
/****************************************************************************
Desc: Rename an existing file (typically an "X" locked file to an
unlocked file) using a safe (non-race) method. To ensure that
an existing file is not being overwritten by a rename operation,
we will first create a new file with the desired name (using the
CREAT and EXCL options, (ensuring a unique file name)). Then,
the source file will be renamed to new name.
****************************************************************************/
#if defined( FLM_UNIX) || defined( FLM_LIBC_NLM)
RCODE F_FileSystem::renameSafe(
const char * pszSrcFile,
const char * pszDestFile)
{
RCODE rc = NE_FLM_OK;
struct stat temp_stat_buf;
errno = 0;
if( stat( pszDestFile, &temp_stat_buf) != -1)
{
// If we were able to stat it, then the file obviously exists...
rc = RC_SET( NE_FLM_IO_RENAME_FAILURE);
goto Exit;
}
else
{
if (errno != ENOENT)
{
// ENOENT means the file didn't exist, which is what we were
// hoping for.
rc = f_mapPlatformError( errno, NE_FLM_IO_RENAME_FAILURE);
goto Exit;
}
}
errno = 0;
if( rename( pszSrcFile, pszDestFile) != 0)
{
rc = f_mapPlatformError( errno, NE_FLM_IO_RENAME_FAILURE);
}
Exit:
return( rc);
}
#endif
/****************************************************************************
Desc:
****************************************************************************/
RCODE FLMAPI f_filecpy(
const char * pszSourceFile,
const char * pszData)
{
RCODE rc = NE_FLM_OK;
IF_FileHdl * pFileHdl = NULL;
F_FileSystem fileSystem;
FLMUINT uiBytesWritten = 0;
if( RC_OK( rc = fileSystem.doesFileExist( pszSourceFile)))
{
if( RC_BAD( rc = fileSystem.deleteFile( pszSourceFile)))
{
goto Exit;
}
}
if( RC_BAD( rc = fileSystem.createFile( pszSourceFile, FLM_IO_RDWR,
&pFileHdl)))
{
goto Exit;
}
if( RC_BAD( rc = pFileHdl->write( 0, f_strlen( pszData), (void *)pszData,
&uiBytesWritten)))
{
goto Exit;
}
Exit:
if( pFileHdl)
{
pFileHdl->closeFile();
pFileHdl->Release();
pFileHdl = NULL;
}
return( rc);
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE FLMAPI f_filecat(
const char * pszSourceFile,
const char * pszData)
{
RCODE rc = NE_FLM_OK;
IF_FileHdl * pFileHdl = NULL;
FLMUINT64 ui64FileSize = 0;
FLMUINT uiBytesWritten = 0;
IF_FileSystem * pFileSystem = f_getFileSysPtr();
if (RC_BAD( rc = pFileSystem->doesFileExist( pszSourceFile)))
{
if( rc == NE_FLM_IO_PATH_NOT_FOUND)
{
if( RC_BAD( rc = pFileSystem->createFile(
pszSourceFile, FLM_IO_RDWR, &pFileHdl)))
{
goto Exit;
}
}
else
{
goto Exit;
}
}
else
{
if( RC_BAD( rc = pFileSystem->openFile( pszSourceFile,
FLM_IO_RDWR, &pFileHdl)))
{
goto Exit;
}
}
if ( RC_BAD( rc = pFileHdl->size( &ui64FileSize)))
{
goto Exit;
}
if( RC_BAD( rc = pFileHdl->write( ui64FileSize, f_strlen( pszData),
(void *)pszData, &uiBytesWritten)))
{
goto Exit;
}
Exit:
if( pFileHdl)
{
pFileHdl->Release();
}
return( rc);
}
/****************************************************************************
Desc: Split the path into its components
Output:
pServer - pointer to a buffer to hold the server name
pVolume - pointer to a buffer to hold the volume name
pDirPath - pointer to a buffer to hold the path
pFileName pointer to a buffer to hold the filename
All of the output parameters are optional. If you do not want one
of the components, simply give a NULL pointer.
Note: if the input path has no file name, d:\dir_1 for example, then
pass a NULL pointer for pFileName. Otherwise dir_1 will be returned
as pFileName.
The server name may be ommitted in the input path:
sys:\system\autoexec.ncf
UNC paths of the form:
\\server-name\volume-name\dir_1\dir_2\file.ext
are supported.
DOS paths of the form:
d:\dir_1\dir_2\file.ext
are also supported.
Example:
Given this input: orm-prod48/sys:\system\autoexec.ncf
The output would be:
pServer = "orm-prod48"
pVolume = "sys:"
pDirPath = "\system"
pFileName "autoexec.ncf"
****************************************************************************/
void FLMAPI F_FileSystem::pathParse(
const char * pszInputPath,
char * pszServer,
char * pszVolume,
char * pszDirPath,
char * pszFileName)
{
char szInput[ F_PATH_MAX_SIZE];
char * pszNext;
char * pszColon;
char * pszComponent;
FLMUINT uiEndChar;
FLMBOOL bUNC = FALSE;
IF_FileSystem * pFileSystem = f_getFileSysPtr();
// Initialize return buffers
if (pszServer)
{
*pszServer = 0;
}
if (pszVolume)
{
*pszVolume = 0;
}
if (pszDirPath)
{
*pszDirPath = 0;
}
if (pszFileName)
{
// Get the file name
*pszFileName = 0;
pFileSystem->pathReduce( pszInputPath, szInput, pszFileName);
}
else
{
f_strcpy( szInput, pszInputPath);
}
// Split out the rest of the components
pszComponent = &szInput [0];
// Is this a UNC path?
if (szInput[0] == '\\' && szInput[1] == '\\')
{
// Yes, assume a UNC path
pszComponent += 2;
bUNC = TRUE;
}
pszNext = pszColon = pszComponent;
// Is there a ':' in the szInput path?
while (*pszColon && *pszColon != ':')
{
pszColon++;
}
if (*pszColon || bUNC)
{
// Yes, assume there is a volume in the path
pszComponent = f_getPathComponent( &pszNext, &uiEndChar);
if (uiEndChar != ':')
{
// Assume that this component is the server
if (pszServer)
{
f_strcpy( pszServer, pszComponent);
}
// Get the next component
pszComponent = f_getPathComponent( &pszNext, &uiEndChar);
}
// Assume that this component is the volume
if (pszVolume)
{
char * pszSrc = pszComponent;
char * pszDst = pszVolume;
while (*pszSrc)
{
*pszDst++ = *pszSrc++;
}
*pszDst++ = ':';
*pszDst = 0;
}
// For UNC paths, the leading '\' of the path is set to 0 by
// f_getPathComponent. This code restores the leading '\'.
if (f_isSlashSeparator( (char)uiEndChar))
{
*(--pszNext) = (char)uiEndChar;
}
}
// Assume that all that is left of the input is the path
if (pszDirPath)
{
f_strcpy( pszDirPath, pszNext);
}
}
/****************************************************************************
Desc: This function will strip off the filename or trailing
directory of a path. The stripped component of the path will
be placed into the area pointed at by string. The source
path will not be modified. The dest path will contain the
remainder of the stripped path. A stripped path can be processed
repeatedly by this function until there is no more path to reduce.
If the string is set to NULL, the copying of the stripped portion of
the path will be bypassed by the function.
Notes: This function handles drive based, UNC, Netware, and UNIX type
paths.
****************************************************************************/
RCODE FLMAPI F_FileSystem::pathReduce(
const char * pszPath,
char * pszDir,
char * pszPathComponent)
{
RCODE rc = NE_FLM_OK;
const char * pszFileNameStart;
char szLocalPath[ F_PATH_MAX_SIZE];
FLMUINT uiLen;
// Check for valid path pointers
if( !pszPath || !pszDir)
{
rc = RC_SET( NE_FLM_INVALID_PARM);
goto Exit;
}
if ((uiLen = f_strlen( pszPath)) == 0)
{
rc = RC_SET( NE_FLM_IO_CANNOT_REDUCE_PATH);
goto Exit;
}
// Trim out any trailing slash separators
if( f_isSlashSeparator( pszPath [uiLen - 1]))
{
f_strcpy( szLocalPath, pszPath);
while( f_isSlashSeparator( szLocalPath[ uiLen - 1]))
{
szLocalPath[ --uiLen] = 0;
if( !uiLen)
{
rc = RC_SET( NE_FLM_IO_CANNOT_REDUCE_PATH);
goto Exit;
}
}
pszPath = szLocalPath;
}
if( f_canReducePath( pszPath))
{
// Search for a slash or beginning of path
pszFileNameStart = f_findFileNameStart( pszPath);
// Copy the sliced portion of the path if requested by caller
if( pszPathComponent)
{
f_strcpy( pszPathComponent, pszFileNameStart);
}
// Copy the reduced source path to the dir path
if (pszFileNameStart > pszPath)
{
uiLen = (FLMUINT)(pszFileNameStart - pszPath);
f_memcpy( pszDir, pszPath, uiLen);
if (uiLen >= 2 && f_isSlashSeparator( pszDir [uiLen - 1])
#ifndef FLM_UNIX
&& pszDir [uiLen - 2] != ':'
#endif
)
{
// Trim off the trailing path separator
pszDir [uiLen - 1] = 0;
}
else
{
pszDir [uiLen] = 0;
}
}
else
{
*pszDir = 0;
}
}
else
{
// We've found the drive id or server\volume specifier.
if (pszPathComponent)
{
f_strcpy( pszPathComponent, pszPath);
}
*pszDir = 0;
}
Exit:
return( rc);
}
/****************************************************************************
Desc: Internal function for WpioPathBuild() and WpioPathModify().
Appends string the path & adds a path delimiter if necessary.
In: *path = pointer to an IO_PATH
*string = pointer to a NULL terminated string
*end_ptr = pointer to the end of the IO_PATH which is being built.
****************************************************************************/
RCODE FLMAPI F_FileSystem::pathAppend(
char * pszPath,
const char * pszPathComponent)
{
// Don't put a slash separator if pszPath is empty
if (*pszPath)
{
FLMUINT uiStrLen = f_strlen( pszPath);
char * pszEnd = pszPath + uiStrLen - 1;
if (!f_isSlashSeparator( *pszEnd))
{
// Check for maximum path size - 2 is for slash separator
// and null byte.
if (uiStrLen + 2 + f_strlen( pszPathComponent) > F_PATH_MAX_SIZE)
{
return RC_SET_AND_ASSERT( NE_FLM_IO_PATH_TOO_LONG);
}
pszEnd++;
#if defined( FLM_UNIX)
*pszEnd = '/';
#else
*pszEnd = '\\';
#endif
}
else
{
// Check for maximum path size +1 is for null byte.
if (uiStrLen + 1 + f_strlen( pszPathComponent) > F_PATH_MAX_SIZE)
{
return RC_SET_AND_ASSERT( NE_FLM_IO_PATH_TOO_LONG);
}
}
f_strcpy( pszEnd + 1, pszPathComponent);
}
else
{
f_strcpy( pszPath, pszPathComponent);
}
return( NE_FLM_OK);
}
/****************************************************************************
Desc: Convert an PATH into a fully qualified, storable C string
reference to a file or directory.
In: pszPath - the path to convert.
pszStorageString - a pointer to a string that is atleast
F_PATH_MAX_SIZE in size
****************************************************************************/
RCODE FLMAPI F_FileSystem::pathToStorageString(
const char * pszPath,
char * pszStorageString)
{
#ifdef FLM_WIN
char * pszNamePart;
if (GetFullPathName( (LPCSTR)pszPath,
(DWORD)F_PATH_MAX_SIZE - 1,
(LPSTR)pszStorageString,
(LPSTR *)&pszNamePart) != 0)
{
}
else
{
// Convert to upper case.
while (*pszPath)
{
*pszStorageString++ = *pszPath;
pszPath++;
}
*pszStorageString = 0;
}
return( NE_FLM_OK);
#elif defined( FLM_RING_ZERO_NLM)
while (*pszPath)
{
*pszStorageString++ = f_toupper( *pszPath);
pszPath++;
}
*pszStorageString = 0;
return( NE_FLM_OK);
#else
char szFile[ F_PATH_MAX_SIZE];
char szDir[ F_PATH_MAX_SIZE];
char * pszRealPath = NULL;
RCODE rc = NE_FLM_OK;
if (RC_BAD( rc = pathReduce( pszPath, szDir, szFile)))
{
goto Exit;
}
if (!szDir [0])
{
szDir [0] = '.';
szDir [1] = '\0';
}
if (RC_BAD( rc = f_alloc( (FLMUINT)PATH_MAX, &pszRealPath)))
{
goto Exit;
}
if (!realpath( (char *)szDir, (char *)pszRealPath))
{
rc = f_mapPlatformError( errno, NE_FLM_PARSING_FILE_NAME);
goto Exit;
}
if (f_strlen( pszRealPath) >= F_PATH_MAX_SIZE)
{
rc = RC_SET_AND_ASSERT( NE_FLM_IO_PATH_TOO_LONG);
goto Exit;
}
f_strcpy( pszStorageString, pszRealPath);
if (RC_BAD( rc = pathAppend( pszStorageString, szFile)))
{
goto Exit;
}
Exit:
if (pszRealPath)
{
f_free( &pszRealPath);
}
return( rc);
#endif
}
/****************************************************************************
Desc: Generates a file name given a seed and some modifiers, it is built
to be called in a loop until the file can be sucessfully written or
created with the increment being changed every time.
In: bModext -> if TRUE then we will use the extension for collisions.
In\Out: puiTime -> a modified time stamp which is used as the base
filename. To properly set up this value, make sure
the puiTime points to a 0 the first time this routine
is called and it will be set up for you. Thereafter,
do not change it between calls.
pHighChars-> these are the 8 bits that were shifted off the top of
the time struct. It will be set up for you the first
time you call this routine if puiTime points to a 0
the first time this routine is called. Do not change
this value between calls.
pszFileName -> should be pointing to a null string on the way in.
going out it will be the complete filename.
pszFileExt -> the last char of the ext will be used for collisions,
depending on the bModext flag. If null then
the extension will be .00x where x is the collision
counter.
Notes: The counter on the collision is 0-9, a-z.
****************************************************************************/
void FLMAPI F_FileSystem::pathCreateUniqueName(
FLMUINT * puiTime,
char * pszFileName,
const char * pszFileExt,
FLMBYTE * pHighChars,
FLMBOOL bModext)
{
FLMINT iCount, iLength;
FLMUINT uiSdTmp = 0;
FLMUINT uiIncVal = 1;
f_setupTime( puiTime, pHighChars);
uiSdTmp = *puiTime;
*(pszFileName + 8) = NATIVE_DOT;
f_memset( (pszFileName + 9), NATIVE_ZERO, 3 );
if( (pszFileExt != NULL))
{
if ((iLength = f_strlen(pszFileExt)) > 3)
{
iLength = 3;
}
f_memmove( (pszFileName + 9), pszFileExt, iLength);
}
if( bModext)
{
f_hexToNative((FLMBYTE)(uiSdTmp & 0x0000001F), pszFileName+(11));
}
else
{
uiIncVal = 32;
}
uiSdTmp = uiSdTmp >> 5;
for( iCount = 0; iCount < 6; iCount++)
{
f_hexToNative((FLMBYTE)(uiSdTmp & 0x0000000F), pszFileName+(7-iCount));
uiSdTmp = uiSdTmp >> 4;
}
for( iCount = 0; iCount < 2; iCount++)
{
f_hexToNative((FLMBYTE)(*pHighChars & 0x0000000F), pszFileName+(1-iCount));
*pHighChars = *pHighChars >> 4;
}
*(pszFileName + 12) = '\0';
*puiTime += uiIncVal;
}
/****************************************************************************
Desc: Compares the current file against a pattern template
****************************************************************************/
FLMBOOL FLMAPI F_FileSystem::doesFileMatch(
const char * pszFileName,
const char * pszTemplate)
{
FLMUINT uiPattern;
FLMUINT uiChar;
if( !*pszTemplate)
{
return( TRUE);
}
while( *pszTemplate)
{
uiPattern = *pszTemplate++;
switch( uiPattern)
{
case NATIVE_WILDCARD:
{
if( *pszTemplate == 0)
{
return( TRUE);
}
while( *pszFileName)
{
if( doesFileMatch( pszFileName, pszTemplate))
{
return( TRUE);
}
pszFileName++;
}
return( FALSE);
}
case NATIVE_QUESTIONMARK:
{
if( *pszFileName++ == 0)
{
return( FALSE);
}
break;
}
default:
{
uiChar = *pszFileName++;
if( f_toupper( uiPattern) != f_toupper( uiChar))
{
return( FALSE);
}
break;
}
}
}
return( (*pszFileName != 0) ? FALSE : TRUE);
}
/****************************************************************************
Desc:
*****************************************************************************/
RCODE FLMAPI F_FileSystem::deleteMultiFileStream(
const char * pszDirectory,
const char * pszBaseName)
{
RCODE rc = NE_FLM_OK;
F_MultiFileOStream * pMultiStream = NULL;
if( (pMultiStream = f_new F_MultiFileOStream) == NULL)
{
rc = RC_SET( NE_FLM_MEM);
goto Exit;
}
if( RC_BAD( rc = pMultiStream->processDirectory(
pszDirectory, pszBaseName, TRUE)))
{
goto Exit;
}
Exit:
if( pMultiStream)
{
pMultiStream->Release();
}
return( rc);
}
/****************************************************************************
Desc: This routine obtains exclusive access to a database by creating
a .lck file. FLAIM holds the .lck file open as long as the database
is open. When the database is finally closed, it deletes the .lck
file. This is only used for 3.x databases.
****************************************************************************/
RCODE FLMAPI F_FileSystem::createLockFile(
const char * pszPath,
IF_FileHdl ** ppLockFileHdl)
{
RCODE rc = NE_FLM_OK;
IF_FileHdl * pLockFileHdl = NULL;
// Attempt to create the lock file. If that succeeds, we have
// the lock. If it fails, the lock file may have been left because
// of a crash. Hence, we first try to delete the file. If that succeeds,
// we then attempt to create the file again. If that, or the 2nd create
// fail, we simply return an access denied error.
#ifdef FLM_UNIX
if( RC_BAD( createFile( pszPath,
FLM_IO_RDWR | FLM_IO_EXCL | FLM_IO_SH_DENYRW,
&pLockFileHdl)))
{
if( RC_BAD( openFile( pszPath,
FLM_IO_RDWR | FLM_IO_SH_DENYRW, &pLockFileHdl)))
{
rc = RC_SET( NE_FLM_IO_ACCESS_DENIED);
goto Exit;
}
}
if( RC_BAD( pLockFileHdl->lock()))
{
rc = RC_SET( NE_FLM_IO_ACCESS_DENIED);
goto Exit;
}
#else
if( RC_BAD( createFile( pszPath,
FLM_IO_RDWR | FLM_IO_EXCL | FLM_IO_SH_DENYRW | FLM_IO_DELETE_ON_RELEASE,
&pLockFileHdl)))
{
if( RC_BAD( deleteFile( pszPath)))
{
rc = RC_SET( NE_FLM_IO_ACCESS_DENIED);
goto Exit;
}
else if( RC_BAD( createFile( pszPath,
FLM_IO_RDWR | FLM_IO_EXCL | FLM_IO_SH_DENYRW | FLM_IO_DELETE_ON_RELEASE,
&pLockFileHdl)))
{
rc = RC_SET( NE_FLM_IO_ACCESS_DENIED);
goto Exit;
}
}
#endif
*ppLockFileHdl = pLockFileHdl;
pLockFileHdl = NULL;
Exit:
if (pLockFileHdl)
{
pLockFileHdl->Release();
pLockFileHdl = NULL;
}
return( rc);
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE FLMAPI f_filetobuf(
const char * pszSourceFile,
char ** ppszBuffer)
{
RCODE rc = NE_FLM_OK;
char * pszBuffer = NULL;
IF_FileHdl * pFileHdl = NULL;
FLMUINT64 ui64FileSize;
FLMUINT uiBytesRead;
IF_FileSystem * pFileSystem = f_getFileSysPtr();
if( RC_BAD(rc = pFileSystem->openFile( pszSourceFile,
FLM_IO_RDONLY, &pFileHdl)))
{
goto Exit;
}
if( RC_BAD( rc = pFileHdl->size( &ui64FileSize)))
{
goto Exit;
}
if( !ui64FileSize)
{
*ppszBuffer = NULL;
goto Exit;
}
if( RC_BAD( rc = f_alloc( (FLMUINT)(ui64FileSize + 1), &pszBuffer)))
{
goto Exit;
}
if( RC_BAD( rc = pFileHdl->read( 0, (FLMUINT)ui64FileSize,
pszBuffer, &uiBytesRead)))
{
goto Exit;
}
f_assert( (FLMUINT)ui64FileSize == uiBytesRead);
pszBuffer[ ui64FileSize] = 0;
*ppszBuffer = pszBuffer;
pszBuffer = NULL;
Exit:
if( pFileHdl)
{
pFileHdl->Release();
}
if( pszBuffer)
{
f_free( &pszBuffer);
}
return( rc);
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE FLMAPI f_pathReduce(
const char * pszSourcePath,
char * pszDestPath,
char * pszString)
{
return( f_getFileSysPtr()->pathReduce(
pszSourcePath, pszDestPath, pszString));
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE FLMAPI f_pathAppend(
char * pszPath,
const char * pszPathComponent)
{
return( f_getFileSysPtr()->pathAppend( pszPath, pszPathComponent));
}
/****************************************************************************
Desc:
****************************************************************************/
F_FileHdlCache::F_FileHdlCache()
{
m_pTimeoutThread = NULL;
m_pAvailList = NULL;
m_uiMaxIdleTime = 0;
}
/****************************************************************************
Desc:
****************************************************************************/
F_FileHdlCache::~F_FileHdlCache()
{
if( m_pTimeoutThread)
{
m_pTimeoutThread->stopThread();
m_pTimeoutThread->Release();
m_pTimeoutThread = NULL;
}
if( m_pAvailList)
{
m_pAvailList->Release();
m_pAvailList = NULL;
}
m_uiMaxIdleTime = 0;
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE F_FileHdlCache::setup(
FLMUINT uiMaxCachedFiles,
FLMUINT uiIdleTimeoutSecs)
{
RCODE rc = NE_FLM_OK;
if( (m_pAvailList = f_new F_HashTable) == NULL)
{
rc = RC_SET( NE_FLM_MEM);
goto Exit;
}
if( RC_BAD( rc = m_pAvailList->setupHashTable( TRUE,
uiMaxCachedFiles, uiMaxCachedFiles)))
{
goto Exit;
}
m_uiMaxIdleTime = uiIdleTimeoutSecs;
if( RC_BAD( rc = f_threadCreate( &m_pTimeoutThread,
timeoutThread, "F_FileHdlCache Timeout", F_DEFAULT_THREAD_GROUP, 0,
this, NULL, F_THREAD_DEFAULT_STACK_SIZE)))
{
goto Exit;
}
Exit:
return( rc);
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE FLMAPI F_FileHdlCache::setOpenThreshold(
FLMUINT uiMaxOpenFiles)
{
return( m_pAvailList->setMaxObjects( uiMaxOpenFiles));
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE F_FileHdlCache::openOrCreate(
const char * pszFileName,
FLMUINT uiIoFlags,
FLMBOOL bCreate,
IF_FileHdl ** ppFile)
{
RCODE rc = NE_FLM_OK;
F_CachedFileHdl * pFileHdl = NULL;
F_HashObject * pHashObject;
FLMUINT uiNameLen = f_strlen( pszFileName);
FLMUINT uiKeyLen = uiNameLen + 4;
FLMBYTE ucKeyBuf[ F_PATH_MAX_SIZE + 4];
UD2FBA( uiIoFlags, ucKeyBuf);
f_memcpy( &ucKeyBuf[ 4], pszFileName, uiNameLen);
if( RC_BAD( rc = m_pAvailList->getObject( ucKeyBuf, uiKeyLen,
&pHashObject, TRUE)))
{
if( rc != NE_FLM_NOT_FOUND)
{
goto Exit;
}
if( (pFileHdl = f_new F_CachedFileHdl) == NULL)
{
rc = RC_SET( NE_FLM_MEM);
goto Exit;
}
if( RC_BAD( rc = pFileHdl->openOrCreate( pszFileName,
uiIoFlags, bCreate)))
{
goto Exit;
}
if( RC_BAD( rc = f_alloc( uiKeyLen, &pFileHdl->m_pucKey)))
{
goto Exit;
}
f_memcpy( pFileHdl->m_pucKey, ucKeyBuf, uiKeyLen);
pFileHdl->m_uiKeyLen = uiKeyLen;
pFileHdl->m_pFileHdlCache = this;
}
else
{
pFileHdl = (F_CachedFileHdl *)pHashObject;
pFileHdl->m_bInAvailList = FALSE;
if( bCreate)
{
if( RC_BAD( rc = pFileHdl->truncateFile()))
{
goto Exit;
}
}
}
*ppFile = pFileHdl;
pFileHdl = NULL;
Exit:
if( pFileHdl)
{
pFileHdl->Release();
}
return( rc);
}
/****************************************************************************
Desc:
****************************************************************************/
void FLMAPI F_FileHdlCache::closeUnusedFiles(
FLMUINT uiUnusedTime)
{
if( !uiUnusedTime)
{
m_pAvailList->removeAllObjects();
}
else
{
m_pAvailList->removeAgedObjects( uiUnusedTime);
}
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE F_FileHdlCache::timeoutThread(
IF_Thread * pThread)
{
FLMUINT uiCurrentTime;
FLMUINT uiLastPurgeTime = FLM_GET_TIMER();
F_FileHdlCache * pThis = (F_FileHdlCache *)pThread->getParm1();
for( ;;)
{
if( pThread->getShutdownFlag())
{
break;
}
uiCurrentTime = FLM_GET_TIMER();
if( FLM_TIMER_UNITS_TO_SECS(
FLM_ELAPSED_TIME( uiCurrentTime, uiLastPurgeTime)) >=
pThis->m_uiMaxIdleTime)
{
pThis->m_pAvailList->removeAgedObjects( pThis->m_uiMaxIdleTime);
uiLastPurgeTime = uiCurrentTime;
}
f_sleep( 100);
}
return( NE_FLM_OK);
}
/****************************************************************************
Desc:
****************************************************************************/
FLMINT FLMAPI F_CachedFileHdl::Release( void)
{
FLMINT iRefCnt = --m_refCnt;
F_FileHdlCache * pFileHdlCache = m_pFileHdlCache;
if( !iRefCnt)
{
if( pFileHdlCache)
{
f_assert( getHashBucket() == F_INVALID_HASH_BUCKET);
if( m_bInAvailList)
{
// This should only happen if the object is being released
// by the avail list hash table
m_bInAvailList = FALSE;
}
else if( isOpen())
{
if( RC_OK( pFileHdlCache->m_pAvailList->addObject( this, TRUE)))
{
m_bInAvailList = TRUE;
}
}
}
if( !m_refCnt)
{
delete this;
}
}
return( iRefCnt);
}
/*****************************************************************************
Desc:
******************************************************************************/
RCODE FLMAPI F_FileSystem::allocFileHandleCache(
FLMUINT uiMaxCachedFiles,
FLMUINT uiIdleTimeoutSecs,
IF_FileHdlCache ** ppFileHdlCache)
{
RCODE rc = NE_FLM_OK;
F_FileHdlCache * pFileHdlCache = NULL;
if( (pFileHdlCache = f_new F_FileHdlCache) == NULL)
{
rc = RC_SET( NE_FLM_MEM);
goto Exit;
}
if( RC_BAD( rc = pFileHdlCache->setup(
uiMaxCachedFiles, uiIdleTimeoutSecs)))
{
goto Exit;
}
*ppFileHdlCache = pFileHdlCache;
pFileHdlCache = NULL;
Exit:
if( pFileHdlCache)
{
pFileHdlCache->Release();
}
return( rc);
}
/*****************************************************************************
Desc:
******************************************************************************/
RCODE FLMAPI F_FileSystem::allocIOBuffer(
FLMUINT uiMinSize,
IF_IOBuffer ** ppIOBuffer)
{
RCODE rc = NE_FLM_OK;
F_IOBuffer * pIOBuffer = NULL;
if( (pIOBuffer = f_new F_IOBuffer) == NULL)
{
rc = RC_SET( NE_FLM_MEM);
goto Exit;
}
if( RC_BAD( rc = pIOBuffer->setupBuffer( uiMinSize, NULL)))
{
goto Exit;
}
*ppIOBuffer = pIOBuffer;
pIOBuffer = NULL;
Exit:
if( pIOBuffer)
{
pIOBuffer->Release();
}
return( rc);
}
/*****************************************************************************
Desc:
******************************************************************************/
void F_FileHdl::initCommonData( void)
{
m_pszFileName = NULL;
m_uiBytesPerSector = 0;
m_ui64NotOnSectorBoundMask = 0;
m_ui64GetSectorBoundMask = 0;
m_uiExtendSize = 0;
m_uiMaxAutoExtendSize = 0;
m_pucAlignedBuff = NULL;
m_uiAlignedBuffSize = 0;
m_ui64CurrentPos = 0;
m_bFileOpened = FALSE;
m_bDeleteOnRelease = FALSE;
m_bOpenedReadOnly = FALSE;
m_bOpenedExclusive = FALSE;
m_bOpenedInAsyncMode = FALSE;
m_bRequireAlignedIO = FALSE;
m_bDoDirectIO = FALSE;
m_numAsyncPending = 0;
}
/*****************************************************************************
Desc:
******************************************************************************/
void F_FileHdl::freeCommonData( void)
{
f_assert( !m_numAsyncPending);
if( m_pucAlignedBuff)
{
f_freeAlignedBuffer( (void **)&m_pucAlignedBuff);
m_uiAlignedBuffSize = 0;
}
if( m_pszFileName)
{
f_free( &m_pszFileName);
}
}
/****************************************************************************
Desc: Open a file
****************************************************************************/
RCODE F_FileHdl::openFile(
const char * pszFileName,
FLMUINT uiIoFlags)
{
return( openOrCreate( pszFileName, uiIoFlags, FALSE));
}
/****************************************************************************
Desc: Create a file
****************************************************************************/
RCODE F_FileHdl::createFile(
const char * pszFileName,
FLMUINT uiIoFlags)
{
return( openOrCreate( pszFileName, uiIoFlags, TRUE));
}
/****************************************************************************
Desc: Create a unique file name in the specified directory
****************************************************************************/
RCODE F_FileHdl::createUniqueFile(
char * pszDirName,
const char * pszFileExtension,
FLMUINT uiIoFlags)
{
RCODE rc = NE_FLM_OK;
char * pszTmp;
FLMBOOL bModext = TRUE;
FLMUINT uiBaseTime = 0;
FLMBYTE ucHighByte = 0;
char szFileName[ F_FILENAME_SIZE];
char szDirPath[ F_PATH_MAX_SIZE];
char szTmpPath[ F_PATH_MAX_SIZE];
FLMUINT uiCount;
IF_FileSystem * pFileSystem = f_getFileSysPtr();
f_assert( !m_bFileOpened);
f_assert( !m_pszFileName);
szFileName[0] = '\0';
szTmpPath[0] = '\0';
#if defined( FLM_UNIX)
if( !pszDirName || pszDirName[ 0] == '\0')
{
f_strcpy( szDirPath, "./");
}
else
#endif
{
f_strcpy( szDirPath, pszDirName);
}
// Truncate any trailing spaces
pszTmp = (char *)szDirPath;
pszTmp += (f_strlen( pszTmp) - 1);
while( pszTmp >= (char *)szDirPath && (*pszTmp == 0x20))
{
*pszTmp = 0;
pszTmp--;
}
// Append a slash if one isn't already there
#if defined( FLM_UNIX)
if( pszTmp >= (char *)szDirPath && *pszTmp != '/')
{
pszTmp++;
*pszTmp++ = '/';
}
#else
if( pszTmp >= (char *)szDirPath && *pszTmp != '\\')
{
pszTmp++;
*pszTmp++ = '\\';
}
#endif
else
{
pszTmp++;
}
*pszTmp = 0;
if( pszFileExtension && f_strlen( pszFileExtension) >= 3)
{
bModext = FALSE;
}
uiCount = 0;
do
{
pFileSystem->pathCreateUniqueName( &uiBaseTime, szFileName,
pszFileExtension, &ucHighByte, bModext);
f_strcpy( szTmpPath, szDirPath);
pFileSystem->pathAppend( szTmpPath, szFileName);
rc = createFile( szTmpPath, uiIoFlags | FLM_IO_EXCL);
} while( rc != NE_FLM_OK && (uiCount++ < 10));
// Check if the path was created
if( uiCount >= 10 && rc != NE_FLM_OK)
{
rc = RC_SET( NE_FLM_IO_PATH_CREATE_FAILURE);
goto Exit;
}
// Created file name needs to be returned
f_strcpy( pszDirName, szTmpPath);
Exit:
return( rc);
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE F_FileHdl::getPreWriteExtendSize(
FLMUINT64 ui64WriteOffset,
FLMUINT uiBytesToWrite,
FLMUINT64 * pui64CurrFileSize,
FLMUINT * puiTotalBytesToExtend)
{
RCODE rc = NE_FLM_OK;
FLMUINT uiTotalBytesToExtend = 0;
FLMUINT64 ui64CurrFileSize = 0;
// Determine if the write will extend the file beyond its
// current size.
if( RC_BAD( rc = size( &ui64CurrFileSize)))
{
goto Exit;
}
if( ui64WriteOffset + uiBytesToWrite > ui64CurrFileSize)
{
if( (uiTotalBytesToExtend = m_uiExtendSize) != 0)
{
if( ui64CurrFileSize > m_uiMaxAutoExtendSize)
{
uiTotalBytesToExtend = 0;
}
else
{
// Don't extend beyond maximum file size.
if( m_uiMaxAutoExtendSize - ui64CurrFileSize < uiTotalBytesToExtend)
{
uiTotalBytesToExtend =
(FLMUINT)(m_uiMaxAutoExtendSize - ui64CurrFileSize);
}
// If the extend size is not on a sector boundary, round it down.
uiTotalBytesToExtend =
(FLMUINT)truncateToPrevSector( uiTotalBytesToExtend);
}
}
}
Exit:
*pui64CurrFileSize = ui64CurrFileSize;
*puiTotalBytesToExtend = uiTotalBytesToExtend;
return( rc);
}
/****************************************************************************
Desc:
****************************************************************************/
FLMINT FLMAPI F_FileAsyncClient::Release()
{
FLMINT iRefCnt;
if( m_refCnt == 1)
{
f_assert( !m_pFileHdl);
f_assert( !m_pIOBuffer);
f_mutexLock( F_FileHdl::m_hAsyncListMutex);
if( F_FileHdl::m_uiAvailAsyncCount < 32)
{
f_assert( !m_pNext);
m_pNext = F_FileHdl::m_pFirstAvailAsync;
F_FileHdl::m_pFirstAvailAsync = this;
F_FileHdl::m_uiAvailAsyncCount++;
m_completionRc = NE_FLM_OK;
m_uiBytesToDo = 0;
m_uiBytesDone = 0;
iRefCnt = m_refCnt;
}
else
{
iRefCnt = --m_refCnt;
}
f_mutexUnlock( F_FileHdl::m_hAsyncListMutex);
}
else
{
iRefCnt = --m_refCnt;
}
if( !m_refCnt)
{
delete this;
}
return( iRefCnt);
}
/****************************************************************************
Desc:
****************************************************************************/
F_FileAsyncClient::~F_FileAsyncClient()
{
f_assert( !m_pNext);
#ifdef FLM_WIN
if( m_Overlapped.hEvent)
{
CloseHandle( m_Overlapped.hEvent);
}
#endif
#ifdef FLM_RING_ZERO_NLM
if( m_hSem)
{
(void)kSemaphoreFree( m_hSem);
m_hSem = NULL;
}
#endif
if( m_pFileHdl)
{
m_pFileHdl->Release();
}
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE F_FileAsyncClient::prepareForAsync(
IF_IOBuffer * pIOBuffer)
{
RCODE rc = NE_FLM_OK;
if( m_pIOBuffer || !m_pFileHdl)
{
rc = RC_SET_AND_ASSERT( NE_FLM_ILLEGAL_OP);
goto Exit;
}
#ifdef FLM_WIN
if( !m_Overlapped.hEvent)
{
if( (m_Overlapped.hEvent = CreateEvent( NULL, TRUE, FALSE, NULL)) == NULL)
{
rc = f_mapPlatformError( GetLastError(), NE_FLM_MEM);
goto Exit;
}
}
else
{
if( !ResetEvent( m_Overlapped.hEvent))
{
rc = f_mapPlatformError( GetLastError(), NE_FLM_MEM);
goto Exit;
}
}
#endif
#if defined( FLM_UNIX) && defined( FLM_HAS_ASYNC_IO)
f_memset( &m_aio, 0, sizeof( m_aio));
#endif
#ifdef FLM_RING_ZERO_NLM
if( !m_hSem)
{
if( (m_hSem = kSemaphoreAlloc( (BYTE *)"FTK_SEM", 0)) == NULL)
{
rc = RC_SET( NE_FLM_MEM);
goto Exit;
}
}
#endif
m_completionRc = NE_FLM_IO_PENDING;
m_uiBytesToDo = 0;
m_uiBytesDone = 0;
m_uiStartTime = FLM_GET_TIMER();
m_uiEndTime = m_uiStartTime;
if( pIOBuffer)
{
pIOBuffer->setAsyncClient( this);
m_pIOBuffer = pIOBuffer;
m_pIOBuffer->AddRef();
m_pIOBuffer->setPending();
}
f_atomicInc( &m_pFileHdl->m_numAsyncPending);
Exit:
return( rc);
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE FLMAPI F_FileAsyncClient::waitToComplete(
FLMBOOL bRelease)
{
RCODE completionRc = NE_FLM_OK;
FLMUINT uiBytesDone = 0;
#if defined( FLM_WIN)
DWORD udBytesDone;
if( !GetOverlappedResult( m_pFileHdl->m_hFile, &m_Overlapped,
&udBytesDone, TRUE))
{
completionRc = f_mapPlatformError( GetLastError(), NE_FLM_ASYNC_FAILED);
}
uiBytesDone = (FLMUINT)udBytesDone;
#endif
#if defined( FLM_UNIX)
#if defined( FLM_HAS_ASYNC_IO)
{
FLMINT iAsyncResult;
const struct aiocb * ppAio[ 1];
ppAio[ 0] = &m_aio;
for( ;;)
{
#ifdef FLM_AIX
aio_suspend( 1, ppAio);
#else
aio_suspend( ppAio, 1, NULL);
#endif
iAsyncResult = aio_error( &m_aio);
if( !iAsyncResult)
{
if( (iAsyncResult = aio_return( &m_aio)) < 0)
{
f_assert( 0);
completionRc = f_mapPlatformError( errno, NE_FLM_ASYNC_FAILED);
}
else
{
uiBytesDone = (FLMUINT)iAsyncResult;
}
break;
}
if( iAsyncResult == EINTR || iAsyncResult == EINPROGRESS)
{
continue;
}
f_assert( 0);
completionRc = f_mapPlatformError( iAsyncResult, NE_FLM_ASYNC_FAILED);
}
}
#endif
#endif
#ifdef FLM_RING_ZERO_NLM
if( kSemaphoreWait( m_hSem) != 0)
{
f_assert( 0);
}
f_assert( kSemaphoreExamineCount( m_hSem) == 0);
if( RC_BAD( completionRc))
{
m_uiBytesDone = 0;
}
else
{
m_uiBytesDone = m_uiBytesToDo;
uiBytesDone = m_uiBytesDone;
}
#endif
notifyComplete( completionRc, uiBytesDone, bRelease);
return( completionRc);
}
/****************************************************************************
Desc:
****************************************************************************/
void FLMAPI F_FileAsyncClient::notifyComplete(
RCODE completionRc,
FLMUINT uiBytesDone,
FLMBOOL bRelease)
{
m_uiEndTime = FLM_GET_TIMER();
m_completionRc = completionRc;
m_uiBytesDone = uiBytesDone;
if( m_pIOBuffer)
{
m_pIOBuffer->notifyComplete( m_completionRc);
m_pIOBuffer->Release();
m_pIOBuffer = NULL;
}
if( m_pFileHdl)
{
f_assert( m_pFileHdl->m_numAsyncPending);
f_atomicDec( &m_pFileHdl->m_numAsyncPending);
m_pFileHdl->Release();
m_pFileHdl = NULL;
}
if( bRelease)
{
Release();
}
}
/****************************************************************************
Desc:
****************************************************************************/
FLMUINT FLMAPI F_FileAsyncClient::getElapsedTime( void)
{
return( FLM_TIMER_UNITS_TO_MILLI(
FLM_ELAPSED_TIME( m_uiEndTime, m_uiStartTime)));
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE FLMAPI F_FileAsyncClient::getCompletionCode( void)
{
return( m_completionRc);
}
/****************************************************************************
Desc:
****************************************************************************/
void F_FileAsyncClient::signalComplete(
RCODE rc,
FLMUINT uiBytesDone)
{
F_UNREFERENCED_PARM( uiBytesDone);
#ifdef FLM_RING_ZERO_NLM
m_completionRc = rc;
f_assert( kSemaphoreExamineCount( m_hSem) == 0);
kSemaphoreSignal( m_hSem);
#else
F_UNREFERENCED_PARM( rc);
#endif
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE F_FileHdl::allocFileAsyncClient(
F_FileAsyncClient ** ppAsyncClient)
{
RCODE rc = NE_FLM_OK;
F_FileAsyncClient * pAsyncClient = NULL;
FLMBOOL bMutexLocked = FALSE;
f_mutexLock( m_hAsyncListMutex);
bMutexLocked = TRUE;
if( m_pFirstAvailAsync)
{
pAsyncClient = m_pFirstAvailAsync;
m_pFirstAvailAsync = m_pFirstAvailAsync->m_pNext;
pAsyncClient->m_pNext = NULL;
m_uiAvailAsyncCount--;
}
else
{
f_mutexUnlock( m_hAsyncListMutex);
bMutexLocked = FALSE;
if( (pAsyncClient = f_new F_FileAsyncClient) == NULL)
{
rc = RC_SET( NE_FLM_MEM);
goto Exit;
}
}
pAsyncClient->m_pFileHdl = this;
pAsyncClient->m_pFileHdl->AddRef();
*ppAsyncClient = pAsyncClient;
pAsyncClient = NULL;
Exit:
if( bMutexLocked)
{
f_mutexUnlock( m_hAsyncListMutex);
}
if( pAsyncClient)
{
pAsyncClient->Release();
}
return( rc);
}
/****************************************************************************
Desc: Read from a file
****************************************************************************/
RCODE F_FileHdl::directRead(
FLMUINT64 ui64ReadOffset,
FLMUINT uiBytesToRead,
void * pvBuffer,
IF_IOBuffer * pIOBuffer,
FLMUINT * puiBytesRead)
{
RCODE rc = NE_FLM_OK;
FLMUINT uiBytesRead = 0;
FLMUINT uiCurrentBytesRead;
FLMBYTE * pucReadBuffer;
FLMBYTE * pucDestBuffer;
FLMUINT uiMaxBytesToRead;
FLMBOOL bHitEOF;
if( !m_bFileOpened || !m_bDoDirectIO || !uiBytesToRead)
{
rc = RC_SET_AND_ASSERT( NE_FLM_ILLEGAL_OP);
goto Exit;
}
if( ui64ReadOffset == FLM_IO_CURRENT_POS)
{
ui64ReadOffset = m_ui64CurrentPos;
}
else
{
m_ui64CurrentPos = ui64ReadOffset;
}
// 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 ((ui64ReadOffset & m_ui64NotOnSectorBoundMask) ||
(((FLMUINT64)pucDestBuffer) & m_ui64NotOnSectorBoundMask) ||
(((FLMUINT64)uiBytesToRead & m_ui64NotOnSectorBoundMask)))
{
if( m_bRequireAlignedIO)
{
rc = RC_SET_AND_ASSERT( NE_FLM_MISALIGNED_IO);
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
// (ui64ReadOffset & m_ui64NotOnSectorBoundMask) 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 = (FLMUINT)roundToNextSector( uiBytesToRead +
(ui64ReadOffset & m_ui64NotOnSectorBoundMask));
// Can't read more than the aligned buffer will hold.
if (uiMaxBytesToRead > m_uiAlignedBuffSize)
{
uiMaxBytesToRead = m_uiAlignedBuffSize;
}
}
else
{
uiMaxBytesToRead = (FLMUINT)roundToNextSector( uiBytesToRead);
f_assert( uiMaxBytesToRead >= uiBytesToRead);
pucReadBuffer = pucDestBuffer;
}
bHitEOF = FALSE;
if( RC_BAD( rc = lowLevelRead( truncateToPrevSector( ui64ReadOffset),
uiMaxBytesToRead, pucReadBuffer, pIOBuffer, &uiCurrentBytesRead)))
{
if( rc != NE_FLM_IO_END_OF_FILE)
{
goto Exit;
}
bHitEOF = TRUE;
rc = NE_FLM_OK;
}
// 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( uiCurrentBytesRead && (ui64ReadOffset & m_ui64NotOnSectorBoundMask))
{
pucReadBuffer += (ui64ReadOffset & m_ui64NotOnSectorBoundMask);
f_assert( uiCurrentBytesRead >= m_uiBytesPerSector);
uiCurrentBytesRead -= (FLMUINT)(ui64ReadOffset & m_ui64NotOnSectorBoundMask);
}
// If bytes read is more than we actually need, truncate it back
// so that we only copy what we actually need.
if( uiCurrentBytesRead > uiBytesToRead)
{
uiCurrentBytesRead = uiBytesToRead;
}
uiBytesToRead -= uiCurrentBytesRead;
uiBytesRead += uiCurrentBytesRead;
// If using a different buffer for reading, copy the
// data read into the destination buffer.
if( pucDestBuffer != pucReadBuffer)
{
f_memcpy( pucDestBuffer, pucReadBuffer, uiCurrentBytesRead);
}
if( !uiBytesToRead)
{
break;
}
// Still more to read - did we hit EOF above?
if( bHitEOF)
{
rc = RC_SET( NE_FLM_IO_END_OF_FILE);
break;
}
pucDestBuffer += uiCurrentBytesRead;
ui64ReadOffset += uiCurrentBytesRead;
}
Exit:
if( puiBytesRead)
{
*puiBytesRead = uiBytesRead;
}
return( rc);
}
/****************************************************************************
Desc: Write to a file using direct I/O
****************************************************************************/
RCODE F_FileHdl::directWrite(
FLMUINT64 ui64WriteOffset,
FLMUINT uiBytesToWrite,
const void * pvBuffer,
IF_IOBuffer * pIOBuffer,
FLMUINT * puiBytesWritten)
{
RCODE rc = NE_FLM_OK;
FLMBYTE * pucWriteBuffer;
FLMBYTE * pucSrcBuffer;
FLMUINT uiBytesWritten = 0;
FLMUINT uiMaxBytesToWrite;
FLMUINT uiBytesBeingOutput;
FLMBOOL bWaitForWrite = (pIOBuffer == NULL)
? TRUE
: FALSE;
FLMUINT64 ui64LastWriteOffset;
FLMUINT uiLastWriteSize;
FLMBOOL bOffsetOnSectorBound;
FLMBOOL bBufferOnSectorBound;
FLMBOOL bSizeIsSectorMultiple;
if( !m_bFileOpened || !m_bDoDirectIO || !uiBytesToWrite)
{
rc = RC_SET_AND_ASSERT( NE_FLM_ILLEGAL_OP);
goto Exit;
}
if( pIOBuffer && pvBuffer && pvBuffer != pIOBuffer->getBufferPtr())
{
rc = RC_SET_AND_ASSERT( NE_FLM_ILLEGAL_OP);
goto Exit;
}
if( ui64WriteOffset == FLM_IO_CURRENT_POS)
{
ui64WriteOffset = m_ui64CurrentPos;
}
else
{
m_ui64CurrentPos = ui64WriteOffset;
}
if( pIOBuffer)
{
pucSrcBuffer = pIOBuffer->getBufferPtr();
}
else
{
pucSrcBuffer = (FLMBYTE *)pvBuffer;
}
for (;;)
{
bOffsetOnSectorBound = (ui64WriteOffset & m_ui64NotOnSectorBoundMask)
? FALSE
: TRUE;
bBufferOnSectorBound = (((FLMUINT)pucSrcBuffer) & m_ui64NotOnSectorBoundMask)
? FALSE
: TRUE;
bSizeIsSectorMultiple = (uiBytesToWrite & m_ui64NotOnSectorBoundMask)
? FALSE
: TRUE;
if( !bOffsetOnSectorBound ||
!bBufferOnSectorBound ||
!bSizeIsSectorMultiple)
{
if( m_bRequireAlignedIO)
{
rc = RC_SET_AND_ASSERT( NE_FLM_MISALIGNED_IO);
goto Exit;
}
bWaitForWrite = TRUE;
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
// (ui64WriteOffset & m_ui64NotOnSectorBoundMask) 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 = (FLMUINT)roundToNextSector( uiBytesToWrite +
(ui64WriteOffset & m_ui64NotOnSectorBoundMask));
// Can't write more than the aligned buffer will hold.
if( uiMaxBytesToWrite > m_uiAlignedBuffSize)
{
uiMaxBytesToWrite = m_uiAlignedBuffSize;
uiBytesBeingOutput = (FLMUINT)(uiMaxBytesToWrite -
(ui64WriteOffset & m_ui64NotOnSectorBoundMask));
}
else
{
uiBytesBeingOutput = uiBytesToWrite;
}
// If the write offset is not on a sector boundary, we must
// read at least the first sector into the buffer.
if( ui64WriteOffset & m_ui64NotOnSectorBoundMask)
{
// 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 = lowLevelRead(
truncateToPrevSector( ui64WriteOffset),
m_uiBytesPerSector, pucWriteBuffer, NULL, NULL)))
{
goto Exit;
}
}
// If we are writing more than one sector, and the last sector's
// worth of data we are writing out is only a partial sector,
// we must read in this sector as well.
if( (uiMaxBytesToWrite > m_uiBytesPerSector) &&
(uiMaxBytesToWrite > uiBytesToWrite))
{
// Read the last 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 = lowLevelRead(
(truncateToPrevSector( ui64WriteOffset)) +
(uiMaxBytesToWrite - m_uiBytesPerSector),
m_uiBytesPerSector,
(&pucWriteBuffer[ uiMaxBytesToWrite - m_uiBytesPerSector]),
NULL, NULL)))
{
if (rc == NE_FLM_IO_END_OF_FILE)
{
rc = NE_FLM_OK;
f_memset( &pucWriteBuffer [uiMaxBytesToWrite - m_uiBytesPerSector],
0, m_uiBytesPerSector);
}
else
{
goto Exit;
}
}
}
// Finally, copy the data from the source buffer into the
// write buffer.
f_memcpy( &pucWriteBuffer[ ui64WriteOffset & m_ui64NotOnSectorBoundMask],
pucSrcBuffer, uiBytesBeingOutput);
}
else
{
pucWriteBuffer = pucSrcBuffer;
uiMaxBytesToWrite = uiBytesToWrite;
uiBytesBeingOutput = uiBytesToWrite;
}
// Position the file to the nearest sector below the write offset.
ui64LastWriteOffset = truncateToPrevSector( ui64WriteOffset);
uiLastWriteSize = uiMaxBytesToWrite;
if( !bWaitForWrite)
{
f_assert( pucWriteBuffer == pIOBuffer->getBufferPtr());
if( RC_BAD( rc = lowLevelWrite( ui64LastWriteOffset,
uiMaxBytesToWrite, NULL, pIOBuffer, NULL)))
{
goto Exit;
}
}
else
{
if( pIOBuffer)
{
pIOBuffer->setPending();
}
rc = lowLevelWrite( ui64LastWriteOffset, uiMaxBytesToWrite,
pucWriteBuffer, NULL, NULL);
if( pIOBuffer)
{
pIOBuffer->notifyComplete( rc);
pIOBuffer = NULL;
}
if( RC_BAD( rc))
{
goto Exit;
}
}
uiBytesToWrite -= uiBytesBeingOutput;
uiBytesWritten += uiBytesBeingOutput;
if( !uiBytesToWrite)
{
break;
}
pucSrcBuffer += uiBytesBeingOutput;
ui64WriteOffset += uiBytesBeingOutput;
}
Exit:
if( puiBytesWritten)
{
*puiBytesWritten = uiBytesWritten;
}
return( rc);
}
/****************************************************************************
Desc: Sets current position of file.
****************************************************************************/
RCODE FLMAPI F_FileHdl::seek(
FLMUINT64 ui64Offset,
FLMINT iWhence,
FLMUINT64 * pui64NewOffset)
{
RCODE rc = NE_FLM_OK;
switch (iWhence)
{
case FLM_IO_SEEK_CUR:
{
m_ui64CurrentPos += ui64Offset;
break;
}
case FLM_IO_SEEK_SET:
{
m_ui64CurrentPos = ui64Offset;
break;
}
case FLM_IO_SEEK_END:
{
if( RC_BAD( rc = size( &m_ui64CurrentPos )))
{
goto Exit;
}
break;
}
default:
{
rc = RC_SET_AND_ASSERT( NE_FLM_NOT_IMPLEMENTED);
goto Exit;
}
}
if( pui64NewOffset)
{
*pui64NewOffset = m_ui64CurrentPos;
}
Exit:
return( rc);
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE FLMAPI F_FileHdl::tell(
FLMUINT64 * pui64Offset)
{
*pui64Offset = m_ui64CurrentPos;
return( NE_FLM_OK);
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE FLMAPI F_FileHdl::read(
FLMUINT64 ui64Offset,
FLMUINT uiLength,
void * pvBuffer,
FLMUINT * puiBytesRead)
{
if( m_bDoDirectIO)
{
return( directRead( ui64Offset, uiLength, pvBuffer,
NULL, puiBytesRead));
}
else
{
return( lowLevelRead( ui64Offset, uiLength, pvBuffer,
NULL, puiBytesRead));
}
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE FLMAPI F_FileHdl::read(
FLMUINT64 ui64ReadOffset,
FLMUINT uiBytesToRead,
IF_IOBuffer * pIOBuffer)
{
return( directRead( ui64ReadOffset, uiBytesToRead,
NULL, pIOBuffer, NULL));
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE FLMAPI F_FileHdl::write(
FLMUINT64 ui64WriteOffset,
FLMUINT uiBytesToWrite,
const void * pvBuffer,
FLMUINT * puiBytesWritten)
{
if( m_bDoDirectIO)
{
return( directWrite( ui64WriteOffset, uiBytesToWrite, pvBuffer,
NULL, puiBytesWritten));
}
else
{
return( lowLevelWrite( ui64WriteOffset, uiBytesToWrite,
pvBuffer, NULL, puiBytesWritten));
}
}
/****************************************************************************
Desc:
****************************************************************************/
RCODE FLMAPI F_FileHdl::write(
FLMUINT64 ui64WriteOffset,
FLMUINT uiBytesToWrite,
IF_IOBuffer * pIOBuffer)
{
RCODE rc = NE_FLM_OK;
if( m_bDoDirectIO)
{
if( RC_BAD( rc = directWrite( ui64WriteOffset, uiBytesToWrite,
NULL, pIOBuffer, NULL)))
{
goto Exit;
}
}
else
{
pIOBuffer->setPending();
rc = lowLevelWrite( ui64WriteOffset, uiBytesToWrite,
pIOBuffer->getBufferPtr(), NULL, NULL);
pIOBuffer->notifyComplete( rc);
if( RC_BAD( rc))
{
goto Exit;
}
}
Exit:
return( rc);
}