//------------------------------------------------------------------------------ // 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( f_atomicInc( &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 RCODE rc = NE_FLM_OK; 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 = f_atomicDec( &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( FLMBOOL bOkToReuse) { FLMINT iRefCnt; if( m_refCnt == 1) { #if !defined( FLM_UNIX) FLMUINT uiSignalCount = f_semGetSignalCount( m_hSem); #endif #if !defined( FLM_UNIX) f_assert( uiSignalCount <= 1); #endif f_assert( !m_pIOBuffer); f_assert( !m_pFileHdl); #if !defined( FLM_UNIX) if( uiSignalCount == 1) { // The application may not have cared to wait on this // individual write to complete. Since the // semaphore has been signaled, we need to consume // the signal so that the next time this // async client is used, the semaphore will // block until that I/O operation is complete. f_semWait( m_hSem, F_WAITFOREVER); } #endif if( m_pIOBuffer) { m_pIOBuffer->Release(); m_pIOBuffer = NULL; } if( m_pFileHdl) { m_pFileHdl->Release(); m_pFileHdl = NULL; } if( bOkToReuse) { 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 = f_atomicDec( &m_refCnt); } f_mutexUnlock( F_FileHdl::m_hAsyncListMutex); } else { iRefCnt = f_atomicDec( &m_refCnt); } } else { iRefCnt = f_atomicDec( &m_refCnt); } if( !m_refCnt) { delete this; } return( iRefCnt); } /**************************************************************************** Desc: ****************************************************************************/ F_FileAsyncClient::~F_FileAsyncClient() { f_assert( !m_pNext); f_assert( !m_refCnt); #if !defined( FLM_UNIX) if( m_hSem != F_SEM_NULL) { f_semDestroy( &m_hSem); } #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; } #if !defined( FLM_UNIX) if( m_hSem == F_SEM_NULL) { if( RC_BAD( rc = f_semCreate( &m_hSem))) { goto Exit; } } f_assert( f_semGetSignalCount( m_hSem) == 0); #endif #if defined( FLM_UNIX) && defined( FLM_HAS_ASYNC_IO) f_memset( &m_aio, 0, sizeof( m_aio)); #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( void) { RCODE completionRc = NE_FLM_OK; #if defined( FLM_UNIX) FLMUINT uiBytesDone = 0; #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 m_completionRc = completionRc; m_uiBytesDone = uiBytesDone; #else if( RC_BAD( completionRc = f_semWait( m_hSem, F_WAITFOREVER))) { return( completionRc); } f_assert( f_semGetSignalCount( m_hSem) == 0); #ifndef FLM_RING_ZERO_NLM if( m_pIOBuffer) { f_assert( !m_pIOBuffer->isPending()); } #endif f_assert( m_completionRc != NE_FLM_IO_PENDING); completionRc = m_completionRc; #endif #if defined( FLM_UNIX) || defined( FLM_RING_ZERO_NLM) notifyComplete( m_completionRc, m_uiBytesDone); #endif return( completionRc); } /**************************************************************************** Desc: ****************************************************************************/ void FLMAPI F_FileAsyncClient::notifyComplete( RCODE completionRc, FLMUINT uiBytesDone) { f_assert( !m_pIOBuffer || m_pIOBuffer->isPending()); #if !defined( FLM_UNIX) && !defined( FLM_RING_ZERO_NLM) f_assert( m_completionRc == NE_FLM_IO_PENDING); #endif AddRef(); m_completionRc = completionRc; m_uiBytesDone = uiBytesDone; m_uiEndTime = FLM_GET_TIMER(); m_completionRc = completionRc; m_uiBytesDone = uiBytesDone; if( m_pFileHdl) { f_assert( m_pFileHdl->m_numAsyncPending); f_atomicDec( &m_pFileHdl->m_numAsyncPending); m_pFileHdl->Release(); m_pFileHdl = NULL; } if( m_pIOBuffer) { IF_IOBuffer * pIOBuffer = m_pIOBuffer; m_pIOBuffer = NULL; pIOBuffer->notifyComplete( m_completionRc); pIOBuffer->Release(); pIOBuffer = NULL; } #if !defined( FLM_UNIX) && !defined( FLM_RING_ZERO_NLM) f_semSignal( m_hSem); f_assert( f_semGetSignalCount( m_hSem) == 1); #endif f_assert( !m_pIOBuffer || !m_pIOBuffer->isPending()); 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: ****************************************************************************/ 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); }