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

📄 stream.c

📁 cryptlib安全工具包
💻 C
📖 第 1 页 / 共 4 页
字号:
/****************************************************************************
*																			*
*							Stream I/O Functions							*
*						Copyright Peter Gutmann 1993-2007					*
*																			*
****************************************************************************/

#include <stdio.h>
#include <stdarg.h>
#if defined( INC_ALL )
  #include "stream_int.h"
#else
  #include "io/stream_int.h"
#endif /* Compiler-specific includes */

/* Prototypes for functions in file.c */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4 ) ) \
int fileRead( STREAM *stream, 
			  OUT_BUFFER( length, *bytesRead ) void *buffer, 
			  IN_LENGTH const int length, 
			  OUT_LENGTH_Z int *bytesRead );
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
int fileWrite( STREAM *stream, 
			   IN_BUFFER( length ) const void *buffer, 
			   IN_LENGTH const int length );
CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
int fileFlush( STREAM *stream );
CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
int fileSeek( STREAM *stream, IN_LENGTH_Z const long position );

/****************************************************************************
*																			*
*								Utility Functions							*
*																			*
****************************************************************************/

/* Sanity-check the stream state */

CHECK_RETVAL_BOOL STDC_NONNULL_ARG( ( 1 ) ) \
static BOOLEAN sanityCheck( const STREAM *stream )
	{
	assert( isReadPtr( stream, sizeof( STREAM ) ) );

	/* Perform stream type-specific checks */
	switch( stream->type )
		{
		case STREAM_TYPE_NULL:
			if( stream->flags )
				return( FALSE );
			break;

		case STREAM_TYPE_MEMORY:
			if( stream->flags & ~STREAM_MFLAG_MASK )
				return( FALSE );
			break;

		case STREAM_TYPE_FILE:
			if( stream->flags & ~STREAM_FFLAG_MASK )
				return( FALSE );
			break;

		case STREAM_TYPE_NETWORK:
			{
			NET_STREAM_INFO *netStream = \
					( NET_STREAM_INFO * ) stream->netStreamInfo;

			if( netStream->timeout < 0 || netStream->timeout > 300 )
				return( FALSE );
			break;
			}

		default:
			return( FALSE );
		}

	/* Null streams have no internal buffer so the buffer position 
	   indicators aren't used */
	if( stream->type == STREAM_TYPE_NULL )
		{
		/* Null streams, which act as data sinks, have a virtual content-
		   length indicator so although the buffer size is zero the buffer 
		   position values can be nonzero to indicate how much (virtual)
		   data they've absorbed */
		if( stream->buffer != NULL || stream->bufSize != 0 )
			return( FALSE );
		if( stream->bufPos < 0 || stream->bufPos > stream->bufEnd || 
			stream->bufEnd < 0 || stream->bufEnd >= MAX_INTLENGTH )
			return( FALSE );

		return( TRUE );
		}

	/* Network streams may be buffered, but if they're not then the internal
	   buffer indicators aren't used */
	if( stream->type == STREAM_TYPE_NETWORK )
		{
		NET_STREAM_INFO *netStream = \
				( NET_STREAM_INFO * ) stream->netStreamInfo;

		/* If it's an unbuffered network stream then all buffer values must 
		   be zero */
		if( stream->buffer == NULL )
			{
			if( stream->bufPos != 0 || stream->bufSize != 0 || \
				stream->bufEnd != 0 )
				return( FALSE );
			if( netStream->writeBuffer != NULL || \
				netStream->writeBufSize != 0 || \
				netStream->writeBufEnd != 0 )
				return( FALSE );

			return( TRUE );
			}

		/* Network streams have a second buffer used for writes, make sure 
		   that the write buffer position is within bounds */
		if( netStream->writeBuffer == NULL || \
			netStream->writeBufSize <= 0 || \
			netStream->writeBufSize >= MAX_INTLENGTH )
			return( FALSE );
		if( netStream->writeBufEnd < 0 || \
			netStream->writeBufEnd > netStream->writeBufSize )
			return( FALSE );

		return( TRUE );
		}

	/* Everything else requires a buffer, however file streams have to be 
	   explicitly connected to a buffer after creation so if it's a 
	   partially-initialised file stream we allow an absent buffer */
	if( stream->buffer == NULL )
		{
		if( stream->type == STREAM_TYPE_FILE && \
			!( stream->flags & STREAM_FFLAG_BUFFERSET ) && \
			stream->bufPos == 0 && stream->bufEnd == 0 && \
			stream->bufSize == 0 )
			return( TRUE );
		
		return( FALSE );
		}

	/* Make sure that the buffer position is within bounds:

								 bufSize
									|
			<------ buffer ------>	v
		+---------------------------+
		|						|	|
		+---------------------------+
				^				^
				|				|
			 bufPos			 bufEnd */
	if( stream->bufPos < 0 || stream->bufPos > stream->bufEnd || \
		stream->bufEnd < 0 || stream->bufEnd > stream->bufSize || \
		stream->bufSize <= 0 || stream->bufSize >= MAX_INTLENGTH )
		return( FALSE );
	 
	/* If it's a file stream make sure that the position within the file
	   makes sense */
	if( stream->type == STREAM_TYPE_FILE && \
		( stream->bufCount < 0 || \
		  stream->bufCount >= ( MAX_INTLENGTH / stream->bufSize ) ) )
		return( FALSE );

	return( TRUE );
	}

