//------------------------------------------------------------------------------ // Desc: Contains the methods for the F_FileHdl class on Windows platforms. // // Tabs: 3 // // Copyright (c) 1999-2006 Novell, Inc. All Rights Reserved. // // This program is free software; you can redistribute it and/or // modify it under the terms of version 2 of the GNU General Public // License as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, contact Novell, Inc. // // To contact Novell about this file by physical or electronic mail, // you may find current contact information at www.novell.com // // $Id: fwin.cpp 3115 2006-01-19 13:24:39 -0700 (Thu, 19 Jan 2006) dsanders $ //------------------------------------------------------------------------------ #include "ftksys.h" #if defined( FLM_WIN) extern FLMATOMIC gv_openFiles; /**************************************************************************** Desc: ****************************************************************************/ F_FileHdl::F_FileHdl() { initCommonData(); m_hFile = INVALID_HANDLE_VALUE; m_bFlushRequired = FALSE; } /**************************************************************************** Desc: ****************************************************************************/ F_FileHdl::~F_FileHdl() { close(); } /*************************************************************************** Desc: Open or create a file. ***************************************************************************/ RCODE F_FileHdl::openOrCreate( const char * pszFileName, FLMUINT uiIoFlags, FLMBOOL bCreateFlag) { RCODE rc = NE_FLM_OK; DWORD udAccessMode = 0; DWORD udShareMode = 0; DWORD udCreateMode = 0; DWORD udAttrFlags = 0; DWORD udErrCode; FLMBOOL bOpenInAsyncMode = FALSE; FLMBOOL bDoDirectIO; IF_FileSystem * pFileSystem = f_getFileSysPtr(); f_assert( !m_bFileOpened); f_assert( !m_pszFileName); bDoDirectIO = (uiIoFlags & FLM_IO_DIRECT) ? TRUE : FALSE; // Save the file name if( RC_BAD( rc = f_alloc( F_PATH_MAX_SIZE, &m_pszFileName))) { goto Exit; } f_strcpy( m_pszFileName, pszFileName); // If doing direct IO, need to get the sector size. if( bDoDirectIO) { if( RC_BAD( rc = pFileSystem->getSectorSize( pszFileName, &m_uiBytesPerSector))) { goto Exit; } m_ui64NotOnSectorBoundMask = m_uiBytesPerSector - 1; m_ui64GetSectorBoundMask = ~m_ui64NotOnSectorBoundMask; } // Only enable asynchronous writes if direct I/O is enabled. if( bDoDirectIO && f_getFileSysPtr()->canDoAsync()) { bOpenInAsyncMode = TRUE; } // Set up the file characteristics requested by caller. if( uiIoFlags & FLM_IO_SH_DENYRW) { udShareMode = 0; uiIoFlags &= ~FLM_IO_SH_DENYRW; } else if( uiIoFlags & FLM_IO_SH_DENYWR) { udShareMode = FILE_SHARE_READ; uiIoFlags &= ~FLM_IO_SH_DENYWR; } else if (uiIoFlags & FLM_IO_SH_DENYNONE) { udShareMode = (FILE_SHARE_READ | FILE_SHARE_WRITE); uiIoFlags &= ~FLM_IO_SH_DENYNONE; } else { udShareMode = (FILE_SHARE_READ | FILE_SHARE_WRITE); } // Begin setting the CreateFile flags and fields udAttrFlags = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS; if( bDoDirectIO) { // Specifying FILE_FLAG_NO_BUFFERING and FILE_FLAG_WRITE_THROUGH // results in the data being immediately flushed to disk without // going through the system cache. The operating system also // requests a write-through the hard disk cache to persistent // media. However, not all hardware supports this write-through // capability. udAttrFlags |= FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH; } if( bOpenInAsyncMode) { udAttrFlags |= FILE_FLAG_OVERLAPPED; } if( bCreateFlag) { if( uiIoFlags & FLM_IO_EXCL) { udCreateMode = CREATE_NEW; } else { udCreateMode = CREATE_ALWAYS; } } else { udCreateMode = OPEN_EXISTING; } udAccessMode = GENERIC_READ | GENERIC_WRITE; if( (!bCreateFlag) && (uiIoFlags & FLM_IO_RDONLY)) { udAccessMode = GENERIC_READ; } Retry_Create: if( (m_hFile = CreateFile( (LPCTSTR)pszFileName, udAccessMode, udShareMode, NULL, udCreateMode, udAttrFlags, NULL)) == INVALID_HANDLE_VALUE) { udErrCode = GetLastError(); if( (udErrCode == ERROR_PATH_NOT_FOUND) && (uiIoFlags & FLM_IO_CREATE_DIR)) { char szTemp[ F_PATH_MAX_SIZE]; char szDirPath[ F_PATH_MAX_SIZE]; uiIoFlags &= ~FLM_IO_CREATE_DIR; // Remove the file name for which we are creating the directory. if( RC_OK( pFileSystem->pathReduce( m_pszFileName, szDirPath, szTemp))) { if( RC_OK( rc = pFileSystem->createDir( szDirPath))) { goto Retry_Create; } else { goto Exit; } } } rc = f_mapPlatformError( udErrCode, (RCODE)(bCreateFlag ? (RCODE)(bDoDirectIO ? (RCODE)NE_FLM_DIRECT_CREATING_FILE : (RCODE)NE_FLM_CREATING_FILE) : (RCODE)(bDoDirectIO ? (RCODE)NE_FLM_DIRECT_OPENING_FILE : (RCODE)NE_FLM_OPENING_FILE))); goto Exit; } if( uiIoFlags & FLM_IO_DELETE_ON_RELEASE) { m_bDeleteOnRelease = TRUE; } else { m_bDeleteOnRelease = FALSE; } // Allocate at least 64K - this will handle most read and write // operations and will also be a multiple of the sector size most of // the time. The calculation below rounds it up to the next sector // boundary if it is not already on one. m_uiAlignedBuffSize = 64 * 1024; if( bDoDirectIO) { m_uiAlignedBuffSize = roundToNextSector( m_uiAlignedBuffSize); } if( RC_BAD( rc = f_allocAlignedBuffer( m_uiAlignedBuffSize, &m_pucAlignedBuff))) { goto Exit; } m_bFileOpened = TRUE; m_ui64CurrentPos = 0; m_bOpenedReadOnly = (uiIoFlags & FLM_IO_RDONLY) ? TRUE : FALSE; m_bOpenedExclusive = (uiIoFlags & FLM_IO_SH_DENYRW) ? TRUE : FALSE; m_bDoDirectIO = bDoDirectIO; m_bOpenedInAsyncMode = bOpenInAsyncMode; m_bFlushRequired = FALSE; f_atomicInc( &gv_openFiles); Exit: if( RC_BAD( rc)) { close(); } return( rc); } /**************************************************************************** Desc: Close a file ****************************************************************************/ RCODE FLMAPI F_FileHdl::close( void) { if( m_bFlushRequired) { flush(); } if( m_hFile != INVALID_HANDLE_VALUE) { CloseHandle( m_hFile); m_hFile = INVALID_HANDLE_VALUE; } if( m_bDeleteOnRelease) { DeleteFile( (LPTSTR)m_pszFileName); m_bDeleteOnRelease = FALSE; } if( m_bFileOpened) { f_atomicDec( &gv_openFiles); } freeCommonData(); m_bFileOpened = FALSE; m_ui64CurrentPos = 0; m_bOpenedReadOnly = FALSE; m_bOpenedExclusive = FALSE; m_bDoDirectIO = FALSE; m_bOpenedInAsyncMode = FALSE; m_bFlushRequired = FALSE; return( NE_FLM_OK); } /**************************************************************************** Desc: ****************************************************************************/ RCODE FLMAPI F_FileHdl::flush( void) { RCODE rc = NE_FLM_OK; if( !m_bDoDirectIO || m_bFlushRequired) { if( !FlushFileBuffers( m_hFile)) { rc = f_mapPlatformError( GetLastError(), NE_FLM_FLUSHING_FILE); } m_bFlushRequired = FALSE; } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_FileHdl::lowLevelRead( FLMUINT64 ui64ReadOffset, FLMUINT uiBytesToRead, void * pvBuffer, IF_IOBuffer * pIOBuffer, FLMUINT * puiBytesRead) { RCODE rc = NE_FLM_OK; DWORD uiBytesRead = 0; OVERLAPPED * pOverlapped = NULL; LARGE_INTEGER liTmp; F_FileAsyncClient * pAsyncClient = NULL; FLMBOOL bWaitForRead = FALSE; if( pIOBuffer && pvBuffer && pvBuffer != pIOBuffer->getBufferPtr()) { rc = RC_SET_AND_ASSERT( NE_FLM_ILLEGAL_OP); goto Exit; } if( ui64ReadOffset == FLM_IO_CURRENT_POS) { ui64ReadOffset = m_ui64CurrentPos; } else { m_ui64CurrentPos = ui64ReadOffset; } if( !pvBuffer) { pvBuffer = pIOBuffer->getBufferPtr(); } if( m_bOpenedInAsyncMode) { if( RC_BAD( rc = allocFileAsyncClient( &pAsyncClient))) { goto Exit; } if( RC_BAD( rc = pAsyncClient->prepareForAsync( pIOBuffer))) { goto Exit; } if( !pIOBuffer) { f_assert( pvBuffer); bWaitForRead = TRUE; } pAsyncClient->m_uiBytesToDo = uiBytesToRead; pOverlapped = &pAsyncClient->m_Overlapped; pOverlapped->Offset = (DWORD)(ui64ReadOffset & 0xFFFFFFFF); pOverlapped->OffsetHigh = (DWORD)(ui64ReadOffset >> 32); pIOBuffer = NULL; if( !ReadFile( m_hFile, pvBuffer, uiBytesToRead, &uiBytesRead, pOverlapped)) { DWORD udErrCode = GetLastError(); if( udErrCode != ERROR_IO_PENDING) { rc = f_mapPlatformError( udErrCode, NE_FLM_READING_FILE); pAsyncClient->notifyComplete( rc, uiBytesRead, FALSE); goto Exit; } } if( bWaitForRead) { if( RC_BAD( rc = pAsyncClient->waitToComplete( FALSE))) { if( rc != NE_FLM_IO_END_OF_FILE) { goto Exit; } rc = NE_FLM_OK; } uiBytesRead = pAsyncClient->m_uiBytesDone; } else { uiBytesRead = uiBytesToRead; } } else { if( pIOBuffer) { pIOBuffer->setPending(); } liTmp.QuadPart = ui64ReadOffset; if( !SetFilePointerEx( m_hFile, liTmp, NULL, FILE_BEGIN)) { rc = f_mapPlatformError( GetLastError(), NE_FLM_POSITIONING_IN_FILE); } else { if( !ReadFile( m_hFile, pvBuffer, uiBytesToRead, &uiBytesRead, NULL)) { rc = f_mapPlatformError( GetLastError(), NE_FLM_READING_FILE); } } if( pIOBuffer) { pIOBuffer->notifyComplete( rc); pIOBuffer = NULL; } if( RC_BAD( rc)) { goto Exit; } } m_ui64CurrentPos += uiBytesRead; if( uiBytesRead < uiBytesToRead) { rc = RC_SET( NE_FLM_IO_END_OF_FILE); goto Exit; } Exit: f_assert( uiBytesRead || RC_BAD( rc)); if( pAsyncClient) { pAsyncClient->Release(); } if( pIOBuffer && !pIOBuffer->isPending()) { f_assert( RC_BAD( rc)); pIOBuffer->notifyComplete( rc); } if( puiBytesRead) { *puiBytesRead = uiBytesRead; } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_FileHdl::lowLevelWrite( FLMUINT64 ui64WriteOffset, FLMUINT uiBytesToWrite, const void * pvBuffer, IF_IOBuffer * pIOBuffer, FLMUINT * puiBytesWritten) { RCODE rc = NE_FLM_OK; OVERLAPPED * pOverlapped = NULL; LARGE_INTEGER liTmp; DWORD uiBytesWritten = 0; F_FileAsyncClient * pAsyncClient = NULL; FLMBOOL bWaitForWrite = FALSE; FLMUINT uiTotalBytesToExtend; FLMUINT64 ui64CurrFileSize; 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( m_bDoDirectIO && !m_numAsyncPending && m_uiExtendSize) { if( RC_BAD( rc = getPreWriteExtendSize( ui64WriteOffset, uiBytesToWrite, &ui64CurrFileSize, &uiTotalBytesToExtend))) { goto Exit; } if( uiTotalBytesToExtend) { if( RC_BAD( rc = extendFile( ui64CurrFileSize, uiTotalBytesToExtend))) { goto Exit; } } } if( !pvBuffer) { pvBuffer = pIOBuffer->getBufferPtr(); } if( m_bOpenedInAsyncMode) { if( RC_BAD( rc = allocFileAsyncClient( &pAsyncClient))) { goto Exit; } if( RC_BAD( rc = pAsyncClient->prepareForAsync( pIOBuffer))) { goto Exit; } if( !pIOBuffer) { bWaitForWrite = TRUE; } pAsyncClient->m_uiBytesToDo = uiBytesToWrite; pOverlapped = &pAsyncClient->m_Overlapped; pOverlapped->Offset = (DWORD)(ui64WriteOffset & 0xFFFFFFFF); pOverlapped->OffsetHigh = (DWORD)(ui64WriteOffset >> 32); pIOBuffer = NULL; if( !WriteFile( m_hFile, pvBuffer, uiBytesToWrite, &uiBytesWritten, pOverlapped)) { DWORD udErrCode = GetLastError(); if( udErrCode != ERROR_IO_PENDING) { rc = f_mapPlatformError( udErrCode, NE_FLM_WRITING_FILE); pAsyncClient->notifyComplete( rc, uiBytesWritten, FALSE); goto Exit; } } if( bWaitForWrite) { if( RC_BAD( rc = pAsyncClient->waitToComplete( FALSE))) { if( rc != NE_FLM_IO_DISK_FULL) { goto Exit; } rc = NE_FLM_OK; } uiBytesWritten = pAsyncClient->m_uiBytesDone; } else { uiBytesWritten = uiBytesToWrite; } } else { if( pIOBuffer) { pIOBuffer->setPending(); } liTmp.QuadPart = ui64WriteOffset; if( !SetFilePointerEx( m_hFile, liTmp, NULL, FILE_BEGIN)) { rc = f_mapPlatformError( GetLastError(), NE_FLM_POSITIONING_IN_FILE); } else { if( !WriteFile( m_hFile, pvBuffer, uiBytesToWrite, &uiBytesWritten, NULL)) { rc = f_mapPlatformError( GetLastError(), NE_FLM_WRITING_FILE); } } if( pIOBuffer) { pIOBuffer->notifyComplete( rc); pIOBuffer = NULL; } if( RC_BAD( rc)) { goto Exit; } } m_ui64CurrentPos += uiBytesWritten; if( uiBytesWritten < uiBytesToWrite) { rc = RC_SET_AND_ASSERT( NE_FLM_IO_DISK_FULL); goto Exit; } Exit: if( pAsyncClient) { pAsyncClient->Release(); } if( pIOBuffer && !pIOBuffer->isPending()) { f_assert( RC_BAD( rc)); pIOBuffer->notifyComplete( rc); } if( puiBytesWritten) { *puiBytesWritten = uiBytesWritten; } return( rc); } /**************************************************************************** Desc: Return the size of the file ****************************************************************************/ RCODE FLMAPI F_FileHdl::size( FLMUINT64 * pui64Size) { RCODE rc = NE_FLM_OK; LARGE_INTEGER liTmp; if( !GetFileSizeEx( m_hFile, &liTmp)) { rc = f_mapPlatformError( GetLastError(), NE_FLM_GETTING_FILE_SIZE); goto Exit; } *pui64Size = liTmp.QuadPart; Exit: return( rc); } /**************************************************************************** Desc: Truncate the file to the indicated size WARNING: Direct IO methods are calling this method. Make sure that all changes to this method work in direct IO mode. ****************************************************************************/ RCODE FLMAPI F_FileHdl::truncate( FLMUINT64 ui64NewSize) { RCODE rc = NE_FLM_OK; LARGE_INTEGER liTmp; FLMUINT64 ui64CurrentSize; f_assert( m_bFileOpened); if( RC_BAD( rc = size( &ui64CurrentSize))) { goto Exit; } if( ui64NewSize >= ui64CurrentSize) { goto Exit; } liTmp.QuadPart = ui64NewSize; if( !SetFilePointerEx( m_hFile, liTmp, NULL, FILE_BEGIN)) { rc = f_mapPlatformError( GetLastError(), NE_FLM_POSITIONING_IN_FILE); goto Exit; } if( !SetEndOfFile( m_hFile)) { rc = f_mapPlatformError( GetLastError(), NE_FLM_TRUNCATING_FILE); goto Exit; } m_bFlushRequired = TRUE; Exit: return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE F_FileHdl::extendFile( FLMUINT64 ui64FileSize, FLMUINT uiTotalBytesToExtend) { RCODE rc = NE_FLM_OK; FLMUINT uiBytesToWrite; FLMUINT uiBytesWritten; FLMBYTE * pucBuffer = NULL; FLMUINT uiBufferSize; F_FileAsyncClient * pAsyncClient = NULL; uiBufferSize = 64 * 1024; if( RC_BAD( rc = f_allocAlignedBuffer( uiBufferSize, &pucBuffer))) { goto Exit; } f_memset( pucBuffer, 0, uiBufferSize); // Extend the file until we run out of bytes to write. while( uiTotalBytesToExtend) { if( (uiBytesToWrite = uiBufferSize) > uiTotalBytesToExtend) { uiBytesToWrite = uiTotalBytesToExtend; } if( m_bOpenedInAsyncMode) { OVERLAPPED * pOverlapped; if( RC_BAD( rc = allocFileAsyncClient( &pAsyncClient))) { goto Exit; } if( RC_BAD( rc = pAsyncClient->prepareForAsync( NULL))) { goto Exit; } pAsyncClient->m_uiBytesToDo = uiBytesToWrite; pOverlapped = &pAsyncClient->m_Overlapped; pOverlapped->Offset = (DWORD)(ui64FileSize & 0xFFFFFFFF); pOverlapped->OffsetHigh = (DWORD)(ui64FileSize >> 32); if( !WriteFile( m_hFile, pucBuffer, uiBytesToWrite, &uiBytesWritten, pOverlapped)) { DWORD udErrCode = GetLastError(); if( udErrCode != ERROR_IO_PENDING) { rc = f_mapPlatformError( udErrCode, NE_FLM_WRITING_FILE); pAsyncClient->notifyComplete( rc, uiBytesWritten, FALSE); goto Exit; } } if( RC_BAD( rc = pAsyncClient->waitToComplete( FALSE))) { goto Exit; } uiBytesWritten = pAsyncClient->m_uiBytesDone; pAsyncClient->Release(); pAsyncClient = NULL; } else { LONG lDummy = 0; if( SetFilePointer( m_hFile, (LONG)ui64FileSize, &lDummy, FILE_BEGIN) == 0xFFFFFFFF) { rc = f_mapPlatformError( GetLastError(), NE_FLM_POSITIONING_IN_FILE); goto Exit; } if( !WriteFile( m_hFile, (LPVOID)pucBuffer, (DWORD)uiBytesToWrite, &uiBytesWritten, NULL)) { rc = f_mapPlatformError( GetLastError(), NE_FLM_WRITING_FILE); goto Exit; } } // No more room on disk if( uiBytesWritten < uiBytesToWrite) { rc = RC_SET( NE_FLM_IO_DISK_FULL); goto Exit; } uiTotalBytesToExtend -= uiBytesToWrite; ui64FileSize += uiBytesToWrite; } m_bFlushRequired = TRUE; Exit: if( pucBuffer) { f_freeAlignedBuffer( &pucBuffer); } if( pAsyncClient) { pAsyncClient->Release(); } return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE FLMAPI F_FileHdl::lock( void) { RCODE rc = NE_FLM_OK; if( !LockFile( m_hFile, 0, 0, 1, 1)) { rc = RC_SET( NE_FLM_IO_FILE_LOCK_ERR); goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: ****************************************************************************/ RCODE FLMAPI F_FileHdl::unlock( void) { RCODE rc = NE_FLM_OK; if( !UnlockFile( m_hFile, 0, 0, 1, 1)) { rc = RC_SET( NE_FLM_IO_FILE_LOCK_ERR); goto Exit; } Exit: return( rc); } /**************************************************************************** Desc: ****************************************************************************/ void FLMAPI f_yieldCPU( void) { Sleep( 0); } /********************************************************************** Desc: **********************************************************************/ RCODE FLMAPI f_chdir( const char * pszDir) { RCODE rc = NE_FLM_OK; if( _chdir( pszDir) != 0) { rc = f_mapPlatformError( GetLastError(), NE_FLM_IO_PATH_NOT_FOUND); goto Exit; } Exit: return( rc); } /********************************************************************** Desc: **********************************************************************/ RCODE FLMAPI f_getcwd( char * pszDir) { RCODE rc = NE_FLM_OK; if( _getcwd( pszDir, F_PATH_MAX_SIZE) == NULL) { *pszDir = 0; rc = f_mapPlatformError( GetLastError(), NE_FLM_IO_PATH_NOT_FOUND); goto Exit; } Exit: return( rc); } #endif // FLM_WIN /**************************************************************************** Desc: Deletes a file ****************************************************************************/ #if defined( FLM_WATCOM_NLM) || defined( FLM_OSX) int gv_ftkwinDummy(void) { return( 0); } #endif