diff options
Diffstat (limited to '')
-rw-r--r-- | main.c | 178 |
1 files changed, 81 insertions, 97 deletions
@@ -1,24 +1,24 @@ -/* Xlunzip - Test tool for the lzip_decompress linux module - Copyright (C) 2016-2020 Antonio Diaz Diaz. +/* Xlunzip - Test tool for the lzip_decompress linux module + Copyright (C) 2016-2020 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 - the Free Software Foundation, either version 2 of the License, or - (at your option) any later version. + 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 + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ /* - 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 - (eg, bug) which caused xlunzip to panic. + 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 + (eg, bug) which caused xlunzip to panic. */ #define _FILE_OFFSET_BITS 64 @@ -94,21 +94,20 @@ static bool delete_output_on_interrupt = false; static void show_help( void ) { - printf( "Xlunzip is a test tool for the lzip decompression code of my lzip patch\n" - "for linux. Xlunzip is similar to lunzip, but it uses the lzip_decompress\n" - "linux module as a backend. Xlunzip tests the module for stream,\n" - "buffer-to-buffer and mixed decompression modes, including in-place\n" - "decompression (using the same buffer for input and output). You can use\n" - "xlunzip to verify that the module produces correct results when\n" - "decompressing single member files, multimember files, or the\n" - "concatenation of two or more compressed files. Xlunzip can be used with\n" - "unzcrash to test the robustness of the module to the decompression of\n" - "corrupted data.\n" - "\nNote that the in-place decompression of concatenated files can't be\n" - "guaranteed to work because an arbitrarily low compression ratio of the\n" - "last part of the data can be achieved by appending enough empty\n" - "compressed members to a file, masking a high compression ratio at the\n" - "beginning of the data.\n" + printf( "Xlunzip is a test tool for the lzip decompression code of my lzip patch for\n" + "linux. Xlunzip is similar to lunzip, but it uses the lzip_decompress linux\n" + "module as a backend. Xlunzip tests the module for stream, buffer-to-buffer,\n" + "and mixed decompression modes, including in-place decompression (using the\n" + "same buffer for input and output). You can use xlunzip to verify that the\n" + "module produces correct results when decompressing single member files,\n" + "multimember files, or the concatenation of two or more compressed files.\n" + "Xlunzip can be used with unzcrash to test the robustness of the module to\n" + "the decompression of corrupted data.\n" + "\nThe distributed index feature of the lzip format allows xlunzip to\n" + "decompress concatenated files in place. This can't be guaranteed to work\n" + "with formats like gzip or bzip2 because they can't detect whether a high\n" + "compression ratio in the first members of the multimember data is being\n" + "masked by a low compression ratio in the last members.\n" "\nUsage: %s [options] [files]\n", invocation_name ); printf( "\nOptions:\n" " -h, --help display this help and exit\n" @@ -118,7 +117,7 @@ static void show_help( void ) " -f, --force overwrite existing output files\n" " -I, --in-place decompress or test using only one buffer\n" " -k, --keep keep (don't delete) input files\n" - " -o, --output=<file> if reading standard input, write to <file>\n" + " -o, --output=<file> write to <file>, keep input files\n" " -q, --quiet suppress all messages\n" " -t, --test test compressed file integrity\n" " -v, --verbose be verbose (a 2nd -v gives more)\n" @@ -157,11 +156,7 @@ static void * resize_buffer( void * buf, const unsigned min_size ) { if( buf ) buf = realloc( buf, min_size ); else buf = malloc( min_size ); - if( !buf ) - { - show_error( "Not enough memory.", 0, false ); - cleanup_and_fail( 1 ); - } + if( !buf ) { show_error( mem_msg, 0, false ); cleanup_and_fail( 1 ); } return buf; } @@ -316,7 +311,7 @@ static void set_d_outname( const char * const name, const int eindex ) static int open_instream( const char * const name, struct stat * const in_statsp, - const bool no_ofile ) + const bool one_to_one ) { int infd = open( name, O_RDONLY | O_BINARY ); if( infd < 0 ) @@ -328,13 +323,12 @@ static int open_instream( const char * const name, struct stat * const in_statsp const bool can_read = ( i == 0 && ( S_ISBLK( mode ) || S_ISCHR( mode ) || S_ISFIFO( mode ) || S_ISSOCK( mode ) ) ); - if( i != 0 || ( !S_ISREG( mode ) && ( !can_read || !no_ofile ) ) ) + if( i != 0 || ( !S_ISREG( mode ) && ( !can_read || one_to_one ) ) ) { if( verbosity >= 0 ) fprintf( stderr, "%s: Input file '%s' is not a regular file%s.\n", - program_name, name, - ( can_read && !no_ofile ) ? - ",\n and '--stdout' was not specified" : "" ); + program_name, name, ( can_read && one_to_one ) ? + ",\n and neither '-c' nor '-o' were specified" : "" ); close( infd ); infd = -1; } @@ -343,11 +337,11 @@ static int open_instream( const char * const name, struct stat * const in_statsp } -static bool open_outstream( const bool force, const bool from_stdin ) +static bool open_outstream( const bool force, const bool protect ) { const mode_t usr_rw = S_IRUSR | S_IWUSR; const mode_t all_rw = usr_rw | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; - const mode_t outfd_mode = from_stdin ? all_rw : usr_rw; + const mode_t outfd_mode = protect ? usr_rw : all_rw; int flags = O_APPEND | O_CREAT | O_RDWR | O_BINARY; if( force ) flags |= O_TRUNC; else flags |= O_EXCL; @@ -399,7 +393,7 @@ static void signal_handler( int sig ) } - /* Set permissions, owner, and times. */ +/* Set permissions, owner, and times. */ static void close_and_set_permissions( const struct stat * const in_statsp ) { bool warning = false; @@ -499,7 +493,7 @@ static int decompress( struct Pretty_print * const pp, const long cl_insize, long in_pos, out_pos; int retval; if( ( in_len > 0 && !inbuf ) || ( out_size > 0 && !outbuf ) ) - { show_error( "Not enough memory.", 0, false ); return 1; } + { show_error( mem_msg, 0, false ); return 1; } if( inbuf ) { @@ -544,6 +538,9 @@ void show_results( struct Pretty_print * const pp, const long in_pos, } +static inline void set_retval( int * retval, const int new_val ) + { if( *retval < new_val ) *retval = new_val; } + static void show_error( const char * const msg, const int errcode, const bool help ) { @@ -623,7 +620,7 @@ int main( const int argc, const char * const argv[] ) if( argc > 0 ) invocation_name = argv[0]; if( !ap_init( &parser, argc, argv, options, 0 ) ) - { show_error( "Not enough memory.", 0, false ); return 1; } + { show_error( mem_msg, 0, false ); return 1; } if( ap_error( &parser ) ) /* bad option */ { show_error( ap_error( &parser ), 0, true ); return 1; } @@ -641,7 +638,8 @@ int main( const int argc, const char * const argv[] ) case 'I': in_place = true; break; case 'k': keep_input_files = true; break; case 'n': break; - case 'o': default_output_filename = arg; break; + case 'o': if( strcmp( arg, "-" ) == 0 ) to_stdout = true; + else { default_output_filename = arg; } break; case 'q': verbosity = -1; break; case 't': testing = true; break; case 'v': if( verbosity < 4 ) ++verbosity; break; @@ -672,64 +670,44 @@ int main( const int argc, const char * const argv[] ) if( strcmp( filenames[i], "-" ) != 0 ) filenames_given = true; } - if( testing ) - outfd = -1; + if( testing ) to_stdout = false; /* apply overrides */ + if( testing || to_stdout ) default_output_filename = ""; - if( !to_stdout && !testing && - ( filenames_given || default_output_filename[0] ) ) + output_filename = resize_buffer( output_filename, 1 ); + output_filename[0] = 0; + if( to_stdout && !testing ) outfd = STDOUT_FILENO; + else outfd = -1; + + const bool to_file = !to_stdout && !testing && default_output_filename[0]; + if( !to_stdout && !testing && ( filenames_given || to_file ) ) set_signals( signal_handler ); Pp_init( &pp, filenames, num_filenames ); - output_filename = resize_buffer( output_filename, 1 ); + const bool one_to_one = !to_stdout && !testing && !to_file; for( i = 0; i < num_filenames; ++i ) { const char * input_filename = ""; int tmp; struct stat in_stats; const struct stat * in_statsp; - output_filename[0] = 0; if( strcmp( filenames[i], "-" ) == 0 ) { if( stdin_used ) continue; else stdin_used = true; infd = STDIN_FILENO; - if( !testing ) - { - if( to_stdout || !default_output_filename[0] ) - outfd = STDOUT_FILENO; - else - { - output_filename = resize_buffer( output_filename, - strlen( default_output_filename ) + 1 ); - strcpy( output_filename, default_output_filename ); - if( !open_outstream( force, true ) ) - { - if( retval < 1 ) retval = 1; - close( infd ); infd = -1; - continue; - } - } - } + if( one_to_one ) { outfd = STDOUT_FILENO; output_filename[0] = 0; } } else { input_filename = filenames[i]; - infd = open_instream( input_filename, &in_stats, to_stdout || testing ); - if( infd < 0 ) { if( retval < 1 ) retval = 1; continue; } - if( !testing ) + infd = open_instream( input_filename, &in_stats, one_to_one ); + if( infd < 0 ) { set_retval( &retval, 1 ); continue; } + if( one_to_one ) { - if( to_stdout ) outfd = STDOUT_FILENO; - else - { - set_d_outname( input_filename, extension_index( input_filename ) ); - if( !open_outstream( force, false ) ) - { - if( retval < 1 ) retval = 1; - close( infd ); infd = -1; - continue; - } - } + set_d_outname( input_filename, extension_index( input_filename ) ); + if( !open_outstream( force, true ) ) + { set_retval( &retval, 1 ); close( infd ); infd = -1; continue; } } } @@ -738,37 +716,43 @@ int main( const int argc, const char * const argv[] ) { show_file_error( pp.name, "I won't read compressed data from a terminal.", 0 ); - if( retval < 1 ) retval = 1; + set_retval( &retval, 1 ); if( testing ) { close( infd ); infd = -1; continue; } cleanup_and_fail( retval ); } - in_statsp = input_filename[0] ? &in_stats : 0; + if( to_file && outfd < 0 ) /* open outfd after verifying infd */ + { + output_filename = resize_buffer( output_filename, + strlen( default_output_filename ) + 1 ); + strcpy( output_filename, default_output_filename ); + if( !open_outstream( force, false ) ) return 1; + } + + in_statsp = ( input_filename[0] && one_to_one ) ? &in_stats : 0; if( in_place ) tmp = decompress_in_place( infd, &pp, testing ); else tmp = decompress( &pp, cl_insize, cl_outsize, nofill, noflush, testing ); if( close( infd ) != 0 ) - { - show_error( input_filename[0] ? "Error closing input file" : - "Error closing stdin", errno, false ); - if( tmp < 1 ) tmp = 1; - } + { show_file_error( pp.name, "Error closing input file", errno ); + set_retval( &tmp, 1 ); } infd = -1; - if( tmp > retval ) retval = tmp; + set_retval( &retval, tmp ); if( tmp ) { if( !testing ) cleanup_and_fail( retval ); else ++failed_tests; } - if( delete_output_on_interrupt ) + if( delete_output_on_interrupt && one_to_one ) close_and_set_permissions( in_statsp ); - if( input_filename[0] && !keep_input_files && !to_stdout && !testing ) + if( input_filename[0] && !keep_input_files && one_to_one ) remove( input_filename ); } - if( outfd >= 0 && close( outfd ) != 0 ) + if( delete_output_on_interrupt ) close_and_set_permissions( 0 ); /* -o */ + else if( outfd >= 0 && close( outfd ) != 0 ) /* -c */ { show_error( "Error closing stdout", errno, false ); - if( retval < 1 ) retval = 1; + set_retval( &retval, 1 ); } if( failed_tests > 0 && verbosity >= 1 && num_filenames > 1 ) fprintf( stderr, "%s: warning: %d %s failed the test.\n", |