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