tripwire-open-source/src/core/file_unix.cpp

414 lines
11 KiB
C++
Executable File

//
// The developer of the original code and/or files is Tripwire, Inc.
// Portions created by Tripwire, Inc. are copyright (C) 2000 Tripwire,
// Inc. Tripwire is a registered trademark of Tripwire, Inc. All rights
// reserved.
//
// This program is free software. The contents of this file are subject
// to the terms of the GNU General Public License as published by the
// Free Software Foundation; either version 2 of the License, or (at your
// option) any later version. You may redistribute it and/or modify it
// only in compliance with the GNU General Public License.
//
// This program is distributed in the hope that it will be useful.
// However, this program is distributed AS-IS WITHOUT ANY
// WARRANTY; INCLUDING THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS
// FOR A PARTICULAR PURPOSE. Please 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, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
// USA.
//
// Nothing in the GNU General Public License or any other license to use
// the code or files shall permit you to use Tripwire's trademarks,
// service marks, or other intellectual property without Tripwire's
// prior written consent.
//
// If you have any questions, please contact Tripwire, Inc. at either
// info@tripwire.org or www.tripwire.org.
//
// file_unix.cpp : Specific implementation of file operations for Unix.
#include "core/stdcore.h"
#if !IS_UNIX
#error Need to be unix to use unixfsservices
#endif
#include "core/file.h"
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include "core/debug.h"
#include "core/corestrings.h"
#include "core/fsservices.h"
#include "core/errorutil.h"
///////////////////////////////////////////////////////////////////////////////
// util_GetErrnoString -- return the result of strerror(errno) as a tstring
///////////////////////////////////////////////////////////////////////////////
static TSTRING util_GetErrnoString()
{
TSTRING ret;
char* pErrorStr = strerror(errno);
#ifdef _UNICODE
#error We dont currently support unicode on unix
#else
ret = pErrorStr;
#endif
return ret;
}
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
// cFile_i : Insulated implementation for cFile objects.
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
struct cFile_i
{
cFile_i();
~cFile_i();
FILE* mpCurrStream; //currently defined file stream
TSTRING mFileName; //the name of the file we are currently referencing.
};
//Ctor
cFile_i::cFile_i() :
mpCurrStream(NULL)
{}
//Dtor
cFile_i::~cFile_i()
{
if (mpCurrStream != NULL)
fclose( mpCurrStream );
mpCurrStream = NULL;
mFileName.empty();
}
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
// cFile () -- Implements file operations
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
cFile::cFile() :
mpData(NULL), isWritable(false)
{
mpData = new cFile_i;
}
cFile::~cFile()
{
if( mpData != NULL)
{
delete mpData;
mpData = NULL;
}
}
///////////////////////////////////////////////////////////////////////////////
// Open
///////////////////////////////////////////////////////////////////////////////
#ifndef __AROS
void cFile::Open( const TSTRING& sFileName, uint32 flags )
{
#else
void cFile::Open( const TSTRING& sFileNameC, uint32 flags )
{
TSTRING sFileName = cArosPath::AsNative(sFileNameC);
#endif
mode_t openmode = 0664;
if ( mpData->mpCurrStream != NULL )
Close();
//
// set up the sopen permissions
//
int perm = 0;
TSTRING mode;
if( flags & OPEN_WRITE )
{
perm |= O_RDWR;
isWritable = true;
mode = _T("rb");
if( flags & OPEN_TRUNCATE )
{
perm |= O_TRUNC;
perm |= O_CREAT;
mode = _T("w+b");
}
else
mode = _T("r+b");
}
else
{
perm |= O_RDONLY;
isWritable = false;
mode = _T("rb");
}
if ( flags & OPEN_EXCLUSIVE ) {
perm |= O_CREAT | O_EXCL;
openmode = (mode_t) 0600; // Make sure only root can read the file
}
if ( flags & OPEN_CREATE )
perm |= O_CREAT;
#ifdef O_NONBLOCK
if( flags & OPEN_NONBLOCKING )
perm |= O_NONBLOCK;
#endif
//
// actually open the file
//
int fh = _topen( sFileName.c_str(), perm, openmode );
if( fh == -1 )
{
throw( eFileOpen( sFileName, iFSServices::GetInstance()->GetErrString() ) );
}
#ifndef __AROS__
if( flags & OPEN_LOCKED_TEMP )
{
// unlink this file
if( 0 != unlink( sFileName.c_str() ) )
{
// we weren't able to unlink file, so close handle and fail
close( fh );
throw( eFileOpen( sFileName, iFSServices::GetInstance()->GetErrString() ) );
}
}
#endif
//
// turn the file handle into a FILE*
//
mpData->mpCurrStream = _tfdopen(fh, mode.c_str());
mpData->mFileName = sFileName; //Set mFileName to the newly opened file.
cFile::Rewind();
}
///////////////////////////////////////////////////////////////////////////
// Close -- Closes mpCurrStream and sets the pointer to NULL
///////////////////////////////////////////////////////////////////////////
void cFile::Close() //throw(eFile)
{
if(mpData->mpCurrStream != NULL)
{
fclose( mpData->mpCurrStream );
mpData->mpCurrStream = NULL;
}
mpData->mFileName.empty();
}
bool cFile::IsOpen( void ) const
{
return( mpData->mpCurrStream != NULL );
}
///////////////////////////////////////////////////////////////////////////
// Seek -- Positions the read/write offset in mpCurrStream. Returns the
// current offset upon completion. Returns 0 if no stream is defined.
///////////////////////////////////////////////////////////////////////////
cFile::File_t cFile::Seek( File_t offset, SeekFrom From) const //throw(eFile)
{
//Check to see if a file as been opened yet...
ASSERT( mpData->mpCurrStream != 0);
int apiFrom;
switch( From )
{
case cFile::SEEK_BEGIN:
apiFrom = SEEK_SET;
break;
case cFile::SEEK_CURRENT:
apiFrom = SEEK_CUR;
break;
case cFile::SEEK_EOF:
apiFrom = SEEK_END;
break;
default:
//An invalid SeekFrom parameter was passed.
throw( eInternal( _T("file_unix") ) );
}
// this is a hack to simulate running out of disk space
#if 0
static int blowupCount = 1;
if (++blowupCount == 1075)
{
fputs("***** faking seek failure!\n", stderr);
//throw eFileSeek();
throw std::bad_alloc();
}
fprintf(stderr, "%d\n", blowupCount);
#endif
if (fseeko( mpData->mpCurrStream, offset, apiFrom ) != 0)
{
#ifdef _DEBUG
cDebug d("cFile::Seek");
d.TraceDebug("Seek failed!\n");
#endif
throw eFileSeek();
}
return ftello(mpData->mpCurrStream);
}
///////////////////////////////////////////////////////////////////////////
// Read -- Returns the actual bytes read from mpCurrStream. Returns 0 if
// mpCurrStream is undefined.
///////////////////////////////////////////////////////////////////////////
cFile::File_t cFile::Read( void* buffer, File_t nBytes ) const //throw(eFile)
{
File_t iBytesRead;
// Has a file been opened?
ASSERT( mpData->mpCurrStream != NULL );
// Is the nBytes parameter 0? If so, return without touching buffer:
if( nBytes == 0 )
return 0;
iBytesRead = fread( buffer, sizeof(byte), nBytes, mpData->mpCurrStream );
if( ferror( mpData->mpCurrStream ) != 0 )
throw eFileRead( mpData->mFileName, iFSServices::GetInstance()->GetErrString() ) ;
else
return iBytesRead;
}
///////////////////////////////////////////////////////////////////////////
// Write -- Returns the actual number of bytes written to mpCurrStream
// Returns 0 if no file has been opened.
///////////////////////////////////////////////////////////////////////////
cFile::File_t cFile::Write( const void* buffer, File_t nBytes ) //throw(eFile)
{
File_t actual_count = 0;
// Has a file been opened? Is it writable?
ASSERT( mpData->mpCurrStream != NULL );
ASSERT( isWritable );
if( ( actual_count = fwrite( buffer, sizeof(byte), nBytes, mpData->mpCurrStream ) ) < nBytes )
throw eFileWrite( mpData->mFileName, iFSServices::GetInstance()->GetErrString() );
else
return actual_count;
}
///////////////////////////////////////////////////////////////////////////
// Tell -- Returns the current file offset. Returns 0 if no file has been
// opened.
///////////////////////////////////////////////////////////////////////////
cFile::File_t cFile::Tell() const
{
ASSERT( mpData->mpCurrStream != 0);
return ftell( mpData->mpCurrStream );
}
///////////////////////////////////////////////////////////////////////////
// Flush -- Flushes the current stream.
///////////////////////////////////////////////////////////////////////////
bool cFile::Flush() //throw(eFile)
{
if ( mpData->mpCurrStream == NULL )
throw eFileFlush( mpData->mFileName, iFSServices::GetInstance()->GetErrString() );
return ( fflush( mpData->mpCurrStream) == 0 );
}
///////////////////////////////////////////////////////////////////////////
// Rewind -- Sets the offset to the beginning of the file. If mpCurrStream
// is NULL, this method returns false. If the rewind operation fails,
// an exception is thrown.
///////////////////////////////////////////////////////////////////////////
void cFile::Rewind() const //throw(eFile)
{
ASSERT( mpData->mpCurrStream != 0);
rewind( mpData->mpCurrStream );
if( ftell( mpData->mpCurrStream ) != 0 )
throw( eFileRewind( mpData->mFileName, iFSServices::GetInstance()->GetErrString() ) );
}
///////////////////////////////////////////////////////////////////////////
// GetSize -- Returns the size of the current stream, if one has been
// opened. If no stream has been opened, returns -1.
///////////////////////////////////////////////////////////////////////////
cFile::File_t cFile::GetSize() const
{
File_t vCurrentOffset = Tell(); //for saving the current offset
File_t ret;
//Has a file been opened? If not, return -1
if( mpData->mpCurrStream == NULL )
return -1;
ret = Seek( 0, cFile::SEEK_EOF );
Seek( vCurrentOffset, cFile::SEEK_BEGIN );
//return the offset to it's position prior to GetSize call.
return ret;
}
/////////////////////////////////////////////////////////////////////////
// Truncate
/////////////////////////////////////////////////////////////////////////
void cFile::Truncate( File_t offset ) // throw(eFile)
{
ASSERT( mpData->mpCurrStream != 0);
ASSERT( isWritable );
ftruncate( fileno(mpData->mpCurrStream), offset );
if( GetSize() != offset )
throw( eFileTrunc( mpData->mFileName, iFSServices::GetInstance()->GetErrString() ) );
}
#ifdef __AROS__
TSTRING cArosPath::AsPosix( const TSTRING& in )
{
if (in[0] == '/')
return in;
TSTRING out = '/' + in;
std::replace(out.begin(), out.end(), ':', '/');
return out;
}
TSTRING cArosPath::AsNative( const TSTRING& in )
{
if (in[0] != '/')
return in;
int x;
for (x=1; in[x] == '/' && x<in.length(); x++);
TSTRING out = in.substr(x);
TSTRING::size_type t = out.find_first_of('/');
out[t] = ':';
return out;
}
#endif