/* Refill a stream buffer from backing storage */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int refillStream( INOUT STREAM *stream )
	{
	int length, status;

	assert( isWritePtr( stream, sizeof( STREAM ) ) );

	REQUIRES_S( sanityCheck( stream ) );
	REQUIRES_S( stream->type == STREAM_TYPE_FILE );
	REQUIRES_S( stream->bufPos >= stream->bufEnd || \
				( stream->flags & STREAM_FFLAG_POSCHANGED ) );

	/* If we've reached EOF we can't refill the stream */
	if( stream->flags & STREAM_FFLAG_EOF )
		{
		/* If partial reads are allowed return an indication of how much 
		   data we got.  This only works once, after this the persistent 
		   error state will return an underflow error before we get to this
		   point */
		stream->status = CRYPT_ERROR_UNDERFLOW;
		return( ( stream->flags & STREAM_FLAG_PARTIALREAD ) ? \
				OK_SPECIAL : CRYPT_ERROR_UNDERFLOW );
		}

	/* If we've moved to a different place in the file prepare to get new 
	   data into the buffer at the new location */
	if( ( stream->flags & STREAM_FFLAG_POSCHANGED ) && \
		!( stream->flags & STREAM_FFLAG_POSCHANGED_NOSKIP ) )
		{
		status = fileSeek( stream, stream->bufCount * stream->bufSize );
		if( cryptStatusError( status ) )
			return( sSetError( stream, status ) );
		}

	/* Try and read more data into the stream buffer */
	status = fileRead( stream, stream->buffer, stream->bufSize, &length );
	if( cryptStatusError( status ) )
		return( sSetError( stream, status ) );
	if( length < stream->bufSize )
		{
		/* If we got less than we asked for, remember that we're at the end
		   of the file */
		stream->flags |= STREAM_FFLAG_EOF;
		if( length == 0 )
			{
			/* We ran out of input on an exact buffer boundary, if partial 
			   reads are allowed return an indication of how much data we 
			   got.  This only works once, after this the persistent error 
			   state will return an underflow error before we get to this 
			   point */
			stream->status = CRYPT_ERROR_UNDERFLOW;
			return( ( stream->flags & STREAM_FLAG_PARTIALREAD ) ? \
					OK_SPECIAL : CRYPT_ERROR_UNDERFLOW );
			}
		}

	/* We've refilled the stream buffer from the file, remember the 
	   details */
	if( !( stream->flags & STREAM_FFLAG_POSCHANGED ) )
		{
		stream->bufCount++;
		stream->bufPos = 0;
		}
	stream->bufEnd = length;
	stream->flags &= ~( STREAM_FFLAG_POSCHANGED | \
						STREAM_FFLAG_POSCHANGED_NOSKIP );

	ENSURES_S( sanityCheck( stream ) );

	return( CRYPT_OK );
	}

/* Empty a stream buffer to backing storage */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int emptyStream( INOUT STREAM *stream, const BOOLEAN forcedFlush )
	{
	int status = CRYPT_OK;

	assert( isWritePtr( stream, sizeof( STREAM ) ) );

	REQUIRES_S( sanityCheck( stream ) );
	REQUIRES_S( stream->type == STREAM_TYPE_FILE );

	/* If the stream position has been changed, this can only have been from 
	   a rewind of the stream, in which case we move back to the start of 
	   the file */
	if( stream->flags & STREAM_FFLAG_POSCHANGED )
		{
		status = fileSeek( stream, 0 );
		if( cryptStatusError( status ) )
			return( sSetError( stream, status ) );
		}

	/* Try and write the data to the stream's backing storage */
	status = fileWrite( stream, stream->buffer, stream->bufPos );
	if( cryptStatusError( status ) )
		return( sSetError( stream, status ) );

	/* Reset the position-changed flag and, if we've written another buffer 
	   full of data, remember the details.  If it's a forced flush we leave
	   everything as is so that we remember the last write position in the 
	   file */
	stream->flags &= ~STREAM_FFLAG_POSCHANGED;
	if( !forcedFlush )
		{
		stream->bufCount++;
		stream->bufPos = 0;
		}

	ENSURES_S( sanityCheck( stream ) );

	return( CRYPT_OK );
	}

