1
0
Fork 0

Merging upstream version 0.6.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-02-17 22:51:44 +01:00
parent e39d8907e0
commit f4329ad86e
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
13 changed files with 378 additions and 275 deletions

178
main.c
View file

@ -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",