📄 stream.c
字号:
/****************************************************************************
* *
* Stream I/O Functions *
* Copyright Peter Gutmann 1993-2003 *
* *
****************************************************************************/
#include <stdarg.h>
#include <stdlib.h>
#if defined( INC_ALL )
#include "stream.h"
#elif defined( INC_CHILD )
#include "stream.h"
#else
#include "misc/stream.h"
#endif /* Compiler-specific includes */
/* Prototypes for functions in str_file.c */
int fileRead( STREAM *stream, void *buffer, const int length );
int fileWrite( STREAM *stream, const void *buffer, const int length );
int fileFlush( STREAM *stream );
int fileSeek( STREAM *stream, const long position );
/****************************************************************************
* *
* Utility Functions *
* *
****************************************************************************/
/* Exit after saving a detailed error message. This is used by the stream
transport-layer code to provide more information to the caller than a
basic error code */
int retExtStreamFn( STREAM *stream, const int status, const char *format, ... )
{
#ifdef USE_TCP
va_list argPtr;
va_start( argPtr, format );
vsprintf( stream->errorMessage, format, argPtr );
va_end( argPtr );
#endif /* USE_TCP */
stream->status = status;
assert( !cryptArgError( status ) ); /* Catch leaks */
return( cryptArgError( status ) ? CRYPT_ERROR_FAILED : status );
}
/* Refill a stream buffer from backing storage */
static int refillStream( STREAM *stream )
{
int status;
assert( stream->type == STREAM_TYPE_FILE );
/* If we've reached EOF we can't refill it */
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_FFLAG_PARTIALREAD ) ? \
OK_SPECIAL : CRYPT_ERROR_UNDERFLOW );
}
/* If we've moved to a different place in the file, get new data into
the buffer */
if( ( stream->flags & STREAM_FFLAG_POSCHANGED ) && \
!( stream->flags & STREAM_FFLAG_POSCHANGED_NOSKIP ) )
{
status = fileSeek( stream, stream->bufCount * stream->bufSize );
if( cryptStatusError( status ) )
{
stream->status = status;
return( status );
}
}
/* Try and read more data into the stream buffer */
status = fileRead( stream, stream->buffer, stream->bufSize );
if( cryptStatusError( status ) )
{
stream->status = status;
return( status );
}
if( status < 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( status == 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_FFLAG_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 = status;
stream->flags &= ~( STREAM_FFLAG_POSCHANGED | \
STREAM_FFLAG_POSCHANGED_NOSKIP );
return( CRYPT_OK );
}
/* Empty a stream buffer to backing storage */
static int emptyStream( STREAM *stream, const BOOLEAN forcedFlush )
{
int status = CRYPT_OK;
assert( 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 ) )
{
stream->status = status;
return( status );
}
}
/* Try and write the data to the stream's backing storage */
status = fileWrite( stream, stream->buffer, stream->bufPos );
if( cryptStatusError( status ) )
{
stream->status = status;
return( 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, to remember the last write position in the file */
stream->flags &= ~STREAM_FFLAG_POSCHANGED;
if( !forcedFlush )
{
stream->bufCount++;
stream->bufPos = 0;
}
return( CRYPT_OK );
}
/****************************************************************************
* *
* Read/Write Functions *
* *
****************************************************************************/
/* Read data from a stream */
int sgetc( STREAM *stream )
{
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( stream->type == STREAM_TYPE_MEMORY || \
stream->type == STREAM_TYPE_FILE );
assert( isReadPtr( stream->buffer, stream->bufSize ) );
assert( stream->bufPos >= 0 && stream->bufPos <= stream->bufEnd );
/* 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:
assert( !( stream->flags & ~STREAM_FLAG_MASK ) );
/* Read the data from the stream buffer */
if( stream->bufSize != STREAMSIZE_UNKNOWN && \
stream->bufPos >= stream->bufEnd )
{
stream->status = CRYPT_ERROR_UNDERFLOW;
return( CRYPT_ERROR_UNDERFLOW );
}
return( stream->buffer[ stream->bufPos++ ] );
case STREAM_TYPE_FILE:
assert( !( stream->flags & ~STREAM_FFLAG_MASK ) );
/* 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 );
}
return( stream->buffer[ stream->bufPos++ ] );
}
assert( NOTREACHED );
return( CRYPT_ERROR_READ ); /* Get rid of compiler warning */
}
int sread( STREAM *stream, void *buffer, const int length )
{
int status;
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( stream->type == STREAM_TYPE_MEMORY || \
stream->type == STREAM_TYPE_FILE || \
stream->type == STREAM_TYPE_NETWORK );
assert( stream->bufPos >= 0 && stream->bufPos <= stream->bufEnd );
assert( stream->type == STREAM_TYPE_NETWORK || \
isReadPtr( stream->buffer, stream->bufSize ) );
assert( isWritePtr( buffer, length ) );
assert( length > 0 );
/* 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:
assert( !( stream->flags & ~STREAM_FLAG_MASK ) );
/* Read the data from the stream buffer */
if( stream->bufSize != STREAMSIZE_UNKNOWN && \
stream->bufPos + length > stream->bufEnd )
{
memset( buffer, 0, length ); /* Clear the output buffer */
stream->status = CRYPT_ERROR_UNDERFLOW;
return( CRYPT_ERROR_UNDERFLOW );
}
memcpy( buffer, stream->buffer + stream->bufPos, length );
stream->bufPos += length;
return( CRYPT_OK );
case STREAM_TYPE_FILE:
{
BYTE *bufPtr = buffer;
int dataLength = length, bytesCopied = 0;
assert( !( stream->flags & ~STREAM_FFLAG_MASK ) );
/* Read the data from the file */
while( dataLength > 0 )
{
int bytesToCopy;
/* If the stream buffer is empty, try and refill it */
if( stream->bufPos >= stream->bufEnd || \
( stream->flags & STREAM_FFLAG_POSCHANGED ) )
{
status = refillStream( stream );
if( cryptStatusError( status ) )
return( ( status == OK_SPECIAL ) ? \
bytesCopied : status );
}
/* Copy as much data as we can out of the stream buffer */
bytesToCopy = min( dataLength, \
stream->bufEnd - stream->bufPos );
memcpy( bufPtr, stream->buffer + stream->bufPos,
bytesToCopy );
stream->bufPos += bytesToCopy;
bufPtr += bytesToCopy;
bytesCopied += bytesToCopy;
dataLength -= bytesToCopy;
}
/* Usually reads are atomic so we just return an all-OK
indicator, however if we're performing partial reads we need
to return an exact byte count */
return( ( stream->flags & STREAM_FFLAG_PARTIALREAD ) ? \
bytesCopied : CRYPT_OK );
}
#ifdef USE_TCP
case STREAM_TYPE_NETWORK:
assert( !( stream->flags & ~STREAM_NFLAG_MASK ) );
assert( stream->readFunction != NULL );
assert( ( stream->flags & STREAM_NFLAG_ISSERVER ) || \
stream->host != NULL || \
stream->netSocket != CRYPT_ERROR );
status = stream->readFunction( stream, buffer, length );
/* Read the data from the network */
if( status == CRYPT_ERROR_COMPLETE )
{
/* The other side has closed the connection, update the
stream state and map the status to a standard read error.
The exact code to return here is a bit uncertain, it
isn't specifically a read error because either the other
side is allowed to close the connection after it's said
its bit (and so it's not a read error), or it has to
perform a cryptographically protected close (in which
case any non-OK status indicates a problem). The most
sensible status is probably a read error */
sioctl( stream, STREAM_IOCTL_CONNSTATE, NULL, FALSE );
status = CRYPT_ERROR_READ;
}
return( status );
#endif /* USE_TCP */
}
assert( NOTREACHED );
return( CRYPT_ERROR_READ ); /* Get rid of compiler warning */
}
/* Write data to a stream */
int sputc( STREAM *stream, const int ch )
{
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( stream->type == STREAM_TYPE_NULL || \
stream->type == STREAM_TYPE_MEMORY || \
stream->type == STREAM_TYPE_FILE );
assert( stream->type == STREAM_TYPE_NULL || \
stream->bufSize == STREAMSIZE_UNKNOWN || \
isWritePtr( stream->buffer, stream->bufSize ) );
assert( stream->type == STREAM_TYPE_NULL || \
stream->bufSize == STREAMSIZE_UNKNOWN || \
stream->bufPos >= 0 && stream->bufPos <= stream->bufSize );
assert( !( stream->flags & STREAM_FLAG_READONLY ) );
/* If there's a problem with the stream, don't try to do anything until
the error is cleared */
if( cryptStatusError( stream->status ) )
return( stream->status );
switch( stream->type )
{
case STREAM_TYPE_NULL:
assert( !stream->flags );
/* It's a null stream, just record the write and return */
stream->bufPos++;
return( CRYPT_OK );
case STREAM_TYPE_MEMORY:
assert( !( stream->flags & ~STREAM_FLAG_MASK ) );
/* Write the data to the stream buffer */
if( stream->bufSize != STREAMSIZE_UNKNOWN && \
stream->bufPos >= stream->bufSize )
{
stream->status = CRYPT_ERROR_OVERFLOW;
return( CRYPT_ERROR_OVERFLOW );
}
stream->buffer[ stream->bufPos++ ] = ch;
return( CRYPT_OK );
case STREAM_TYPE_FILE:
assert( !( stream->flags & ~STREAM_FFLAG_MASK ) );
/* Write the data to the file */
if( stream->bufPos >= stream->bufSize )
{
int status;
status = emptyStream( stream, FALSE );
if( cryptStatusError( stream ) )
return( status );
}
stream->buffer[ stream->bufPos++ ] = ch;
stream->flags |= STREAM_FFLAG_DIRTY;
return( CRYPT_OK );
}
assert( NOTREACHED );
return( CRYPT_ERROR_WRITE ); /* Get rid of compiler warning */
}
int swrite( STREAM *stream, const void *buffer, const int length )
{
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( stream->type == STREAM_TYPE_NULL || \
stream->type == STREAM_TYPE_MEMORY || \
stream->type == STREAM_TYPE_FILE || \
stream->type == STREAM_TYPE_NETWORK );
assert( stream->type == STREAM_TYPE_NULL || \
stream->type == STREAM_TYPE_NETWORK || \
stream->bufSize == STREAMSIZE_UNKNOWN || \
isWritePtr( stream->buffer, stream->bufSize ) );
assert( stream->type == STREAM_TYPE_NULL || \
stream->type == STREAM_TYPE_NETWORK || \
stream->bufSize == STREAMSIZE_UNKNOWN || \
( stream->bufPos >= 0 && stream->bufPos <= stream->bufSize ) );
assert( isReadPtr( buffer, length ) );
assert( length > 0 );
assert( !( stream->flags & STREAM_FLAG_READONLY ) );
/* If there's a problem with the stream, don't try to do anything until
the error is cleared */
if( cryptStatusError( stream->status ) )
return( stream->status );
switch( stream->type )
{
case STREAM_TYPE_NULL:
assert( !stream->flags );
/* It's a null stream, just record the write and return */
stream->bufPos += length;
return( CRYPT_OK );
case STREAM_TYPE_MEMORY:
assert( !( stream->flags & ~STREAM_FLAG_MASK ) );
/* Write the data to the stream buffer */
if( stream->bufSize != STREAMSIZE_UNKNOWN && \
stream->bufPos + length > stream->bufSize )
{
stream->status = CRYPT_ERROR_OVERFLOW;
return( CRYPT_ERROR_OVERFLOW );
}
memcpy( stream->buffer + stream->bufPos, buffer, length );
stream->bufPos += length;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -