📄 file.c
字号:
/****************************************************************************
* *
* File Stream I/O Functions *
* Copyright Peter Gutmann 1993-2005 *
* *
****************************************************************************/
#if defined( __UNIX__ ) && defined( __linux__ )
/* In order for the fileReadonly() check to work we need to be able to
check errno, however for this to work the headers that specify that
threading is being used must be the first headers included
(specifically, the include order has to be pthread.h, unistd.h,
everything else) or errno.h, which is pulled in by stdlib.h, gets
set up as an extern int rather than a function */
#include "crypt.h"
#endif /* Older Linux broken include-file dependencies */
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#if defined( INC_ALL )
#include "stream.h"
#include "file.h"
#elif defined( INC_CHILD )
#include "stream.h"
#include "file.h"
#else
#include "io/stream.h"
#include "io/file.h"
#endif /* Compiler-specific includes */
/* In order to get enhanced control over things like file security and
buffering we can't use stdio but have to rely on using OS-level file
routines, which is essential for working with things like ACL's for
sensitive files and forcing disk writes for files we want to erase.
Without the forced disk write the data in the cache doesn't get flushed
before the file delete request arrives, after which it's discarded rather
than being written, so the file never gets overwritten. In addition some
embedded environments don't support stdio so we have to supply our own
alternatives.
When implementing the following for new systems there are certain things
that you need to ensure to guarantee error-free operation:
- File permissions should be set as indicated by the file open flags.
- File sharing controls (shared vs. exclusive access locks) should be
implemented.
- If the file is locked for exclusive access, the open call should either
block until the lock is released (they're never held for more than a
fraction of a second) or return CRYPT_ERROR_TIMEOUT depending on how
the OS handles locks.
When erasing data, we may run into problems on embedded systems using
solid-state storage that implements wear-levelling by using a log-
structured filesystem (LFS) type arrangement. These work by never
writing a sector twice but always appending newly-written data at the
next free location until the volume is full, at which point a garbage
collector runs to reclaim. A main goal of LFS's is speed (data is
written in large sequential writes rather than lots of small random
writes) and error-recovery by taking advantage of the characteristics
of the log structure, however a side-effect of the write mechanism is
that it makes wear-levelling management quite simple. However, the use
of a LFS also makes it impossible to reliably overwrite data, since
new writes never touch the existing data. There's no easy way to cope
with this since we have no way of telling what the underlying media is
doing with our data. A mediating factor though is that embedded systems
are usually sealed, single-use systems where the chances of a second user
accessing the data is low. The only possible threat then is post system-
retirement recovery of the data, presumably if it contains valuable data
it'll be disposed of appropriately */
/* Symbolic defines for stdio-style file access modes */
#if defined( DDNAME_IO )
#define MODE_READ "rb,byteseek"
#define MODE_WRITE "wb,byteseek,recfm=*"
#define MODE_READWRITE "rb+,byteseek,recfm=*"
#else
#define MODE_READ "rb"
#define MODE_WRITE "wb"
#define MODE_READWRITE "rb+"
#endif /* Standard vs. DDNAME I/O */
/****************************************************************************
* *
* AMX File Stream Functions *
* *
****************************************************************************/
#if defined( __AMX__ )
/* Open/close a file stream */
int sFileOpen( STREAM *stream, const char *fileName, const int mode )
{
static const int modes[] = {
FJ_O_RDONLY, FJ_O_RDONLY,
FJ_O_WRONLY | FJ_O_CREAT | FJ_O_NOSHAREANY,
FJ_O_RDWR | FJ_O_NOSHAREWR
};
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( fileName != NULL );
assert( mode != 0 );
/* Initialise the stream structure */
memset( stream, 0, sizeof( STREAM ) );
stream->type = STREAM_TYPE_FILE;
if( ( mode & FILE_RW_MASK ) == FILE_READ )
stream->flags = STREAM_FLAG_READONLY;
openMode = modes[ mode & FILE_RW_MASK ];
/* If we're trying to write to the file, check whether we've got
permission to do so */
if( ( mode & FILE_WRITE ) && fileReadonly( fileName ) )
return( CRYPT_ERROR_PERMISSION );
/* Try and open the file */
stream->fd = fjopen( fileName, openMode, ( openMode & FJ_O_CREAT ) ? \
FJ_S_IREAD | FJ_S_IWRITE : 0 );
if( stream->fd < 0 )
{
const int errNo = fjfserrno();
return( ( errNo == FJ_EACCES || errNo == FJ_ESHARE ) ? \
CRYPT_ERROR_PERMISSION : \
( errNo == FJ_ENOENT ) ? \
CRYPT_ERROR_NOTFOUND : CRYPT_ERROR_OPEN );
}
return( CRYPT_OK );
}
int sFileClose( STREAM *stream )
{
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( stream->type == STREAM_TYPE_FILE );
/* Close the file and clear the stream structure */
fjclose( stream->fd );
zeroise( stream, sizeof( STREAM ) );
return( CRYPT_OK );
}
/* Read/write a block of data from/to a file stream */
int fileRead( STREAM *stream, void *buffer, const int length )
{
int bytesRead;
if( ( bytesRead = fjread( stream->fd, buffer, length ) ) < 0 )
return( CRYPT_ERROR_READ );
return( bytesRead );
}
int fileWrite( STREAM *stream, const void *buffer, const int length )
{
int bytesWritten;
if( ( bytesWritten = fjwrite( stream->fd, buffer, length ) ) < 0 || \
bytesWritten != length )
return( CRYPT_ERROR_WRITE );
return( CRYPT_OK );
}
/* Commit data in a file stream to backing storage */
int fileFlush( STREAM *stream )
{
fjflush( stream->fd );
}
/* Change the read/write position in a file */
int fileSeek( STREAM *stream, const long position )
{
if( fjlseek( stream->fd, position, FJ_SEEK_SET ) < 0 )
return( CRYPT_ERROR_WRITE );
return( CRYPT_OK );
}
/* Check whether a file is writeable */
BOOLEAN fileReadonly( const char *fileName )
{
struct fjxstat fileInfo;
assert( fileName != NULL );
if( fjstat( fileName, &fileInfo ) < 0 )
return( TRUE );
return( ( fileInfo->??? ) ? TRUE : FALSE );
}
/* File deletion functions: Wipe a file from the current position to EOF,
and wipe and delete a file (although it's not terribly rigorous).
Vestigia nulla retrorsum */
static void eraseFile( const STREAM *stream, long position, long length )
{
/* Wipe everything past the current position in the file */
while( length > 0 )
{
RESOURCE_DATA msgData;
BYTE buffer[ BUFSIZ * 2 ];
int bytesToWrite = min( length, BUFSIZ * 2 );
/* We need to make sure that we fill the buffer with random data for
each write, otherwise compressing filesystems will just compress
it to nothing */
setMessageData( &msgData, buffer, bytesToWrite );
krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_GETATTRIBUTE_S,
&msgData, CRYPT_IATTRIBUTE_RANDOM_NONCE );
if( fjwrite( stream->fd, buffer, bytesToWrite ) < 0 )
break; /* An error occurred while writing, exit */
length -= bytesToWrite;
}
fjchsize( stream->fd, position );
}
void fileClearToEOF( const STREAM *stream )
{
struct fjxstat fileInfo;
int length, position;
assert( isReadPtr( stream, sizeof( STREAM ) ) );
assert( stream->type == STREAM_TYPE_FILE );
/* Wipe everything past the current position in the file */
if( fjstat( fileName, &fileInfo ) < 0 )
return;
length = fileInfo.???;
if( ( position = fjtell( stream->fd ) ) < 0 )
return;
length -= position;
if( length <= 0 )
return; /* Nothing to do, exit */
eraseFile( stream, position, length );
}
void fileErase( const char *fileName )
{
STREAM stream;
struct fjxstat fileInfo;
int status;
assert( fileName != NULL );
/* Try and open the file so that we can erase it. If this fails, the
best that we can do is a straight unlink */
status = sFileOpen( &stream, fileName,
FILE_READ | FILE_WRITE | FILE_EXCLUSIVE_ACCESS );
if( cryptStatusError( status ) )
{
remove( fileName );
return;
}
/* Determine the size of the file and erase it */
fjstat( fileName, &fileInfo );
eraseFile( &stream, 0, fileInfo.??? );
/* Reset the file's attributes */
fjfattr( stream.fd, FJ_DA_NORMAL );
/* Delete the file */
sFileClose( &stream );
fjunlink( fileName );
}
/* Build the path to a file in the cryptlib directory */
void fileBuildCryptlibPath( char *path, const char *fileName,
const BUILDPATH_OPTION_TYPE option )
{
/* Make sure that the open fails if we can't build the path */
*path = '\0';
/* Build the path to the configuration file if necessary. We assume that
we're on the correct drive */
strcpy( path, "\\cryptlib\\" );
/* If we're being asked to create the cryptlib directory and it doesn't
already exist, create it now */
if( option == BUILDPATH_CREATEPATH && fjisdir( path ) == 0 )
{
/* The directory doesn't exist, try and create it */
if( fjmkdir( path ) < 0 )
{
*path = '\0';
return;
}
}
/* Add the filename to the path */
if( option == BUILDPATH_RNDSEEDFILE )
strcat( path, "randseed.dat" );
else
{
strcat( path, fileName );
strcat( path, ".p15" );
}
}
/****************************************************************************
* *
* uC/OS-II File Stream Functions *
* *
****************************************************************************/
#elif defined( __UCOSII__ )
/* Open/close a file stream */
int sFileOpen( STREAM *stream, const char *fileName, const int mode )
{
static const char *modes[] = { MODE_READ, MODE_READ,
MODE_WRITE, MODE_READWRITE };
const char *openMode;
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( fileName != NULL );
assert( mode != 0 );
/* Initialise the stream structure */
memset( stream, 0, sizeof( STREAM ) );
stream->type = STREAM_TYPE_FILE;
if( ( mode & FILE_RW_MASK ) == FILE_READ )
stream->flags = STREAM_FLAG_READONLY;
openMode = modes[ mode & FILE_RW_MASK ];
/* If we're trying to write to the file, check whether we've got
permission to do so */
if( ( mode & FILE_WRITE ) && fileReadonly( fileName ) )
return( CRYPT_ERROR_PERMISSION );
/* Try and open the file */
stream->pFile = FS_FOpen( fileName, openMode );
if( stream->pFile == NULL )
{
const FS_i16 errNo = FS_FError();
/* Return what we can in the way of an error message. Curiously
uC/FS doesn't provide an indicator for common errors like file
not found, although it does provide strange indicators like
FS_ERR_CLOSE, an error occurred while calling FS_FClose() */
return( ( errNo == FS_ERR_DISKFULL ) ? \
CRYPT_ERROR_OVEWFLOW : \
( errNo == FS_ERR_READONLY ) ? \
CRYPT_ERROR_PERMISSION : CRYPT_ERROR_OPEN );
}
return( CRYPT_OK );
}
int sFileClose( STREAM *stream )
{
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( stream->type == STREAM_TYPE_FILE );
/* Close the file and clear the stream structure */
FS_FClose( stream->pFile );
zeroise( stream, sizeof( STREAM ) );
return( CRYPT_OK );
}
/* Read/write a block of data from/to a file stream */
int fileRead( STREAM *stream, void *buffer, const int length )
{
int bytesRead;
if( ( bytesRead = FS_Read( stream->pFile, buffer, length ) ) < 0 )
return( CRYPT_ERROR_READ );
return( bytesRead );
}
int fileWrite( STREAM *stream, const void *buffer, const int length )
{
int bytesWritten;
if( ( bytesWritten = FS_Write( stream->pFile, buffer, length ) ) < 0 || \
bytesWritten != length )
return( CRYPT_ERROR_WRITE );
return( CRYPT_OK );
}
/* Commit data in a file stream to backing storage */
int fileFlush( STREAM *stream )
{
/* There is an IOCTL to flush all buffers (for all files) to the backing
store, but it's no supported in all drivers and seems a bit excessive
for this case */
return( CRYPT_OK );
}
/* Change the read/write position in a file */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -