summaryrefslogtreecommitdiffstats
path: root/main.cc
diff options
context:
space:
mode:
Diffstat (limited to 'main.cc')
-rw-r--r--main.cc275
1 files changed, 167 insertions, 108 deletions
diff --git a/main.cc b/main.cc
index 3307194..142216f 100644
--- a/main.cc
+++ b/main.cc
@@ -1,5 +1,5 @@
/* Lziprecover - Data recovery tool for the lzip format
- Copyright (C) 2009-2018 Antonio Diaz Diaz.
+ Copyright (C) 2009-2019 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
@@ -38,25 +38,29 @@
#include <unistd.h>
#include <utime.h>
#include <sys/stat.h>
-#if defined(__MSVCRT__)
+#if defined(__MSVCRT__) || defined(__OS2__) || defined(__DJGPP__)
#include <io.h>
+#if defined(__MSVCRT__)
#define fchmod(x,y) 0
#define fchown(x,y,z) 0
#define SIGHUP SIGTERM
#define S_ISSOCK(x) 0
+#ifndef S_IRGRP
#define S_IRGRP 0
#define S_IWGRP 0
#define S_IROTH 0
#define S_IWOTH 0
#endif
-#if defined(__OS2__)
-#include <io.h>
+#endif
+#if defined(__DJGPP__)
+#define S_ISSOCK(x) 0
+#define S_ISVTX 0
+#endif
#endif
#include "arg_parser.h"
#include "lzip.h"
#include "decoder.h"
-#include "block.h"
#ifndef O_BINARY
#define O_BINARY 0
@@ -67,12 +71,11 @@
#endif
int verbosity = 0;
-std::string output_filename; // global vars for output file
-int outfd = -1;
+std::string output_filename; // global vars for output file
+int outfd = -1; // see 'delete_output_on_interrupt' below
namespace {
-const char * const Program_name = "Lziprecover";
const char * const program_name = "lziprecover";
const char * invocation_name = 0;
@@ -82,53 +85,58 @@ const struct { const char * from; const char * to; } known_extensions[] = {
{ 0, 0 } };
enum Mode { m_none, m_alone_to_lz, m_debug_decompress, m_debug_delay,
- m_debug_repair, m_decompress, m_dump_tdata, m_list, m_merge,
- m_range_dec, m_remove_tdata, m_repair, m_show_packets, m_split,
- m_strip_tdata, m_test };
+ m_debug_repair, m_decompress, m_dump, m_list, m_merge,
+ m_range_dec, m_remove, m_repair, m_show_packets, m_split,
+ m_strip, m_test };
+/* Variable used in signal handler context.
+ It is not declared volatile because the handler never returns. */
bool delete_output_on_interrupt = false;
void show_help()
{
- std::printf( "%s - Data recovery tool and decompressor for the lzip format.\n", Program_name );
- std::printf( "\nLziprecover can repair perfectly most files with small errors (up to one\n"
+ std::printf( "Lziprecover is a data recovery tool and decompressor for files in the lzip\n"
+ "compressed data format (.lz). Lziprecover is able to repair slightly damaged\n"
+ "files, produce a correct file by merging the good parts of two or more\n"
+ "damaged copies, extract data from damaged files, decompress files and test\n"
+ "integrity of files.\n"
+ "\nLziprecover can repair perfectly most files with small errors (up to one\n"
"single-byte error per member), without the need of any extra redundance\n"
"at all. Losing an entire archive just because of a corrupt byte near the\n"
"beginning is a thing of the past.\n"
- "\nLziprecover can also produce a correct file by merging the good parts of\n"
- "two or more damaged copies, extract data from damaged files, decompress\n"
- "files and test integrity of files.\n"
- "\nLziprecover provides random access to the data in multimember files; it\n"
- "only decompresses the members containing the desired data.\n"
- "\nLziprecover facilitates the management of metadata stored as trailing\n"
- "data in lzip files.\n"
+ "\nLziprecover can remove the damaged members from multimember files, for\n"
+ "example multimember tar.lz archives.\n"
+ "\nLziprecover provides random access to the data in multimember files; it only\n"
+ "decompresses the members containing the desired data.\n"
+ "\nLziprecover facilitates the management of metadata stored as trailing data\n"
+ "in lzip files.\n"
"\nLziprecover is not a replacement for regular backups, but a last line of\n"
"defense for the case where the backups are also damaged.\n"
"\nUsage: %s [options] [files]\n", invocation_name );
std::printf( "\nOptions:\n"
- " -h, --help display this help and exit\n"
- " -V, --version output version information and exit\n"
- " -a, --trailing-error exit with error status if trailing data\n"
- " -A, --alone-to-lz convert lzma-alone files to lzip format\n"
- " -c, --stdout write to standard output, keep input files\n"
- " -d, --decompress decompress\n"
- " -D, --range-decompress=<range> decompress a range of bytes (N-M) to stdout\n"
- " -f, --force overwrite existing output files\n"
- " -i, --ignore-errors make '--range-decompress' ignore data errors\n"
- " -k, --keep keep (don't delete) input files\n"
- " -l, --list print (un)compressed file sizes\n"
- " -m, --merge correct errors in file using several copies\n"
- " -o, --output=<file> place the output into <file>\n"
- " -q, --quiet suppress all messages\n"
- " -R, --repair try to repair a small error in file\n"
- " -s, --split split multimember file in single-member files\n"
- " -t, --test test compressed file integrity\n"
- " -v, --verbose be verbose (a 2nd -v gives more)\n"
- " --loose-trailing allow trailing data seeming corrupt header\n"
- " --dump-tdata dump trailing data to standard output\n"
- " --remove-tdata remove trailing data from files in place\n"
- " --strip-tdata copy files to stdout without trailing data\n" );
+ " -h, --help display this help and exit\n"
+ " -V, --version output version information and exit\n"
+ " -a, --trailing-error exit with error status if trailing data\n"
+ " -A, --alone-to-lz convert lzma-alone files to lzip format\n"
+ " -c, --stdout write to standard output, keep input files\n"
+ " -d, --decompress decompress\n"
+ " -D, --range-decompress=<n-m> decompress a range of bytes to stdout\n"
+ " -f, --force overwrite existing output files\n"
+ " -i, --ignore-errors all errors in -D, format errors in -l, --dump\n"
+ " -k, --keep keep (don't delete) input files\n"
+ " -l, --list print (un)compressed file sizes\n"
+ " -m, --merge correct errors in file using several copies\n"
+ " -o, --output=<file> place the output into <file>\n"
+ " -q, --quiet suppress all messages\n"
+ " -R, --repair try to repair a small error in file\n"
+ " -s, --split split multimember file in single-member files\n"
+ " -t, --test test compressed file integrity\n"
+ " -v, --verbose be verbose (a 2nd -v gives more)\n"
+ " --loose-trailing allow trailing data seeming corrupt header\n"
+ " --dump=<list>:d:t dump members listed/damaged, tdata to stdout\n"
+ " --remove=<list>:d:t remove members, tdata from files in place\n"
+ " --strip=<list>:d:t copy files to stdout stripping members given\n" );
if( verbosity >= 1 )
{
std::printf( " -W, --debug-decompress=<pos>,<val> set pos to val and decompress to stdout\n"
@@ -202,6 +210,46 @@ void show_header( const unsigned dictionary_size )
#include "main_common.cc"
+// Colon-separated list of "damaged", "tdata", [r][^]<list> (1 1,3-5,8)
+void Member_list::parse( const char * p )
+ {
+ while( true )
+ {
+ const char * tp = p; // points to terminator; ':' or null
+ while( *tp && *tp != ':' ) ++tp;
+ const unsigned len = tp - p;
+ if( std::isalpha( (const unsigned char)*p ) )
+ {
+ if( len <= 7 && std::strncmp( "damaged", p, len ) == 0 )
+ { damaged = true; goto next; }
+ if( len <= 5 && std::strncmp( "tdata", p, len ) == 0 )
+ { tdata = true; goto next; }
+ }
+ {
+ const bool reverse = ( *p == 'r' );
+ if( reverse ) ++p;
+ if( *p == '^' ) { ++p; if( reverse ) rin = false; else in = false; }
+ std::vector< Block > * rvp = reverse ? &rrange_vector : &range_vector;
+ while( std::isdigit( (const unsigned char)*p ) )
+ {
+ const char * tail;
+ const int pos = getnum( p, 0, 1, INT_MAX, &tail ) - 1;
+ if( rvp->size() && pos < rvp->back().end() ) break;
+ const int size = (*tail == '-') ?
+ getnum( tail + 1, 0, pos + 1, INT_MAX, &tail ) - pos : 1;
+ rvp->push_back( Block( pos, size ) );
+ if( tail == tp ) goto next;
+ if( *tail == ',' ) p = tail + 1; else break;
+ }
+ }
+ show_error( "Invalid list of members." );
+ std::exit( 1 );
+next:
+ if( *(p = tp) != 0 ) ++p; else return;
+ }
+ }
+
+
namespace {
// Recognized formats: <begin> <begin>-<end> <begin>,<size> ,<size>
@@ -215,11 +263,11 @@ void parse_range( const char * const ptr, Block & range )
{
range.pos( value );
if( tail[0] == 0 ) { range.size( INT64_MAX - value ); return; }
- const bool issize = ( tail[0] == ',' );
+ const bool is_size = ( tail[0] == ',' );
value = getnum( tail + 1, 0, 1, INT64_MAX ); // size
- if( issize || value > range.pos() )
+ if( is_size || value > range.pos() )
{
- if( !issize ) value -= range.pos();
+ if( !is_size ) value -= range.pos();
if( INT64_MAX - range.pos() >= value ) { range.size( value ); return; }
}
}
@@ -343,23 +391,23 @@ int open_instream( const char * const name, struct stat * const in_statsp,
int open_truncable_stream( const char * const name,
struct stat * const in_statsp )
{
- int infd = open( name, O_RDWR | O_BINARY );
- if( infd < 0 )
+ int fd = open( name, O_RDWR | O_BINARY );
+ if( fd < 0 )
show_file_error( name, "Can't open input file", errno );
else
{
- const int i = fstat( infd, in_statsp );
+ const int i = fstat( fd, in_statsp );
const mode_t mode = in_statsp->st_mode;
if( i != 0 || !S_ISREG( mode ) )
{
if( verbosity >= 0 )
std::fprintf( stderr, "%s: File '%s' is not a regular file.\n",
program_name, name );
- close( infd );
- infd = -1;
+ close( fd );
+ fd = -1;
}
}
- return infd;
+ return fd;
}
@@ -421,8 +469,17 @@ bool check_tty( const char * const input_filename, const int infd,
}
+void set_signals( void (*action)(int) )
+ {
+ std::signal( SIGHUP, action );
+ std::signal( SIGINT, action );
+ std::signal( SIGTERM, action );
+ }
+
+
void cleanup_and_fail( const int retval )
{
+ set_signals( SIG_IGN ); // ignore signals
if( delete_output_on_interrupt )
{
delete_output_on_interrupt = false;
@@ -438,6 +495,13 @@ void cleanup_and_fail( const int retval )
namespace {
+extern "C" void signal_handler( int )
+ {
+ show_error( "Control-C or similar caught, quitting." );
+ cleanup_and_fail( 1 );
+ }
+
+
// Set permissions, owner and times.
void close_and_set_permissions( const struct stat * const in_statsp )
{
@@ -517,9 +581,9 @@ int decompress( const unsigned long long cfile_size, const int infd,
Range_decoder rdec( infd );
for( bool first_member = true; ; first_member = false )
{
- File_header header;
+ Lzip_header header;
rdec.reset_member_position();
- const int size = rdec.read_data( header.data, File_header::size );
+ const int size = rdec.read_data( header.data, Lzip_header::size );
if( rdec.finished() ) // End Of File
{
if( first_member )
@@ -573,30 +637,16 @@ int decompress( const unsigned long long cfile_size, const int infd,
{ std::fputs( testing ? "ok\n" : "done\n", stderr ); pp.reset(); }
}
}
- catch( std::bad_alloc ) { pp( "Not enough memory." ); retval = 1; }
- catch( Error e ) { pp(); show_error( e.msg, errno ); retval = 1; }
+ catch( std::bad_alloc & ) { pp( "Not enough memory." ); retval = 1; }
+ catch( Error & e ) { pp(); show_error( e.msg, errno ); retval = 1; }
if( verbosity == 1 && retval == 0 )
std::fputs( testing ? "ok\n" : "done\n", stderr );
return retval;
}
-
-extern "C" void signal_handler( int )
- {
- show_error( "Control-C or similar caught, quitting." );
- cleanup_and_fail( 1 );
- }
-
-
-void set_signals()
- {
- std::signal( SIGHUP, signal_handler );
- std::signal( SIGINT, signal_handler );
- std::signal( SIGTERM, signal_handler );
- }
-
} // end namespace
+void set_signal_handler() { set_signals( signal_handler ); }
int close_outstream( const struct stat * const in_statsp )
{
@@ -625,23 +675,15 @@ std::string insert_fixed( std::string name )
void show_file_error( const char * const filename, const char * const msg,
const int errcode )
{
- if( verbosity < 0 ) return;
- std::fprintf( stderr, "%s: %s: %s", program_name, filename, msg );
- if( errcode > 0 ) std::fprintf( stderr, ": %s", std::strerror( errcode ) );
- std::fputc( '\n', stderr );
- }
-
-
-void show_error2( const char * const msg1, const char * const name,
- const char * const msg2 )
- {
if( verbosity >= 0 )
- std::fprintf( stderr, "%s: %s '%s' %s\n", program_name, msg1, name, msg2 );
+ std::fprintf( stderr, "%s: %s: %s%s%s\n", program_name, filename, msg,
+ ( errcode > 0 ) ? ": " : "",
+ ( errcode > 0 ) ? std::strerror( errcode ) : "" );
}
-void show_error4( const char * const msg1, const char * const name1,
- const char * const name2, const char * const msg2 )
+void show_2file_error( const char * const msg1, const char * const name1,
+ const char * const name2, const char * const msg2 )
{
if( verbosity >= 0 )
std::fprintf( stderr, "%s: %s '%s' and '%s' %s\n",
@@ -684,6 +726,7 @@ int main( const int argc, const char * const argv[] )
{
Block range( 0, 0 );
Bad_byte bad_byte;
+ Member_list member_list;
std::string default_output_filename;
std::vector< std::string > filenames;
Mode program_mode = m_none;
@@ -695,7 +738,7 @@ int main( const int argc, const char * const argv[] )
bool to_stdout = false;
invocation_name = argv[0];
- enum { opt_dtd = 256, opt_lt, opt_rtd, opt_std };
+ enum { opt_du = 256, opt_dtd, opt_lt, opt_re, opt_rtd, opt_st, opt_std };
const Arg_parser::Option options[] =
{
{ 'a', "trailing-error", Arg_parser::no },
@@ -721,9 +764,12 @@ int main( const int argc, const char * const argv[] )
{ 'X', "show-packets", Arg_parser::maybe },
{ 'Y', "debug-delay", Arg_parser::yes },
{ 'Z', "debug-repair", Arg_parser::yes },
+ { opt_du, "dump", Arg_parser::yes },
{ opt_dtd, "dump-tdata", Arg_parser::no },
{ opt_lt, "loose-trailing", Arg_parser::no },
+ { opt_re, "remove", Arg_parser::yes },
{ opt_rtd, "remove-tdata", Arg_parser::no },
+ { opt_st, "strip", Arg_parser::yes },
{ opt_std, "strip-tdata", Arg_parser::no },
{ 0 , 0, Arg_parser::no } };
@@ -768,15 +814,24 @@ int main( const int argc, const char * const argv[] )
parse_range( arg, range ); break;
case 'Z': set_mode( program_mode, m_debug_repair );
parse_pos_value( arg, bad_byte ); break;
- case opt_dtd: set_mode( program_mode, m_dump_tdata ); break;
+ case opt_du: set_mode( program_mode, m_dump );
+ member_list.parse( arg ); break;
+ case opt_dtd: set_mode( program_mode, m_dump );
+ member_list.parse( "tdata" ); break;
case opt_lt: loose_trailing = true; break;
- case opt_rtd: set_mode( program_mode, m_remove_tdata ); break;
- case opt_std: set_mode( program_mode, m_strip_tdata ); break;
+ case opt_re: set_mode( program_mode, m_remove );
+ member_list.parse( arg ); break;
+ case opt_rtd: set_mode( program_mode, m_remove );
+ member_list.parse( "tdata" ); break;
+ case opt_st: set_mode( program_mode, m_strip );
+ member_list.parse( arg ); break;
+ case opt_std: set_mode( program_mode, m_strip );
+ member_list.parse( "tdata" ); break;
default : internal_error( "uncaught option." );
}
} // end process options
-#if defined(__MSVCRT__) || defined(__OS2__)
+#if defined(__MSVCRT__) || defined(__OS2__) || defined(__DJGPP__)
setmode( STDIN_FILENO, O_BINARY );
setmode( STDOUT_FILENO, O_BINARY );
#endif
@@ -794,6 +849,7 @@ int main( const int argc, const char * const argv[] )
if( filenames.back() != "-" ) filenames_given = true;
}
+ const char terminator = isatty( STDOUT_FILENO ) ? '\r' : '\n';
try {
switch( program_mode )
{
@@ -804,56 +860,54 @@ int main( const int argc, const char * const argv[] )
return debug_decompress( filenames[0], bad_byte, false );
case m_debug_delay:
one_file( filenames.size() );
- return debug_delay( filenames[0], range );
+ return debug_delay( filenames[0], range, terminator );
case m_debug_repair:
one_file( filenames.size() );
- return debug_repair( filenames[0], bad_byte );
+ return debug_repair( filenames[0], bad_byte, terminator );
case m_decompress: break;
- case m_dump_tdata:
- case m_strip_tdata:
+ case m_dump:
+ case m_strip:
if( filenames.size() < 1 )
{ show_error( "You must specify at least 1 file.", 0, true ); return 1; }
- if( default_output_filename.size() ) set_signals();
- return dump_tdata( filenames, default_output_filename, force,
- program_mode == m_strip_tdata, loose_trailing );
+ return dump_members( filenames, default_output_filename, member_list,
+ force, ignore_errors, ignore_trailing,
+ loose_trailing, program_mode == m_strip );
case m_list: break;
case m_merge:
if( filenames.size() < 2 )
{ show_error( "You must specify at least 2 files.", 0, true ); return 1; }
- set_signals();
- return merge_files( filenames, default_output_filename, force );
+ return merge_files( filenames, default_output_filename, force, terminator );
case m_range_dec:
one_file( filenames.size() );
- set_signals();
return range_decompress( filenames[0], default_output_filename, range,
force, ignore_errors, ignore_trailing,
loose_trailing, to_stdout );
- case m_remove_tdata:
+ case m_remove:
if( filenames.size() < 1 )
{ show_error( "You must specify at least 1 file.", 0, true ); return 1; }
- return remove_tdata( filenames, loose_trailing );
+ return remove_members( filenames, member_list, ignore_errors,
+ ignore_trailing, loose_trailing );
case m_repair:
one_file( filenames.size() );
- set_signals();
- return repair_file( filenames[0], default_output_filename, force );
+ return repair_file( filenames[0], default_output_filename, force, terminator );
case m_show_packets:
one_file( filenames.size() );
return debug_decompress( filenames[0], bad_byte, true );
case m_split:
one_file( filenames.size() );
- set_signals();
return split_file( filenames[0], default_output_filename, force );
case m_test: break;
}
}
- catch( std::bad_alloc )
+ catch( std::bad_alloc & )
{ show_error( "Not enough memory." ); cleanup_and_fail( 1 ); }
- catch( Error e ) { show_error( e.msg, errno ); cleanup_and_fail( 1 ); }
+ catch( Error & e ) { show_error( e.msg, errno ); cleanup_and_fail( 1 ); }
if( filenames.empty() ) filenames.push_back("-");
if( program_mode == m_list )
- return list_files( filenames, ignore_trailing, loose_trailing );
+ return list_files( filenames, ignore_errors, ignore_trailing,
+ loose_trailing );
if( program_mode == m_test )
outfd = -1;
@@ -862,7 +916,7 @@ int main( const int argc, const char * const argv[] )
if( !to_stdout && program_mode != m_test &&
( filenames_given || default_output_filename.size() ) )
- set_signals();
+ set_signals( signal_handler );
Pretty_print pp( filenames );
@@ -941,6 +995,12 @@ int main( const int argc, const char * const argv[] )
else
tmp = decompress( cfile_size, infd, pp, ignore_trailing,
loose_trailing, program_mode == m_test );
+ if( close( infd ) != 0 )
+ {
+ show_error( input_filename.size() ? "Error closing input file" :
+ "Error closing stdin", errno );
+ if( tmp < 1 ) tmp = 1;
+ }
if( tmp > retval ) retval = tmp;
if( tmp )
{ if( program_mode != m_test ) cleanup_and_fail( retval );
@@ -950,7 +1010,6 @@ int main( const int argc, const char * const argv[] )
close_and_set_permissions( in_statsp );
if( input_filename.size() )
{
- close( infd );
if( !keep_input_files && !to_stdout && program_mode != m_test )
std::remove( input_filename.c_str() );
}