📄 stream.c
字号:
/****************************************************************************
* *
* 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 + -