git-svn-id: https://svn.code.sf.net/p/flaim/code/trunk@356 0109f412-320b-0410-ab79-c3e0c5ffbbe6
1683 lines
38 KiB
C++
1683 lines
38 KiB
C++
//------------------------------------------------------------------------------
|
|
// 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)
|
|
|
|
RCODE MapWinErrorToFlaim(
|
|
DWORD udErrCode,
|
|
RCODE defaultRc);
|
|
|
|
FSTATIC RCODE _DeleteFile(
|
|
char * path);
|
|
|
|
/***************************************************************************
|
|
Desc: Maps WIN errors to IO errors.
|
|
***************************************************************************/
|
|
RCODE MapWinErrorToFlaim(
|
|
DWORD udErrCode,
|
|
RCODE defaultRc)
|
|
{
|
|
|
|
// Switch on passed in error code value
|
|
|
|
switch( udErrCode)
|
|
{
|
|
case ERROR_NOT_ENOUGH_MEMORY:
|
|
case ERROR_OUTOFMEMORY:
|
|
return( RC_SET( NE_FLM_MEM));
|
|
|
|
case ERROR_BAD_NETPATH:
|
|
case ERROR_BAD_PATHNAME:
|
|
case ERROR_DIRECTORY:
|
|
case ERROR_FILE_NOT_FOUND:
|
|
case ERROR_INVALID_DRIVE:
|
|
case ERROR_INVALID_NAME:
|
|
case ERROR_NO_NET_OR_BAD_PATH:
|
|
case ERROR_PATH_NOT_FOUND:
|
|
return( RC_SET( NE_FLM_IO_PATH_NOT_FOUND));
|
|
|
|
case ERROR_ACCESS_DENIED:
|
|
case ERROR_SHARING_VIOLATION:
|
|
case ERROR_FILE_EXISTS:
|
|
case ERROR_ALREADY_EXISTS:
|
|
return( RC_SET( NE_FLM_IO_ACCESS_DENIED));
|
|
|
|
case ERROR_BUFFER_OVERFLOW:
|
|
case ERROR_FILENAME_EXCED_RANGE:
|
|
return( RC_SET( NE_FLM_IO_PATH_TOO_LONG));
|
|
|
|
case ERROR_DISK_FULL:
|
|
case ERROR_HANDLE_DISK_FULL:
|
|
return( RC_SET( NE_FLM_IO_DISK_FULL));
|
|
|
|
case ERROR_CURRENT_DIRECTORY:
|
|
case ERROR_DIR_NOT_EMPTY:
|
|
return( RC_SET( NE_FLM_IO_DIRECTORY_ERR));
|
|
|
|
case ERROR_DIRECT_ACCESS_HANDLE:
|
|
case ERROR_INVALID_HANDLE:
|
|
case ERROR_INVALID_TARGET_HANDLE:
|
|
return( RC_SET( NE_FLM_IO_BAD_FILE_HANDLE));
|
|
|
|
case ERROR_HANDLE_EOF:
|
|
return( RC_SET( NE_FLM_IO_END_OF_FILE));
|
|
|
|
case ERROR_OPEN_FAILED:
|
|
return( RC_SET( NE_FLM_IO_OPEN_ERR));
|
|
|
|
case ERROR_CANNOT_MAKE:
|
|
return( RC_SET( NE_FLM_IO_PATH_CREATE_FAILURE));
|
|
|
|
case ERROR_LOCK_FAILED:
|
|
case ERROR_LOCK_VIOLATION:
|
|
return( RC_SET( NE_FLM_IO_FILE_LOCK_ERR));
|
|
|
|
case ERROR_NEGATIVE_SEEK:
|
|
case ERROR_SEEK:
|
|
case ERROR_SEEK_ON_DEVICE:
|
|
return( RC_SET( NE_FLM_IO_SEEK_ERR));
|
|
|
|
case ERROR_NO_MORE_FILES:
|
|
case ERROR_NO_MORE_SEARCH_HANDLES:
|
|
return( RC_SET( NE_FLM_IO_NO_MORE_FILES));
|
|
|
|
case ERROR_TOO_MANY_OPEN_FILES:
|
|
return( RC_SET( NE_FLM_IO_TOO_MANY_OPEN_FILES));
|
|
|
|
case NO_ERROR:
|
|
return( NE_FLM_OK);
|
|
|
|
case ERROR_DISK_CORRUPT:
|
|
case ERROR_DISK_OPERATION_FAILED:
|
|
case ERROR_FILE_CORRUPT:
|
|
case ERROR_FILE_INVALID:
|
|
case ERROR_NOT_SAME_DEVICE:
|
|
case ERROR_IO_DEVICE:
|
|
default:
|
|
return( RC_SET( defaultRc));
|
|
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
F_FileHdl::F_FileHdl()
|
|
{
|
|
m_pNext = NULL;
|
|
m_pPrev = NULL;
|
|
m_bInList = FALSE;
|
|
m_uiAvailTime = 0;
|
|
m_bFileOpened = FALSE;
|
|
m_bDeleteOnRelease = FALSE;
|
|
m_bOpenedReadOnly = FALSE;
|
|
m_pszFileName = NULL;
|
|
|
|
m_FileHandle = INVALID_HANDLE_VALUE;
|
|
m_uiBlockSize = 0;
|
|
m_uiBytesPerSector = 0;
|
|
m_ui64NotOnSectorBoundMask = 0;
|
|
m_ui64GetSectorBoundMask = 0;
|
|
m_bDoDirectIO = FALSE;
|
|
m_uiExtendSize = 0;
|
|
// m_uiMaxAutoExtendSize = gv_XFlmSysData.uiMaxFileSize;
|
|
m_pucAlignedBuff = NULL;
|
|
m_uiAlignedBuffSize = 0;
|
|
m_ui64CurrentPos = 0;
|
|
m_bCanDoAsync = FALSE;
|
|
m_Overlapped.hEvent = NULL;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
F_FileHdl::~F_FileHdl()
|
|
{
|
|
if( m_bFileOpened)
|
|
{
|
|
(void)close();
|
|
}
|
|
|
|
if (m_pucAlignedBuff)
|
|
{
|
|
(void)VirtualFree( m_pucAlignedBuff, 0, MEM_RELEASE);
|
|
m_pucAlignedBuff = NULL;
|
|
m_uiAlignedBuffSize = 0;
|
|
}
|
|
|
|
if (m_Overlapped.hEvent)
|
|
{
|
|
CloseHandle( m_Overlapped.hEvent);
|
|
}
|
|
|
|
if (m_pszFileName)
|
|
{
|
|
f_free( &m_pszFileName);
|
|
}
|
|
}
|
|
|
|
/***************************************************************************
|
|
Desc: Open or create a file.
|
|
***************************************************************************/
|
|
RCODE F_FileHdl::openOrCreate(
|
|
const char * pszFileName,
|
|
FLMUINT uiAccess,
|
|
FLMBOOL bCreateFlag)
|
|
{
|
|
RCODE rc = NE_FLM_OK;
|
|
char szSaveFileName[ F_PATH_MAX_SIZE];
|
|
DWORD udAccessMode = 0;
|
|
DWORD udShareMode = 0;
|
|
DWORD udCreateMode = 0;
|
|
DWORD udAttrFlags = 0;
|
|
DWORD udErrCode;
|
|
|
|
m_bDoDirectIO = (uiAccess & FLM_IO_DIRECT) ? TRUE : FALSE;
|
|
|
|
/* Save the file name in case we have to create the directory. */
|
|
|
|
if ((bCreateFlag) && (uiAccess & FLM_IO_CREATE_DIR))
|
|
{
|
|
f_strcpy( szSaveFileName, pszFileName);
|
|
}
|
|
|
|
// If doing direct IO, need to get the sector size.
|
|
|
|
if (m_bDoDirectIO)
|
|
{
|
|
if (!m_uiBlockSize)
|
|
{
|
|
m_bDoDirectIO = FALSE;
|
|
}
|
|
else
|
|
{
|
|
if (RC_BAD( rc = gv_pFileSystem->getSectorSize(
|
|
pszFileName, &m_uiBytesPerSector)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
m_ui64NotOnSectorBoundMask = m_uiBytesPerSector - 1;
|
|
m_ui64GetSectorBoundMask = ~m_ui64NotOnSectorBoundMask;
|
|
|
|
// Can't do direct IO if the block size isn't a multiple of
|
|
// the sector size.
|
|
|
|
if (m_uiBlockSize < m_uiBytesPerSector ||
|
|
m_uiBlockSize % m_uiBytesPerSector != 0)
|
|
{
|
|
m_bDoDirectIO = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Only enable asynchronous writes if direct I/O is enabled.
|
|
|
|
if (m_bDoDirectIO)
|
|
{
|
|
m_bCanDoAsync = TRUE;
|
|
}
|
|
|
|
// Set up the file characteristics requested by caller.
|
|
|
|
if (uiAccess & FLM_IO_SH_DENYRW)
|
|
{
|
|
udShareMode = 0;
|
|
uiAccess &= ~FLM_IO_SH_DENYRW;
|
|
}
|
|
else if (uiAccess & FLM_IO_SH_DENYWR)
|
|
{
|
|
udShareMode = FILE_SHARE_READ;
|
|
uiAccess &= ~FLM_IO_SH_DENYWR;
|
|
}
|
|
else if (uiAccess & FLM_IO_SH_DENYNONE)
|
|
{
|
|
udShareMode = (FILE_SHARE_READ | FILE_SHARE_WRITE);
|
|
uiAccess &= ~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 (m_bDoDirectIO)
|
|
{
|
|
udAttrFlags |= FILE_FLAG_NO_BUFFERING;
|
|
}
|
|
|
|
if (m_bCanDoAsync)
|
|
{
|
|
udAttrFlags |= FILE_FLAG_OVERLAPPED;
|
|
}
|
|
|
|
if (bCreateFlag)
|
|
{
|
|
if (uiAccess & FLM_IO_EXCL)
|
|
{
|
|
udCreateMode = CREATE_NEW;
|
|
}
|
|
else
|
|
{
|
|
udCreateMode = CREATE_ALWAYS;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
udCreateMode = OPEN_EXISTING;
|
|
}
|
|
|
|
udAccessMode = GENERIC_READ | GENERIC_WRITE;
|
|
|
|
if( (!bCreateFlag) && (uiAccess & FLM_IO_RDONLY))
|
|
{
|
|
udAccessMode = GENERIC_READ;
|
|
}
|
|
|
|
Retry_Create:
|
|
|
|
// Try to create or open the file
|
|
|
|
if( (m_FileHandle = CreateFile( (LPCTSTR)pszFileName, udAccessMode,
|
|
udShareMode, NULL, udCreateMode,
|
|
udAttrFlags, NULL)) == INVALID_HANDLE_VALUE)
|
|
{
|
|
udErrCode = GetLastError();
|
|
if ((udErrCode == ERROR_PATH_NOT_FOUND) && (uiAccess & FLM_IO_CREATE_DIR))
|
|
{
|
|
char szTemp[ F_PATH_MAX_SIZE];
|
|
char szDirPath[ F_PATH_MAX_SIZE];
|
|
|
|
uiAccess &= ~FLM_IO_CREATE_DIR;
|
|
|
|
// Remove the file name for which we are creating the directory.
|
|
|
|
if( RC_OK( gv_pFileSystem->pathReduce( szSaveFileName, szDirPath, szTemp)))
|
|
{
|
|
if( RC_OK( rc = gv_pFileSystem->createDir( szDirPath)))
|
|
{
|
|
goto Retry_Create;
|
|
}
|
|
else
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
rc = MapWinErrorToFlaim( udErrCode,
|
|
(RCODE)(bCreateFlag
|
|
? (RCODE)(m_bDoDirectIO
|
|
? (RCODE)NE_FLM_DIRECT_CREATING_FILE
|
|
: (RCODE)NE_FLM_CREATING_FILE)
|
|
: (RCODE)(m_bDoDirectIO
|
|
? (RCODE)NE_FLM_DIRECT_OPENING_FILE
|
|
: (RCODE)NE_FLM_OPENING_FILE)));
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
|
|
if( RC_BAD( rc))
|
|
{
|
|
m_FileHandle = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Create a file
|
|
****************************************************************************/
|
|
RCODE FLMAPI F_FileHdl::create(
|
|
const char * pszFileName,
|
|
FLMUINT uiIoFlags )
|
|
{
|
|
RCODE rc = NE_FLM_OK;
|
|
|
|
flmAssert( m_bFileOpened == FALSE);
|
|
|
|
if( m_bDeleteOnRelease)
|
|
{
|
|
// This file handle had better not been used for another file
|
|
// before. Otherwise, we will get a memory leak.
|
|
|
|
flmAssert( m_pszFileName == NULL);
|
|
|
|
if( RC_BAD( rc = f_alloc( F_PATH_MAX_SIZE, &m_pszFileName)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
f_strcpy( m_pszFileName, pszFileName);
|
|
}
|
|
|
|
if( RC_BAD( rc = openOrCreate( pszFileName, uiIoFlags, TRUE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
m_bFileOpened = TRUE;
|
|
m_ui64CurrentPos = 0;
|
|
m_bOpenedExclusive = (uiIoFlags & FLM_IO_SH_DENYRW) ? TRUE : FALSE;
|
|
|
|
Exit:
|
|
|
|
if( RC_BAD( rc) && m_bDeleteOnRelease && m_pszFileName)
|
|
{
|
|
f_free( &m_pszFileName);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Create a unique file name in the specified directory
|
|
****************************************************************************/
|
|
RCODE FLMAPI F_FileHdl::createUnique(
|
|
const 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;
|
|
|
|
szFileName[0] = '\0';
|
|
szTmpPath[0] = '\0';
|
|
flmAssert( m_bFileOpened == FALSE);
|
|
|
|
if( m_bDeleteOnRelease)
|
|
{
|
|
|
|
// This file handle had better not been used for another file
|
|
// before. Otherwise, we will get a memory leak.
|
|
|
|
flmAssert( m_pszFileName == NULL);
|
|
|
|
}
|
|
f_strcpy( szDirPath, pszDirName);
|
|
|
|
/*
|
|
Search backwards replacing trailing spaces with NULLs.
|
|
*/
|
|
|
|
pszTmp = (char *) szDirPath;
|
|
pszTmp += (f_strlen( pszTmp) - 1);
|
|
while( pszTmp >= (char *) szDirPath && (*pszTmp == 0x20))
|
|
{
|
|
*pszTmp = 0;
|
|
pszTmp--;
|
|
}
|
|
|
|
/* Append a backslash if one isn't already there. */
|
|
|
|
if (pszTmp >= (char *) szDirPath && *pszTmp != '\\')
|
|
{
|
|
pszTmp++;
|
|
*pszTmp++ = '\\';
|
|
}
|
|
else
|
|
{
|
|
pszTmp++;
|
|
}
|
|
*pszTmp = 0;
|
|
|
|
if ((pszFileExtension) && (f_strlen( pszFileExtension) >= 3))
|
|
{
|
|
bModext = FALSE;
|
|
}
|
|
|
|
uiCount = 0;
|
|
do
|
|
{
|
|
gv_pFileSystem->pathCreateUniqueName( &uiBaseTime, szFileName, pszFileExtension,
|
|
&ucHighByte, bModext);
|
|
|
|
//need to strcpy to the buffer b/c it is uninitialized
|
|
f_strcpy( szTmpPath, szDirPath);
|
|
gv_pFileSystem->pathAppend( szTmpPath, szFileName);
|
|
if( m_pszFileName)
|
|
{
|
|
f_free( &m_pszFileName);
|
|
}
|
|
|
|
rc = create( szTmpPath, uiIoFlags | FLM_IO_EXCL);
|
|
if (rc == NE_FLM_IO_DISK_FULL)
|
|
{
|
|
(void)_DeleteFile( szTmpPath);
|
|
goto Exit;
|
|
}
|
|
if ((rc == NE_FLM_IO_PATH_NOT_FOUND) || (rc == NE_FLM_IO_INVALID_PASSWORD))
|
|
{
|
|
goto Exit;
|
|
}
|
|
} 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;
|
|
}
|
|
m_bFileOpened = TRUE;
|
|
m_bOpenedExclusive = (uiIoFlags & FLM_IO_SH_DENYRW) ? TRUE : FALSE;
|
|
|
|
// Created file name needs to be returned.
|
|
f_strcpy( (char *)pszDirName, szTmpPath);
|
|
|
|
Exit:
|
|
|
|
if( RC_BAD( rc) && m_pszFileName)
|
|
{
|
|
f_free( &m_pszFileName);
|
|
}
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Open a file
|
|
****************************************************************************/
|
|
RCODE FLMAPI F_FileHdl::open(
|
|
const char * pszFileName,
|
|
FLMUINT uiIoFlags)
|
|
{
|
|
RCODE rc = NE_FLM_OK;
|
|
|
|
flmAssert( m_bFileOpened == FALSE);
|
|
|
|
if( m_bDeleteOnRelease)
|
|
{
|
|
|
|
// This file handle had better not been used for another file
|
|
// before. Otherwise, we will get a memory leak.
|
|
|
|
flmAssert( m_pszFileName == NULL);
|
|
|
|
if( RC_BAD( rc = f_alloc( F_PATH_MAX_SIZE, &m_pszFileName)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
f_strcpy( m_pszFileName, pszFileName);
|
|
}
|
|
|
|
// Loop on error open conditions.
|
|
|
|
for(;;)
|
|
{
|
|
if( RC_OK( rc = openOrCreate( pszFileName, uiIoFlags, FALSE)))
|
|
{
|
|
break;
|
|
}
|
|
|
|
if( rc != NE_FLM_IO_TOO_MANY_OPEN_FILES )
|
|
{
|
|
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;
|
|
|
|
Exit:
|
|
|
|
if( RC_BAD( rc) && m_bDeleteOnRelease && m_pszFileName)
|
|
{
|
|
f_free( &m_pszFileName);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Close a file
|
|
****************************************************************************/
|
|
RCODE FLMAPI F_FileHdl::close( void)
|
|
{
|
|
FLMBOOL bDeleteAllowed = TRUE;
|
|
RCODE rc = NE_FLM_OK;
|
|
|
|
if( !m_bFileOpened)
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if (!CloseHandle( m_FileHandle))
|
|
{
|
|
rc = MapWinErrorToFlaim( GetLastError(), NE_FLM_CLOSING_FILE);
|
|
goto Exit;
|
|
}
|
|
|
|
m_FileHandle = INVALID_HANDLE_VALUE;
|
|
m_bFileOpened = m_bOpenedReadOnly = m_bOpenedExclusive = FALSE;
|
|
|
|
if (m_bDeleteOnRelease )
|
|
{
|
|
flmAssert( NULL != m_pszFileName );
|
|
|
|
if( bDeleteAllowed)
|
|
{
|
|
(void)_DeleteFile( m_pszFileName);
|
|
}
|
|
m_bDeleteOnRelease = FALSE;
|
|
f_free( &m_pszFileName);
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Flush IO to disk
|
|
****************************************************************************/
|
|
RCODE FLMAPI F_FileHdl::flush( void)
|
|
{
|
|
RCODE rc = NE_FLM_OK;
|
|
|
|
if( !m_bDoDirectIO)
|
|
{
|
|
if( !FlushFileBuffers( m_FileHandle))
|
|
{
|
|
rc = MapWinErrorToFlaim( GetLastError(), NE_FLM_FLUSHING_FILE);
|
|
}
|
|
}
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Allocate an aligned buffer.
|
|
****************************************************************************/
|
|
RCODE F_FileHdl::allocAlignBuffer( void)
|
|
{
|
|
RCODE rc = NE_FLM_OK;
|
|
|
|
// 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 = roundToNextSector( 64 * 1024);
|
|
if ((m_pucAlignedBuff = (FLMBYTE *)VirtualAlloc( NULL,
|
|
(DWORD)m_uiAlignedBuffSize,
|
|
MEM_COMMIT, PAGE_READWRITE)) == NULL)
|
|
{
|
|
rc = MapWinErrorToFlaim( GetLastError(), NE_FLM_MEM);
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Position and do a single read operation.
|
|
****************************************************************************/
|
|
RCODE F_FileHdl::doOneRead(
|
|
FLMUINT64 ui64ReadOffset,
|
|
FLMUINT uiBytesToRead,
|
|
void * pvReadBuffer,
|
|
FLMUINT * puiBytesRead)
|
|
{
|
|
RCODE rc = NE_FLM_OK;
|
|
OVERLAPPED * pOverlapped;
|
|
LARGE_INTEGER liTmp;
|
|
|
|
// Position the file to the specified offset.
|
|
|
|
if (!m_bCanDoAsync)
|
|
{
|
|
liTmp.QuadPart = ui64ReadOffset;
|
|
if( !SetFilePointerEx( m_FileHandle, liTmp, NULL, FILE_BEGIN))
|
|
{
|
|
rc = MapWinErrorToFlaim( GetLastError(), NE_FLM_POSITIONING_IN_FILE);
|
|
goto Exit;
|
|
}
|
|
|
|
pOverlapped = NULL;
|
|
}
|
|
else
|
|
{
|
|
if (!m_Overlapped.hEvent)
|
|
{
|
|
if ((m_Overlapped.hEvent = CreateEvent( NULL, TRUE,
|
|
FALSE, NULL)) == NULL)
|
|
{
|
|
rc = MapWinErrorToFlaim( GetLastError(),
|
|
NE_FLM_SETTING_UP_FOR_READ);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
pOverlapped = &m_Overlapped;
|
|
pOverlapped->Offset = (DWORD)(ui64ReadOffset & 0xFFFFFFFF);
|
|
pOverlapped->OffsetHigh = (DWORD)(ui64ReadOffset >> 32);
|
|
|
|
if( !ResetEvent( pOverlapped->hEvent))
|
|
{
|
|
rc = MapWinErrorToFlaim( GetLastError(), NE_FLM_SETTING_UP_FOR_READ);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Do the read
|
|
|
|
if( !ReadFile( m_FileHandle, pvReadBuffer, uiBytesToRead,
|
|
puiBytesRead, pOverlapped))
|
|
{
|
|
DWORD udErr = GetLastError();
|
|
|
|
if( udErr == ERROR_IO_PENDING && m_bCanDoAsync)
|
|
{
|
|
if( !GetOverlappedResult( m_FileHandle,
|
|
pOverlapped, puiBytesRead, TRUE))
|
|
{
|
|
rc = MapWinErrorToFlaim( GetLastError(), NE_FLM_READING_FILE);
|
|
goto Exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
rc = MapWinErrorToFlaim( udErr, NE_FLM_READING_FILE);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Read from a file - reads using aligned buffers and offsets - only
|
|
sector boundaries
|
|
****************************************************************************/
|
|
RCODE F_FileHdl::directRead(
|
|
FLMUINT64 ui64ReadOffset,
|
|
FLMUINT uiBytesToRead,
|
|
void * pvBuffer,
|
|
FLMBOOL bBuffHasFullSectors,
|
|
FLMUINT * puiBytesReadRV)
|
|
{
|
|
RCODE rc = NE_FLM_OK;
|
|
FLMUINT uiBytesRead;
|
|
FLMBYTE * pucReadBuffer;
|
|
FLMBYTE * pucDestBuffer;
|
|
FLMUINT uiMaxBytesToRead;
|
|
FLMBOOL bHitEOF;
|
|
|
|
flmAssert( m_bFileOpened);
|
|
|
|
if( puiBytesReadRV)
|
|
{
|
|
*puiBytesReadRV = 0;
|
|
}
|
|
|
|
if( ui64ReadOffset == FLM_IO_CURRENT_POS)
|
|
{
|
|
ui64ReadOffset = m_ui64CurrentPos;
|
|
}
|
|
|
|
// 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) &&
|
|
(!bBuffHasFullSectors)))
|
|
{
|
|
if (!m_pucAlignedBuff)
|
|
{
|
|
if (RC_BAD( rc = allocAlignBuffer()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
pucReadBuffer = m_pucAlignedBuff;
|
|
|
|
// Must read enough bytes to cover all of the sectors that
|
|
// contain the data we are trying to read. The value of
|
|
// (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 = roundToNextSector( uiBytesToRead +
|
|
(ui64ReadOffset & m_ui64NotOnSectorBoundMask));
|
|
|
|
// Can't read more than the aligned buffer will hold.
|
|
|
|
if (uiMaxBytesToRead > m_uiAlignedBuffSize)
|
|
{
|
|
uiMaxBytesToRead = m_uiAlignedBuffSize;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
uiMaxBytesToRead = roundToNextSector( uiBytesToRead);
|
|
flmAssert( uiMaxBytesToRead >= uiBytesToRead);
|
|
pucReadBuffer = pucDestBuffer;
|
|
}
|
|
|
|
bHitEOF = FALSE;
|
|
if (RC_BAD( rc = doOneRead( truncateToPrevSector( ui64ReadOffset),
|
|
uiMaxBytesToRead, pucReadBuffer, &uiBytesRead)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( uiBytesRead < uiMaxBytesToRead)
|
|
{
|
|
bHitEOF = TRUE;
|
|
}
|
|
|
|
// If the offset we want to read from is not on a sector
|
|
// boundary, increment the read buffer pointer to the
|
|
// offset where the data we need starts and decrement the
|
|
// bytes read by the difference between the start of the
|
|
// sector and the actual read offset.
|
|
|
|
if (ui64ReadOffset & m_ui64NotOnSectorBoundMask)
|
|
{
|
|
pucReadBuffer += (ui64ReadOffset & m_ui64NotOnSectorBoundMask);
|
|
flmAssert( uiBytesRead >= m_uiBytesPerSector);
|
|
uiBytesRead -= (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( uiBytesRead > uiBytesToRead)
|
|
{
|
|
uiBytesRead = uiBytesToRead;
|
|
}
|
|
|
|
uiBytesToRead -= uiBytesRead;
|
|
|
|
if( puiBytesReadRV)
|
|
{
|
|
(*puiBytesReadRV) += uiBytesRead;
|
|
}
|
|
|
|
m_ui64CurrentPos = ui64ReadOffset + uiBytesRead;
|
|
|
|
// If using a different buffer for reading, copy the
|
|
// data read into the destination buffer.
|
|
|
|
if (pucDestBuffer != pucReadBuffer)
|
|
{
|
|
f_memcpy( pucDestBuffer, pucReadBuffer, uiBytesRead);
|
|
}
|
|
|
|
if (!uiBytesToRead)
|
|
{
|
|
break;
|
|
}
|
|
|
|
// Still more to read - did we hit EOF above?
|
|
|
|
if (bHitEOF)
|
|
{
|
|
rc = RC_SET( NE_FLM_IO_END_OF_FILE);
|
|
break;
|
|
}
|
|
|
|
pucDestBuffer += uiBytesRead;
|
|
ui64ReadOffset += uiBytesRead;
|
|
}
|
|
|
|
Exit:
|
|
return( rc );
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Read from a file
|
|
****************************************************************************/
|
|
RCODE FLMAPI F_FileHdl::read(
|
|
FLMUINT64 ui64ReadOffset,
|
|
FLMUINT uiBytesToRead,
|
|
void * pvBuffer,
|
|
FLMUINT * puiBytesReadRV)
|
|
{
|
|
RCODE rc = NE_FLM_OK;
|
|
FLMUINT uiBytesRead;
|
|
|
|
// Do the direct IO call if enabled.
|
|
|
|
if (m_bDoDirectIO)
|
|
{
|
|
rc = directRead( ui64ReadOffset, uiBytesToRead,
|
|
pvBuffer, FALSE, puiBytesReadRV);
|
|
goto Exit;
|
|
}
|
|
|
|
// If not doing direct IO, a single read call will do.
|
|
|
|
flmAssert( m_bFileOpened);
|
|
if( puiBytesReadRV)
|
|
{
|
|
*puiBytesReadRV = 0;
|
|
}
|
|
|
|
if( ui64ReadOffset == FLM_IO_CURRENT_POS)
|
|
{
|
|
ui64ReadOffset = m_ui64CurrentPos;
|
|
}
|
|
|
|
if( RC_BAD( rc = doOneRead( ui64ReadOffset, uiBytesToRead,
|
|
pvBuffer, &uiBytesRead)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
if( puiBytesReadRV)
|
|
{
|
|
*puiBytesReadRV = uiBytesRead;
|
|
}
|
|
|
|
m_ui64CurrentPos = ui64ReadOffset + uiBytesRead;
|
|
|
|
if (uiBytesRead < uiBytesToRead)
|
|
{
|
|
rc = RC_SET( NE_FLM_IO_END_OF_FILE);
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
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: Return the size of the file
|
|
****************************************************************************/
|
|
RCODE FLMAPI F_FileHdl::size(
|
|
FLMUINT64 * pui64Size)
|
|
{
|
|
RCODE rc = NE_FLM_OK;
|
|
LARGE_INTEGER liTmp;
|
|
|
|
if( !GetFileSizeEx( m_FileHandle, &liTmp))
|
|
{
|
|
rc = MapWinErrorToFlaim( GetLastError(), NE_FLM_GETTING_FILE_SIZE);
|
|
goto Exit;
|
|
}
|
|
|
|
*pui64Size = liTmp.QuadPart;
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc:
|
|
****************************************************************************/
|
|
RCODE FLMAPI F_FileHdl::tell(
|
|
FLMUINT64 * pui64Offset)
|
|
{
|
|
*pui64Offset = m_ui64CurrentPos;
|
|
return( NE_FLM_OK);
|
|
}
|
|
|
|
/****************************************************************************
|
|
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 ui64Size)
|
|
{
|
|
RCODE rc = NE_FLM_OK;
|
|
LARGE_INTEGER liTmp;
|
|
|
|
flmAssert( m_bFileOpened);
|
|
|
|
// Position the file to the nearest sector below the read offset.
|
|
|
|
liTmp.QuadPart = ui64Size;
|
|
if( !SetFilePointerEx( m_FileHandle, liTmp, NULL, FILE_BEGIN))
|
|
{
|
|
rc = MapWinErrorToFlaim( GetLastError(), NE_FLM_POSITIONING_IN_FILE);
|
|
goto Exit;
|
|
}
|
|
|
|
// Set the new file size.
|
|
|
|
if( !SetEndOfFile( m_FileHandle))
|
|
{
|
|
rc = MapWinErrorToFlaim( GetLastError(), NE_FLM_TRUNCATING_FILE);
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Handles when a file is extended in direct IO mode. May extend the
|
|
file some more. Will always call FlushFileBuffers to ensure that
|
|
the new file size gets written out.
|
|
****************************************************************************/
|
|
RCODE F_FileHdl::extendFile(
|
|
FLMUINT64 ui64EndOfLastWrite,
|
|
FLMUINT uiMaxBytesToExtend,
|
|
FLMBOOL bFlush)
|
|
{
|
|
RCODE rc = NE_FLM_OK;
|
|
OVERLAPPED * pOverlapped;
|
|
FLMUINT uiTotalBytesToExtend;
|
|
FLMUINT uiBytesToWrite;
|
|
FLMUINT uiBytesWritten;
|
|
LARGE_INTEGER liTmp;
|
|
|
|
if ((uiTotalBytesToExtend = uiMaxBytesToExtend) != 0)
|
|
{
|
|
if (ui64EndOfLastWrite > m_uiMaxAutoExtendSize)
|
|
{
|
|
uiTotalBytesToExtend = 0;
|
|
}
|
|
else
|
|
{
|
|
// Don't extend beyond maximum file size.
|
|
|
|
if (m_uiMaxAutoExtendSize - ui64EndOfLastWrite < uiTotalBytesToExtend)
|
|
{
|
|
uiTotalBytesToExtend = m_uiMaxAutoExtendSize - ui64EndOfLastWrite;
|
|
}
|
|
|
|
// If the extend size is not on a sector boundary, round it down.
|
|
|
|
uiTotalBytesToExtend = truncateToPrevSector( uiTotalBytesToExtend);
|
|
}
|
|
}
|
|
|
|
if (uiTotalBytesToExtend)
|
|
{
|
|
// Allocate an aligned buffer if we haven't already.
|
|
|
|
if (!m_pucAlignedBuff)
|
|
{
|
|
if (RC_BAD( rc = allocAlignBuffer()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
f_memset( m_pucAlignedBuff, 0, m_uiAlignedBuffSize);
|
|
}
|
|
|
|
// Extend the file until we run out of bytes to write.
|
|
|
|
while (uiTotalBytesToExtend)
|
|
{
|
|
if ((uiBytesToWrite = m_uiAlignedBuffSize) > uiTotalBytesToExtend)
|
|
{
|
|
uiBytesToWrite = uiTotalBytesToExtend;
|
|
}
|
|
|
|
if (!m_bCanDoAsync)
|
|
{
|
|
liTmp.QuadPart = ui64EndOfLastWrite;
|
|
if( !SetFilePointerEx( m_FileHandle, liTmp, NULL, FILE_BEGIN))
|
|
{
|
|
rc = MapWinErrorToFlaim( GetLastError(),
|
|
NE_FLM_POSITIONING_IN_FILE);
|
|
goto Exit;
|
|
}
|
|
|
|
pOverlapped = NULL;
|
|
}
|
|
else
|
|
{
|
|
pOverlapped = &m_Overlapped;
|
|
if (!pOverlapped->hEvent)
|
|
{
|
|
if ((pOverlapped->hEvent = CreateEvent( NULL, TRUE,
|
|
FALSE, NULL)) == NULL)
|
|
{
|
|
rc = MapWinErrorToFlaim( GetLastError(),
|
|
NE_FLM_SETTING_UP_FOR_WRITE);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
pOverlapped->Offset = (DWORD)(ui64EndOfLastWrite & 0xFFFFFFFF);
|
|
pOverlapped->OffsetHigh = (DWORD)(ui64EndOfLastWrite >> 32);
|
|
|
|
if (!ResetEvent( pOverlapped->hEvent))
|
|
{
|
|
rc = MapWinErrorToFlaim( GetLastError(),
|
|
NE_FLM_SETTING_UP_FOR_WRITE);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Do the write
|
|
|
|
if( !WriteFile( m_FileHandle, m_pucAlignedBuff,
|
|
uiBytesToWrite, &uiBytesWritten, pOverlapped))
|
|
{
|
|
rc = MapWinErrorToFlaim( GetLastError(), NE_FLM_WRITING_FILE);
|
|
|
|
// Don't care if it is a disk full error, because
|
|
// extending the file is optional work.
|
|
|
|
if( rc == NE_FLM_IO_DISK_FULL)
|
|
{
|
|
rc = NE_FLM_OK;
|
|
break;
|
|
}
|
|
|
|
goto Exit;
|
|
}
|
|
|
|
// NO more room on disk, but that's OK because we were only
|
|
// extending the file beyond where it needed to be. If that
|
|
// fails, we will just flush what we have done so far.
|
|
|
|
if( uiBytesWritten < uiBytesToWrite)
|
|
{
|
|
break;
|
|
}
|
|
|
|
uiTotalBytesToExtend -= uiBytesToWrite;
|
|
ui64EndOfLastWrite += uiBytesToWrite;
|
|
}
|
|
|
|
// Flush the file buffers to ensure that the file size gets written
|
|
// out.
|
|
|
|
if( bFlush)
|
|
{
|
|
if( !FlushFileBuffers( m_FileHandle))
|
|
{
|
|
rc = MapWinErrorToFlaim( GetLastError(), NE_FLM_FLUSHING_FILE);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
return( rc);
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Write to a file using direct IO
|
|
****************************************************************************/
|
|
RCODE F_FileHdl::directWrite(
|
|
FLMUINT64 ui64WriteOffset,
|
|
FLMUINT uiBytesToWrite,
|
|
const void * pvBuffer,
|
|
F_IOBuffer * pBufferObj,
|
|
FLMBOOL bBuffHasFullSectors,
|
|
FLMBOOL bZeroFill,
|
|
FLMUINT * puiBytesWrittenRV)
|
|
{
|
|
RCODE rc = NE_FLM_OK;
|
|
FLMUINT uiBytesRead;
|
|
FLMUINT uiBytesWritten;
|
|
FLMBYTE * pucWriteBuffer;
|
|
FLMBYTE * pucSrcBuffer;
|
|
FLMUINT uiMaxBytesToWrite;
|
|
FLMUINT64 ui64CurrFileSize;
|
|
FLMUINT uiBytesBeingOutput;
|
|
OVERLAPPED * pOverlapped;
|
|
DWORD udErr;
|
|
FLMBOOL bExtendFile = FALSE;
|
|
FLMBOOL bDoAsync = (pBufferObj != NULL)
|
|
? TRUE
|
|
: FALSE;
|
|
FLMBOOL bDidAsync = FALSE;
|
|
FLMUINT64 ui64LastWriteOffset;
|
|
FLMUINT uiLastWriteSize;
|
|
LARGE_INTEGER liTmp;
|
|
|
|
flmAssert( m_bFileOpened);
|
|
|
|
#ifdef FLM_DEBUG
|
|
if (bDoAsync)
|
|
{
|
|
flmAssert( m_bCanDoAsync);
|
|
}
|
|
#endif
|
|
|
|
if( puiBytesWrittenRV)
|
|
{
|
|
*puiBytesWrittenRV = 0;
|
|
}
|
|
|
|
if( ui64WriteOffset == FLM_IO_CURRENT_POS)
|
|
{
|
|
ui64WriteOffset = m_ui64CurrentPos;
|
|
}
|
|
|
|
// Determine if the write will extend the file beyond its
|
|
// current size. If so, we will need to call FlushFileBuffers
|
|
|
|
if( !GetFileSizeEx( m_FileHandle, &liTmp))
|
|
{
|
|
rc = MapWinErrorToFlaim( GetLastError(), NE_FLM_GETTING_FILE_SIZE);
|
|
goto Exit;
|
|
}
|
|
|
|
ui64CurrFileSize = liTmp.QuadPart;
|
|
|
|
if( ui64WriteOffset + uiBytesToWrite > ui64CurrFileSize &&
|
|
m_uiExtendSize != (FLMUINT)(~0))
|
|
{
|
|
bExtendFile = TRUE;
|
|
|
|
if( ui64WriteOffset > ui64CurrFileSize)
|
|
{
|
|
|
|
// Fill in the empty space.
|
|
|
|
if (RC_BAD( rc = extendFile( ui64CurrFileSize,
|
|
roundToNextSector( ui64WriteOffset - ui64CurrFileSize), FALSE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Can't do asynchronous if we are going to extend the file.
|
|
|
|
bDoAsync = FALSE;
|
|
}
|
|
|
|
// This loop is for direct IO - must make sure we use
|
|
// aligned buffers.
|
|
|
|
pucSrcBuffer = (FLMBYTE *)pvBuffer;
|
|
for (;;)
|
|
{
|
|
|
|
// See if we are using an aligned buffer. If not, allocate
|
|
// one (if not already allocated), and use it.
|
|
|
|
if ((ui64WriteOffset & m_ui64NotOnSectorBoundMask) ||
|
|
(((FLMUINT)pucSrcBuffer) & m_ui64NotOnSectorBoundMask) ||
|
|
((uiBytesToWrite & m_ui64NotOnSectorBoundMask) &&
|
|
(!bBuffHasFullSectors)))
|
|
{
|
|
|
|
// Cannot be using a temporary write buffer if we are doing
|
|
// asynchronous writes!
|
|
|
|
flmAssert( !bDoAsync || !m_bCanDoAsync);
|
|
if (!m_pucAlignedBuff)
|
|
{
|
|
if (RC_BAD( rc = allocAlignBuffer()))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
pucWriteBuffer = m_pucAlignedBuff;
|
|
|
|
// Must write enough bytes to cover all of the sectors that
|
|
// contain the data we are trying to write out. The value of
|
|
// (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 = roundToNextSector( uiBytesToWrite +
|
|
(ui64WriteOffset & m_ui64NotOnSectorBoundMask));
|
|
|
|
// Can't write more than the aligned buffer will hold.
|
|
|
|
if (uiMaxBytesToWrite > m_uiAlignedBuffSize)
|
|
{
|
|
uiMaxBytesToWrite = m_uiAlignedBuffSize;
|
|
uiBytesBeingOutput = 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 = doOneRead( truncateToPrevSector( ui64WriteOffset),
|
|
m_uiBytesPerSector, pucWriteBuffer, &uiBytesRead)))
|
|
{
|
|
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) &&
|
|
(!bBuffHasFullSectors))
|
|
{
|
|
|
|
// 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 = doOneRead(
|
|
(truncateToPrevSector( ui64WriteOffset)) +
|
|
(uiMaxBytesToWrite - m_uiBytesPerSector),
|
|
m_uiBytesPerSector,
|
|
(&pucWriteBuffer [uiMaxBytesToWrite - m_uiBytesPerSector]),
|
|
&uiBytesRead)))
|
|
{
|
|
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
|
|
{
|
|
uiMaxBytesToWrite = roundToNextSector( uiBytesToWrite);
|
|
uiBytesBeingOutput = uiBytesToWrite;
|
|
pucWriteBuffer = pucSrcBuffer;
|
|
if( bZeroFill && uiMaxBytesToWrite > uiBytesToWrite)
|
|
{
|
|
f_memset( &pucWriteBuffer [uiBytesToWrite], 0,
|
|
uiMaxBytesToWrite - uiBytesToWrite);
|
|
}
|
|
}
|
|
|
|
// Position the file to the nearest sector below the write offset.
|
|
|
|
ui64LastWriteOffset = truncateToPrevSector( ui64WriteOffset);
|
|
if (!m_bCanDoAsync)
|
|
{
|
|
liTmp.QuadPart = ui64LastWriteOffset;
|
|
if( !SetFilePointerEx( m_FileHandle, liTmp, NULL, FILE_BEGIN))
|
|
{
|
|
rc = MapWinErrorToFlaim( GetLastError(),
|
|
NE_FLM_POSITIONING_IN_FILE);
|
|
goto Exit;
|
|
}
|
|
|
|
pOverlapped = NULL;
|
|
}
|
|
else
|
|
{
|
|
if (!pBufferObj)
|
|
{
|
|
pOverlapped = &m_Overlapped;
|
|
}
|
|
else
|
|
{
|
|
pOverlapped = pBufferObj->getOverlapped();
|
|
pBufferObj->setFileHandle( m_FileHandle);
|
|
}
|
|
|
|
if (!pOverlapped->hEvent)
|
|
{
|
|
if ((pOverlapped->hEvent = CreateEvent( NULL, TRUE,
|
|
FALSE, NULL)) == NULL)
|
|
{
|
|
rc = MapWinErrorToFlaim( GetLastError(),
|
|
NE_FLM_SETTING_UP_FOR_WRITE);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
pOverlapped->Offset = (DWORD)(ui64LastWriteOffset & 0xFFFFFFFF);
|
|
pOverlapped->OffsetHigh = (DWORD)(ui64LastWriteOffset >> 32);
|
|
|
|
if (!ResetEvent( pOverlapped->hEvent))
|
|
{
|
|
rc = MapWinErrorToFlaim( GetLastError(),
|
|
NE_FLM_SETTING_UP_FOR_WRITE);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
// Do the write
|
|
|
|
uiLastWriteSize = uiMaxBytesToWrite;
|
|
if( !WriteFile( m_FileHandle, (LPVOID)pucWriteBuffer,
|
|
(DWORD)uiMaxBytesToWrite, &uiBytesWritten,
|
|
pOverlapped))
|
|
{
|
|
udErr = GetLastError();
|
|
if (udErr == ERROR_IO_PENDING && m_bCanDoAsync)
|
|
{
|
|
|
|
// If an async structure was passed in, we better have
|
|
// been able to finish in a single write operation.
|
|
// Otherwise, we are in deep trouble because we are not
|
|
// set up to do multiple async write operations within
|
|
// a single call.
|
|
|
|
if( bDoAsync)
|
|
{
|
|
pBufferObj->makePending();
|
|
bDidAsync = TRUE;
|
|
break;
|
|
}
|
|
|
|
if (!GetOverlappedResult( m_FileHandle, pOverlapped,
|
|
&uiBytesWritten, TRUE))
|
|
{
|
|
rc = MapWinErrorToFlaim( GetLastError(),
|
|
NE_FLM_WRITING_FILE);
|
|
goto Exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
rc = MapWinErrorToFlaim( udErr, NE_FLM_WRITING_FILE);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
if (uiBytesWritten < uiMaxBytesToWrite)
|
|
{
|
|
rc = RC_SET( NE_FLM_IO_DISK_FULL);
|
|
goto Exit;
|
|
}
|
|
|
|
uiBytesToWrite -= uiBytesBeingOutput;
|
|
|
|
if( puiBytesWrittenRV)
|
|
{
|
|
(*puiBytesWrittenRV) += uiBytesBeingOutput;
|
|
}
|
|
|
|
m_ui64CurrentPos = ui64WriteOffset + uiBytesBeingOutput;
|
|
|
|
if (!uiBytesToWrite)
|
|
{
|
|
break;
|
|
}
|
|
|
|
pucSrcBuffer += uiBytesBeingOutput;
|
|
ui64WriteOffset += uiBytesBeingOutput;
|
|
}
|
|
|
|
// See if we extended the file. If so, we may want to extend it some
|
|
// more and then also do a flush.
|
|
|
|
if (bExtendFile)
|
|
{
|
|
// NOTE: ui64LastWriteOffset + uiLastWrite is guaranteed to be
|
|
// on a sector boundary.
|
|
|
|
if (RC_BAD( rc = extendFile(
|
|
ui64LastWriteOffset + uiLastWriteSize,
|
|
m_uiExtendSize, TRUE)))
|
|
{
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
if( !bDidAsync && pBufferObj)
|
|
{
|
|
pBufferObj->notifyComplete( rc);
|
|
}
|
|
|
|
return( rc );
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Write to a file
|
|
****************************************************************************/
|
|
RCODE FLMAPI F_FileHdl::write(
|
|
FLMUINT64 ui64WriteOffset,
|
|
FLMUINT uiBytesToWrite,
|
|
const void * pvBuffer,
|
|
FLMUINT * puiBytesWrittenRV)
|
|
{
|
|
RCODE rc = NE_FLM_OK;
|
|
FLMUINT uiBytesWritten;
|
|
OVERLAPPED * pOverlapped;
|
|
DWORD udErr;
|
|
LARGE_INTEGER liTmp;
|
|
|
|
if (m_bDoDirectIO)
|
|
{
|
|
rc = directWrite( ui64WriteOffset, uiBytesToWrite, pvBuffer,
|
|
NULL, FALSE, FALSE, puiBytesWrittenRV);
|
|
goto Exit;
|
|
}
|
|
|
|
// If not doing direct IO, a single write call will do.
|
|
|
|
flmAssert( m_bFileOpened);
|
|
|
|
if( puiBytesWrittenRV)
|
|
{
|
|
*puiBytesWrittenRV = 0;
|
|
}
|
|
|
|
if( ui64WriteOffset == FLM_IO_CURRENT_POS)
|
|
{
|
|
ui64WriteOffset = m_ui64CurrentPos;
|
|
}
|
|
|
|
// Position the file.
|
|
|
|
if (!m_bCanDoAsync)
|
|
{
|
|
liTmp.QuadPart = ui64WriteOffset;
|
|
if( !SetFilePointerEx( m_FileHandle, liTmp, NULL, FILE_BEGIN))
|
|
{
|
|
rc = MapWinErrorToFlaim( GetLastError(),
|
|
NE_FLM_POSITIONING_IN_FILE);
|
|
goto Exit;
|
|
}
|
|
|
|
pOverlapped = NULL;
|
|
}
|
|
else
|
|
{
|
|
if (!m_Overlapped.hEvent)
|
|
{
|
|
if ((m_Overlapped.hEvent = CreateEvent( NULL, TRUE,
|
|
FALSE, NULL)) == NULL)
|
|
{
|
|
rc = MapWinErrorToFlaim( GetLastError(),
|
|
NE_FLM_SETTING_UP_FOR_WRITE);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
pOverlapped = &m_Overlapped;
|
|
pOverlapped->Offset = (DWORD)(ui64WriteOffset & 0xFFFFFFFF);
|
|
pOverlapped->OffsetHigh = (DWORD)(ui64WriteOffset >> 32);
|
|
|
|
if( !ResetEvent( pOverlapped->hEvent))
|
|
{
|
|
rc = MapWinErrorToFlaim( GetLastError(),
|
|
NE_FLM_SETTING_UP_FOR_WRITE);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
if (!WriteFile( m_FileHandle, (LPVOID)pvBuffer,
|
|
(DWORD)uiBytesToWrite, &uiBytesWritten,
|
|
pOverlapped))
|
|
{
|
|
udErr = GetLastError();
|
|
if (udErr == ERROR_IO_PENDING && m_bCanDoAsync)
|
|
{
|
|
if (!GetOverlappedResult( m_FileHandle, pOverlapped,
|
|
&uiBytesWritten, TRUE))
|
|
{
|
|
rc = MapWinErrorToFlaim( GetLastError(), NE_FLM_WRITING_FILE);
|
|
goto Exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
rc = MapWinErrorToFlaim( udErr, NE_FLM_WRITING_FILE);
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
if( puiBytesWrittenRV)
|
|
{
|
|
*puiBytesWrittenRV = uiBytesWritten;
|
|
}
|
|
|
|
m_ui64CurrentPos = ui64WriteOffset + uiBytesWritten;
|
|
|
|
if (uiBytesWritten < uiBytesToWrite)
|
|
{
|
|
rc = RC_SET( NE_FLM_IO_DISK_FULL);
|
|
goto Exit;
|
|
}
|
|
|
|
Exit:
|
|
return( rc );
|
|
}
|
|
|
|
/****************************************************************************
|
|
Desc: Deletes a file
|
|
****************************************************************************/
|
|
FSTATIC RCODE _DeleteFile(
|
|
char * pszPath)
|
|
{
|
|
RCODE rc = NE_FLM_OK;
|
|
|
|
if( DeleteFile( (LPTSTR)pszPath) == FALSE)
|
|
{
|
|
rc = MapWinErrorToFlaim( GetLastError(), NE_FLM_IO_DELETING_FILE);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
#endif // FLM_WIN
|
|
|
|
/****************************************************************************
|
|
Desc: Deletes a file
|
|
****************************************************************************/
|
|
#if defined( FLM_WATCOM_NLM)
|
|
int gv_ftkwinDummy(void)
|
|
{
|
|
return( 0);
|
|
}
|
|
#endif
|