686 lines
17 KiB
C++
686 lines
17 KiB
C++
|
|
// The developer of the original code and/or files is Tripwire, Inc.
|
|
// Portions created by Tripwire, Inc. are copyright (C) 2000-2018 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.
|
|
|
|
|
|
/* On GNU/Hurd, need to define _GNU_SOURCE in order to use O_NOATIME
|
|
which technically is still a nonstandard extension to open() */
|
|
#if IS_HURD
|
|
# define _GNU_SOURCE
|
|
#endif
|
|
|
|
#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>
|
|
|
|
#if HAVE_SYS_FS_VX_IOCTL_H
|
|
#include <sys/fs/vx_ioctl.h>
|
|
#endif
|
|
|
|
#include "core/debug.h"
|
|
#include "core/corestrings.h"
|
|
#include "core/fsservices.h"
|
|
#include "core/errorutil.h"
|
|
|
|
#if IS_RISCOS
|
|
#include <unixlib/local.h>
|
|
#endif
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// cFile_i : Insulated implementation for cFile objects.
|
|
///////////////////////////////////////////////////////////////////////////
|
|
///////////////////////////////////////////////////////////////////////////
|
|
struct cFile_i
|
|
{
|
|
cFile_i();
|
|
~cFile_i();
|
|
|
|
int m_fd; //underlying file descriptor
|
|
FILE* mpCurrStream; //currently defined file stream
|
|
TSTRING mFileName; //the name of the file we are currently referencing.
|
|
uint32_t mFlags; //Flags used to open the file
|
|
};
|
|
|
|
//Ctor
|
|
cFile_i::cFile_i() : m_fd(-1), mpCurrStream(NULL), mFlags(0)
|
|
{
|
|
}
|
|
|
|
//Dtor
|
|
cFile_i::~cFile_i()
|
|
{
|
|
if (mpCurrStream != NULL)
|
|
{
|
|
fclose(mpCurrStream);
|
|
mpCurrStream = NULL;
|
|
|
|
#if !CAN_UNLINK_WHILE_OPEN // so unlink after close instead
|
|
if (mFlags & cFile::OPEN_LOCKED_TEMP)
|
|
{
|
|
// unlink this file
|
|
if (0 != unlink(mFileName.c_str()))
|
|
{
|
|
throw(eFileOpen(mFileName, iFSServices::GetInstance()->GetErrString()));
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// cFile () -- Implements file operations
|
|
///////////////////////////////////////////////////////////////////////////
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
cFile::cFile() : mpData(NULL), isWritable(false)
|
|
{
|
|
mpData = new cFile_i;
|
|
}
|
|
|
|
cFile::~cFile()
|
|
{
|
|
if (mpData != NULL)
|
|
{
|
|
delete mpData;
|
|
mpData = NULL;
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Open
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
#if !USES_DEVICE_PATH
|
|
void cFile::Open(const TSTRING& sFileName, uint32_t flags)
|
|
{
|
|
#else
|
|
void cFile::Open(const TSTRING& sFileNameC, uint32_t flags)
|
|
{
|
|
TSTRING sFileName = cDevicePath::AsNative(sFileNameC);
|
|
#endif
|
|
mode_t openmode = 0664;
|
|
if (mpData->mpCurrStream != NULL)
|
|
Close();
|
|
|
|
mpData->mFlags = flags;
|
|
|
|
//
|
|
// set up the open 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_SCANNING)
|
|
perm |= O_NONBLOCK;
|
|
#endif
|
|
|
|
#ifdef O_NOATIME
|
|
if (flags & OPEN_SCANNING)
|
|
perm |= O_NOATIME;
|
|
#endif
|
|
|
|
#ifdef O_DIRECT
|
|
//Only use O_DIRECT for scanning, since cfg/policy/report reads
|
|
// don't happen w/ a nice round block size.
|
|
if ((flags & OPEN_DIRECT) && (flags & OPEN_SCANNING))
|
|
perm |= O_DIRECT;
|
|
#endif
|
|
|
|
//
|
|
// actually open the file
|
|
//
|
|
int fh = _topen(sFileName.c_str(), perm, openmode);
|
|
if (fh == -1)
|
|
{
|
|
throw(eFileOpen(sFileName, iFSServices::GetInstance()->GetErrString()));
|
|
}
|
|
mpData->m_fd = fh;
|
|
|
|
#if CAN_UNLINK_WHILE_OPEN
|
|
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();
|
|
|
|
#ifdef F_NOCACHE //OSX
|
|
if ((flags & OPEN_DIRECT) && (flags & OPEN_SCANNING))
|
|
fcntl(fh, F_NOCACHE, 1);
|
|
#endif
|
|
|
|
#if IS_SOLARIS
|
|
if ((flags & OPEN_DIRECT) && (flags & OPEN_SCANNING))
|
|
directio(fh, DIRECTIO_ON);
|
|
#endif
|
|
|
|
#if HAVE_POSIX_FADVISE
|
|
if (flags & OPEN_SCANNING && !(flags & OPEN_DIRECT))
|
|
{
|
|
posix_fadvise(fh, 0, 0, POSIX_FADV_SEQUENTIAL);
|
|
posix_fadvise(fh, 0, 0, POSIX_FADV_NOREUSE);
|
|
}
|
|
|
|
#elif HAVE_SYS_FS_VX_IOCTL_H
|
|
if (flags & OPEN_SCANNING)
|
|
{
|
|
if (flags & OPEN_DIRECT)
|
|
ioctl(fh, VX_SETCACHE, VX_DIRECT);
|
|
else
|
|
ioctl(fh, VX_SETCACHE, VX_SEQ | VX_NOREUSE);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// Close -- Closes mpCurrStream and sets the pointer to NULL
|
|
///////////////////////////////////////////////////////////////////////////
|
|
void cFile::Close() //throw(eFile)
|
|
{
|
|
if (mpData->mpCurrStream != NULL)
|
|
{
|
|
#ifdef HAVE_POSIX_FADVISE
|
|
posix_fadvise(fileno(mpData->mpCurrStream), 0, 0, POSIX_FADV_DONTNEED);
|
|
#endif
|
|
|
|
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;
|
|
|
|
if (mpData->mFlags & OPEN_DIRECT)
|
|
{
|
|
iBytesRead = read(mpData->m_fd, buffer, nBytes);
|
|
if (iBytesRead < 0)
|
|
{
|
|
throw eFileRead(mpData->mFileName, iFSServices::GetInstance()->GetErrString());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
iBytesRead = fread(buffer, sizeof(uint8_t), nBytes, mpData->mpCurrStream);
|
|
if (ferror(mpData->mpCurrStream) != 0)
|
|
{
|
|
throw eFileRead(mpData->mFileName, iFSServices::GetInstance()->GetErrString());
|
|
}
|
|
}
|
|
|
|
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(uint8_t), 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()));
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
// Platform path conversion methods
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
bool cDosPath::IsAbsolutePath(const TSTRING& in)
|
|
{
|
|
if (in.empty())
|
|
return false;
|
|
|
|
if (in[0] == '/')
|
|
return true;
|
|
|
|
if (in.length() >= 2 && in[1] == ':')
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
// For paths of type C:\DOS
|
|
TSTRING cDosPath::AsPosix(const TSTRING& in)
|
|
{
|
|
if (in[0] == '/')
|
|
{
|
|
return in;
|
|
}
|
|
|
|
TSTRING out = (cDosPath::IsAbsolutePath(in)) ? ("/dev/" + in) : in;
|
|
std::replace(out.begin(), out.end(), '\\', '/');
|
|
out.erase(std::remove(out.begin(), out.end(), ':'), out.end());
|
|
|
|
return out;
|
|
}
|
|
|
|
TSTRING cDosPath::AsNative(const TSTRING& in)
|
|
{
|
|
if (in[0] != '/')
|
|
{
|
|
return in;
|
|
}
|
|
|
|
if (in.find("/dev") != 0 || in.length() < 6)
|
|
return in;
|
|
|
|
TSTRING out = "?:/";
|
|
out[0] = in[5];
|
|
|
|
if (in.length() >= 8)
|
|
out.append(in.substr(7));
|
|
|
|
std::replace(out.begin(), out.end(), '/', '\\');
|
|
|
|
return out;
|
|
}
|
|
|
|
TSTRING cDosPath::BackupName(const TSTRING& in)
|
|
{
|
|
TSTRING out = in;
|
|
std::string::size_type pos = out.find_last_of("\\");
|
|
if (std::string::npos == pos)
|
|
return in;
|
|
|
|
TSTRING path = in.substr(0, pos);
|
|
TSTRING name = in.substr(pos, 9);
|
|
std::replace(name.begin(), name.end(), '.', '_');
|
|
path.append(name);
|
|
|
|
return path;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
bool cArosPath::IsAbsolutePath(const TSTRING& in)
|
|
{
|
|
if (in.empty())
|
|
return false;
|
|
|
|
if (in[0] == '/')
|
|
return true;
|
|
|
|
if (in.find(":") != std::string::npos)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
// For paths of type DH0:dir/file
|
|
TSTRING cArosPath::AsPosix(const TSTRING& in)
|
|
{
|
|
if (in[0] == '/')
|
|
{
|
|
return in;
|
|
}
|
|
|
|
TSTRING out = IsAbsolutePath(in) ? '/' + in : in;
|
|
std::replace(out.begin(), out.end(), ':', '/');
|
|
|
|
return out;
|
|
}
|
|
|
|
TSTRING cArosPath::AsNative(const TSTRING& in)
|
|
{
|
|
if (in[0] != '/')
|
|
{
|
|
return in;
|
|
}
|
|
|
|
std::string::size_type drive = in.find_first_not_of("/");
|
|
TSTRING out = (drive != std::string::npos) ? in.substr(drive) : in;
|
|
TSTRING::size_type t = out.find_first_of('/');
|
|
if (t != std::string::npos)
|
|
out[t] = ':';
|
|
else
|
|
out.append(":");
|
|
|
|
return out;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
bool cRiscosPath::IsAbsolutePath(const TSTRING& in)
|
|
{
|
|
if (in.empty())
|
|
return false;
|
|
|
|
if (in[0] == '/')
|
|
return true;
|
|
|
|
if (in.find("$") != std::string::npos)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
// For paths of type SDFS::Volume.$.dir.file
|
|
TSTRING cRiscosPath::AsPosix(const TSTRING& in)
|
|
{
|
|
#if IS_RISCOS
|
|
if (in[0] == '/')
|
|
{
|
|
return in;
|
|
}
|
|
|
|
TSTRING out;
|
|
char* unixified = __unixify(in.c_str(), 0, 0, 0, 0);
|
|
if (unixified)
|
|
{
|
|
out.assign(unixified);
|
|
free(unixified);
|
|
return out;
|
|
}
|
|
return in;
|
|
|
|
#else
|
|
return in;
|
|
#endif
|
|
}
|
|
|
|
TSTRING cRiscosPath::AsNative(const TSTRING& in)
|
|
{
|
|
#if IS_RISCOS
|
|
if (in[0] != '/')
|
|
{
|
|
return in;
|
|
}
|
|
|
|
TSTRING out;
|
|
int buf_size = in.length() + 100; // examples pad by 100
|
|
std::vector<char> buf(buf_size);
|
|
__riscosify(in.c_str(), 0, 0, &buf[0], buf_size, 0);
|
|
if (buf[0])
|
|
{
|
|
out.assign(&buf[0]);
|
|
return out;
|
|
}
|
|
return in;
|
|
#else
|
|
return in;
|
|
#endif
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
bool cRedoxPath::IsAbsolutePath(const TSTRING& in)
|
|
{
|
|
if (in.empty())
|
|
return false;
|
|
|
|
if (in[0] == '/')
|
|
return true;
|
|
|
|
if (in.find(":") != std::string::npos)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
// For paths of type file:/dir/file
|
|
TSTRING cRedoxPath::AsPosix(const TSTRING& in)
|
|
{
|
|
if (in[0] == '/')
|
|
{
|
|
return in;
|
|
}
|
|
|
|
TSTRING out = IsAbsolutePath(in) ? '/' + in : in;
|
|
std::string::size_type colon = out.find_first_of(":");
|
|
if (colon != std::string::npos)
|
|
out.erase(colon, 1);
|
|
return out;
|
|
}
|
|
|
|
TSTRING cRedoxPath::AsNative(const TSTRING& in)
|
|
{
|
|
if (in[0] != '/')
|
|
{
|
|
return in;
|
|
}
|
|
|
|
std::string::size_type drive = in.find_first_not_of("/");
|
|
TSTRING out = (drive != std::string::npos) ? in.substr(drive) : in;
|
|
TSTRING::size_type slash = out.find_first_of('/');
|
|
if (slash != std::string::npos)
|
|
out.insert(slash, ":");
|
|
else
|
|
out.append(":/");
|
|
|
|
return out;
|
|
}
|