summaryrefslogtreecommitdiffstats
path: root/unzcrash.cc
diff options
context:
space:
mode:
Diffstat (limited to 'unzcrash.cc')
-rw-r--r--unzcrash.cc116
1 files changed, 51 insertions, 65 deletions
diff --git a/unzcrash.cc b/unzcrash.cc
index 0c92af8..b04bd05 100644
--- a/unzcrash.cc
+++ b/unzcrash.cc
@@ -1,6 +1,6 @@
/* Unzcrash - Tests robustness of decompressors to corrupted data.
Inspired by unzcrash.c from Julian Seward's bzip2.
- Copyright (C) 2008-2022 Antonio Diaz Diaz.
+ Copyright (C) 2008-2023 Antonio Diaz Diaz.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -17,9 +17,9 @@
*/
/*
Exit status: 0 for a normal exit, 1 for environmental problems
- (file not found, invalid flags, I/O errors, etc), 2 to indicate a
- corrupt or invalid input file, 3 for an internal consistency error
- (e.g., bug) which caused unzcrash to panic.
+ (file not found, invalid command line options, I/O errors, etc), 2 to
+ indicate a corrupt or invalid input file, 3 for an internal consistency
+ error (e.g., bug) which caused unzcrash to panic.
*/
#define _FILE_OFFSET_BITS 64
@@ -91,7 +91,7 @@ void show_help()
" -B, --block[=<size>][,<val>] test blocks of given size [512,0]\n"
" -d, --delta=<n> test one byte/block/truncation every n bytes\n"
" -e, --set-byte=<pos>,<val> set byte at position <pos> to value <val>\n"
- " -n, --no-verify skip initial verification of file.lz\n"
+ " -n, --no-check skip initial test of file.lz and zcmp\n"
" -p, --position=<bytes> first byte position to test [default 0]\n"
" -q, --quiet suppress all messages\n"
" -s, --size=<bytes> number of byte positions to test [all]\n"
@@ -101,10 +101,10 @@ void show_help()
"Examples of <range>: 1 1,2,3 1-4 1,3-5,8 1-3,5-8\n"
"A negative position is relative to the end of file.\n"
"A negative size is relative to the rest of the file.\n"
- "\nExit status: 0 for a normal exit, 1 for environmental problems (file\n"
- "not found, invalid flags, I/O errors, etc), 2 to indicate a corrupt or\n"
- "invalid input file, 3 for an internal consistency error (e.g., bug) which\n"
- "caused unzcrash to panic.\n"
+ "\nExit status: 0 for a normal exit, 1 for environmental problems\n"
+ "(file not found, invalid command line options, I/O errors, etc), 2 to\n"
+ "indicate a corrupt or invalid input file, 3 for an internal consistency\n"
+ "error (e.g., bug) which caused unzcrash to panic.\n"
"\nReport bugs to lzip-bug@nongnu.org\n"
"Lziprecover home page: http://www.nongnu.org/lzip/lziprecover.html\n" );
}
@@ -125,58 +125,46 @@ void parse_block( const char * const arg, const char * const option_name,
if( tail[0] == ',' )
value = getnum( tail + 1, option_name, 0, 0, 255 );
else if( tail[0] )
- {
- if( verbosity >= 0 )
- std::fprintf( stderr, "%s: Bad separator between <size> and <value> in "
- "argument of option '%s'.\n", program_name, option_name );
- std::exit( 1 );
- }
+ { show_option_error( arg, "Missing comma between <size> and <value> in",
+ option_name ); std::exit( 1 ); }
}
/* Return the address of a malloc'd buffer containing the file data and
- the file size in '*size'.
- In case of error, return 0 and do not modify '*size'.
+ the file size in '*file_sizep'.
+ In case of error, return 0 and do not modify '*file_sizep'.
*/
-uint8_t * read_file( const char * const name, long * const size )
+uint8_t * read_file( const char * const filename, long * const file_sizep )
{
- FILE * const f = std::fopen( name, "rb" );
+ FILE * const f = std::fopen( filename, "rb" );
if( !f )
- {
- if( verbosity >= 0 )
- std::fprintf( stderr, "%s: Can't open input file '%s': %s\n",
- program_name, name, std::strerror( errno ) );
- return 0;
- }
+ { show_file_error( filename, "Can't open input file", errno ); return 0; }
- long buffer_size = 1 << 20;
+ long buffer_size = 65536;
uint8_t * buffer = (uint8_t *)std::malloc( buffer_size );
if( !buffer ) { show_error( mem_msg ); return 0; }
long file_size = std::fread( buffer, 1, buffer_size, f );
- while( file_size >= buffer_size )
+ while( file_size >= buffer_size || ( !std::ferror( f ) && !std::feof( f ) ) )
{
- if( buffer_size >= LONG_MAX )
+ if( file_size >= buffer_size ) // may be false because of EINTR
{
- if( verbosity >= 0 )
- std::fprintf( stderr, "%s: Input file '%s' is too large.\n",
- program_name, name );
- std::free( buffer ); return 0;
+ if( buffer_size >= LONG_MAX )
+ { show_file_error( filename, "Input file is larger than LONG_MAX." );
+ std::free( buffer ); return 0; }
+ buffer_size = ( buffer_size <= LONG_MAX / 2 ) ? 2 * buffer_size : LONG_MAX;
+ uint8_t * const tmp = (uint8_t *)std::realloc( buffer, buffer_size );
+ if( !tmp ) { show_error( mem_msg ); std::free( buffer ); return 0; }
+ buffer = tmp;
}
- buffer_size = ( buffer_size <= LONG_MAX / 2 ) ? 2 * buffer_size : LONG_MAX;
- uint8_t * const tmp = (uint8_t *)std::realloc( buffer, buffer_size );
- if( !tmp ) { show_error( mem_msg ); std::free( buffer ); return 0; }
- buffer = tmp;
file_size += std::fread( buffer + file_size, 1, buffer_size - file_size, f );
}
if( std::ferror( f ) || !std::feof( f ) )
{
- if( verbosity >= 0 )
- std::fprintf( stderr, "%s: Error reading file '%s': %s\n",
- program_name, name, std::strerror( errno ) );
+ show_file_error( filename, "Error reading input file", errno );
std::free( buffer ); return 0;
}
std::fclose( f );
- *size = file_size;
+ *file_sizep = file_size;
return buffer;
}
@@ -194,8 +182,9 @@ public:
{ return ( i >= 1 && i <= 8 && data[i-1] ); }
// Recognized formats: 1 1,2,3 1-4 1,3-5,8 1-3,5-8
- bool parse_bs( const char * p )
+ void parse_bs( const char * const arg, const char * const option_name )
{
+ const char * p = arg;
for( int i = 0; i < 8; ++i ) data[i] = false;
while( true )
{
@@ -209,11 +198,11 @@ public:
for( int c = ch1; c <= *p; ++c ) data[c-'1'] = true;
++p;
}
- if( *p == 0 ) return true;
+ if( *p == 0 ) return;
if( *p == ',' ) ++p; else break;
}
- show_error( "Invalid value or range." );
- return false;
+ show_option_error( arg, "Invalid bit position or range in", option_name );
+ std::exit( 1 );
}
// number of N-bit errors per byte (N=0 to 8): 1 8 28 56 70 56 28 8 1
@@ -327,9 +316,9 @@ bool word_split( const char * const command, std::vector< std::string > & args )
}
-// return -1 if fatal error, 0 if OK, >0 if error
+// return -1 if fatal error, 0 if OK, > 0 if error
int fork_and_feed( const uint8_t * const buffer, const long buffer_size,
- const char ** const argv, const bool verify = false )
+ const char ** const argv, const bool check = false )
{
int fda[2]; // pipe to child
if( pipe( fda ) < 0 )
@@ -342,7 +331,7 @@ int fork_and_feed( const uint8_t * const buffer, const long buffer_size,
{
if( close( fda[0] ) != 0 )
{ show_error( "Error closing unused pipe", errno ); return -1; }
- if( writeblock( fda[1], buffer, buffer_size ) != buffer_size && verify )
+ if( writeblock( fda[1], buffer, buffer_size ) != buffer_size && check )
{ show_error( "Can't write to child process", errno ); return -1; }
if( close( fda[1] ) != 0 )
{ show_error( "Error closing pipe", errno ); return -1; }
@@ -375,7 +364,7 @@ int main( const int argc, const char * const argv[] )
long block_size = 512;
Mode program_mode = m_byte;
uint8_t block_value = 0;
- bool verify = true;
+ bool check = true;
if( argc > 0 ) invocation_name = argv[0];
const Arg_parser::Option options[] =
@@ -385,6 +374,7 @@ int main( const int argc, const char * const argv[] )
{ 'B', "block", Arg_parser::maybe },
{ 'd', "delta", Arg_parser::yes },
{ 'e', "set-byte", Arg_parser::yes },
+ { 'n', "no-check", Arg_parser::no },
{ 'n', "no-verify", Arg_parser::no },
{ 'p', "position", Arg_parser::yes },
{ 'q', "quiet", Arg_parser::no },
@@ -409,12 +399,12 @@ int main( const int argc, const char * const argv[] )
switch( code )
{
case 'h': show_help(); return 0;
- case 'b': if( !bits.parse_bs( arg ) ) return 1; program_mode = m_byte; break;
+ case 'b': bits.parse_bs( arg, pn ); program_mode = m_byte; break;
case 'B': if( arg[0] ) parse_block( arg, pn, block_size, block_value );
program_mode = m_block; break;
case 'd': delta = getnum( arg, pn, block_size, 1, INT_MAX ); break;
case 'e': bad_byte.parse_bb( arg, pn ); break;
- case 'n': verify = false; break;
+ case 'n': check = false; break;
case 'p': pos = getnum( arg, pn, block_size, -LONG_MAX, LONG_MAX ); break;
case 'q': verbosity = -1; break;
case 's': max_size = getnum( arg, pn, block_size, -LONG_MAX, LONG_MAX ); break;
@@ -438,7 +428,7 @@ int main( const int argc, const char * const argv[] )
const char * const command = parser.argument( argind ).c_str();
std::vector< std::string > command_args;
if( !word_split( command, command_args ) )
- { show_file_error( command, "Invalid command" ); return 1; }
+ { show_file_error( command, "Invalid command." ); return 1; }
const char ** const command_argv = new const char *[command_args.size()+1];
for( unsigned i = 0; i < command_args.size(); ++i )
command_argv[i] = command_args[i].c_str();
@@ -456,7 +446,7 @@ int main( const int argc, const char * const argv[] )
zcmp_command = zcmp_program;
zcmp_command += " '"; zcmp_command += filename; zcmp_command += "' -";
if( !word_split( zcmp_command.c_str(), zcmp_args ) )
- { show_file_error( zcmp_command.c_str(), "Invalid zcmp command" );
+ { show_file_error( zcmp_command.c_str(), "Invalid zcmp command." );
return 1; }
zcmp_argv = new const char *[zcmp_args.size()+1];
for( unsigned i = 0; i < zcmp_args.size(); ++i )
@@ -464,9 +454,9 @@ int main( const int argc, const char * const argv[] )
zcmp_argv[zcmp_args.size()] = 0;
}
- // verify original file
+ // check original file
if( verbosity >= 1 ) fprintf( stderr, "Testing file '%s'\n", filename );
- if( verify )
+ if( check )
{
const int ret = fork_and_feed( buffer, file_size, command_argv, true );
if( ret != 0 )
@@ -510,12 +500,8 @@ int main( const int argc, const char * const argv[] )
if( max_size < 0 ) max_size += file_size - pos;
const long end = ( ( max_size < file_size - pos ) ? pos + max_size : file_size );
if( bad_byte.pos >= file_size )
- {
- if( verbosity >= 0 )
- std::fprintf( stderr, "%s: Position is beyond end of file "
- "in option '%s'.\n", program_name, bad_byte.option_name );
- return 1;
- }
+ { show_option_error( bad_byte.argument, "Position is beyond end of file in",
+ bad_byte.option_name ); return 1; }
if( bad_byte.pos >= 0 )
buffer[bad_byte.pos] = bad_byte( buffer[bad_byte.pos] );
long positions = 0, decompressions = 0, successes = 0, failed_comparisons = 0;
@@ -625,17 +611,17 @@ int main( const int argc, const char * const argv[] )
if( verbosity >= 0 )
{
- std::fprintf( stderr, "\n%8ld %ss tested\n%8ld total decompressions"
- "\n%8ld decompressions returned with zero status",
+ std::fprintf( stderr, "\n%9ld %ss tested\n%9ld total decompressions"
+ "\n%9ld decompressions returned with zero status",
positions, mode_str[program_mode], decompressions, successes );
if( successes > 0 )
{
if( zcmp_command.empty() )
- std::fputs( "\n comparisons disabled\n", stderr );
+ std::fputs( "\n comparisons disabled\n", stderr );
else if( failed_comparisons > 0 )
- std::fprintf( stderr, ", of which\n%8ld comparisons failed\n",
+ std::fprintf( stderr, ", of which\n%9ld comparisons failed\n",
failed_comparisons );
- else std::fputs( "\n all comparisons passed\n", stderr );
+ else std::fputs( "\n all comparisons passed\n", stderr );
}
else std::fputc( '\n', stderr );
}