#ifdef VIRTUAL_FILE_STREAM 

/* Expand a virtual file stream's buffer to make room for new data when it
   fills up */

CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int expandVirtualFileStream( INOUT STREAM *stream, 
									IN_LENGTH const int length )
	{
	void *newBuffer;
	int newSize;

	assert( isWritePtr( stream, sizeof( STREAM ) ) );

	REQUIRES_S( sanityCheck( stream ) && \
				sIsVirtualFileStream( stream ) );
	REQUIRES_S( length > 0 && length < MAX_INTLENGTH );

	/* If it's a small buffer allocated when we initially read a file and it 
	   doesn't look like we'll be overflowing a standard-size buffer, just 
	   expand it up to STREAM_VFILE_BUFSIZE */
	if( stream->bufSize < STREAM_VFILE_BUFSIZE && \
		stream->bufPos + length < STREAM_VFILE_BUFSIZE - 1024 )
		newSize = STREAM_VFILE_BUFSIZE;
	else
		{
		/* Increase the stream buffer size in STREAM_VFILE_BUFSIZE steps */
		newSize = stream->bufSize + STREAM_VFILE_BUFSIZE;
		}

	/* Allocate the buffer and copy the new data across using a safe realloc 
	   that wipes the original buffer.  If the malloc fails we return 
	   CRYPT_ERROR_OVERFLOW rather than CRYPT_ERROR_MEMORY since the former 
	   is more appropriate for the emulated-I/O environment */
	if( ( newBuffer = clDynAlloc( "expandVirtualFileStream", \
								  stream->bufSize + STREAM_VFILE_BUFSIZE ) ) == NULL )
		return( sSetError( stream, CRYPT_ERROR_OVERFLOW ) );
	memcpy( newBuffer, stream->buffer, stream->bufEnd );
	zeroise( stream->buffer, stream->bufEnd );
	clFree( "expandVirtualFileStream", stream->buffer );
	stream->buffer = newBuffer;
	stream->bufSize = newSize;

	ENSURES_S( sanityCheck( stream ) );

	return( CRYPT_OK );
	}
#endif /* VIRTUAL_FILE_STREAM */

/****************************************************************************
*																			*
*							Stream Read Functions							*
*																			*
****************************************************************************/

/* Read data from a stream */

RETVAL_RANGE( MAX_ERROR, 0xFF ) STDC_NONNULL_ARG( ( 1 ) ) \
int sgetc( INOUT STREAM *stream )
	{
	int ch;

	assert( isWritePtr( stream, sizeof( STREAM ) ) );
	assert( isReadPtr( stream->buffer, stream->bufSize ) );
	
	/* Check that the input parameters are in order */
	if( !isWritePtr( stream, sizeof( STREAM ) ) )
		retIntError();

	REQUIRES_S( sanityCheck( stream ) );
	REQUIRES_S( stream->type == STREAM_TYPE_MEMORY || \
				stream->type == STREAM_TYPE_FILE );

	/* If there's a problem with the stream don't try to do anything */
	if( cryptStatusError( stream->status ) )
		return( stream->status );

	switch( stream->type )
		{
		case STREAM_TYPE_MEMORY:
			/* Read the data from the stream buffer */
			if( stream->bufPos >= stream->bufEnd )
				return( sSetError( stream, CRYPT_ERROR_UNDERFLOW ) );
			ch = stream->buffer[ stream->bufPos++ ];
			break;

		case STREAM_TYPE_FILE:
			REQUIRES_S( stream->flags & STREAM_FFLAG_BUFFERSET );

			/* Read the data from the file */
			if( stream->bufPos >= stream->bufEnd || \
				( stream->flags & STREAM_FFLAG_POSCHANGED ) )
				{
				int status = refillStream( stream );
				if( cryptStatusError( status ) )
					return( ( status == OK_SPECIAL ) ? 0 : status );
				}
			ch = stream->buffer[ stream->bufPos++ ];
			break;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -