⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 file.c

📁 cryptlib是功能强大的安全工具集。允许开发人员快速在自己的软件中集成加密和认证服务。
💻 C
📖 第 1 页 / 共 5 页
字号:
/****************************************************************************
*																			*
*							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 + -