1
0
Fork 0

Merging upstream version 1.13.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-02-21 10:13:18 +01:00
parent e51de8f895
commit a4df31fc03
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
21 changed files with 720 additions and 282 deletions

View file

@ -1,49 +1,49 @@
2011-11-20 Antonio Diaz Diaz <ant_diaz@teleline.es>
2012-02-24 Antonio Diaz Diaz <ant_diaz@teleline.es>
* Version 1.13-rc2 released.
* Added new option `-D, --range-decompress' which extracts a
range of bytes decompressing only the members containing the
desired data.
* Added new option `-l, --list' which prints correct total file
sizes and ratios even for multimember files.
* New file range_dec.cc.
* testsuite/check.sh: Do not use `ln'. `ln' doesn't work on OS/2.
2011-11-12 Antonio Diaz Diaz <ant_diaz@teleline.es>
* Version 1.13-rc1 released.
* Version 1.13 released.
* Lziprecover is now distributed in its own package. Until
version 1.12 it was included in the lzip package.
version 1.12 it was included in the lzip package. Previous
entries in this file are taken from there.
* lziprecover.cc: Renamed to main.cc.
* New files merge.cc, repair.cc, split.cc, and range_dec.cc.
* main.cc: Added decompressor options (-c, -d, -k, -t) so that
a external decompressor is not needed for recovery nor for
"make check".
* New files merge.cc, repair.cc and split.cc.
* Added new option '-D, --range-decompress' which extracts a
range of bytes decompressing only the members containing the
desired data.
* Added new option '-l, --list' which prints correct total file
sizes and ratios even for multi-member files.
* merge.cc repair.cc: Remove output file if recovery fails.
* Changed quote characters in messages as advised by GNU Standards.
* split.cc: Use Boyer-Moore algorithm to search for headers.
* testsuite/check.sh: Use `ln' instead of `cat' for input files.
* configure: 'datadir' renamed to 'datarootdir'.
2011-04-30 Antonio Diaz Diaz <ant_diaz@teleline.es>
* Version 1.12 released.
* lziprecover.cc: If `-v' is not specified show errors only.
* lziprecover.cc: If '-v' is not specified show errors only.
* testsuite/unzcrash.cc: Use Arg_parser.
* testsuite/unzcrash.cc: Added new option '-b, --bits'.
* testsuite/unzcrash.cc: Added new option '-p, --position'.
* testsuite/unzcrash.cc: Added new option '-s, --size'.
2010-09-16 Antonio Diaz Diaz <ant_diaz@teleline.es>
* Version 1.11 released.
* lziprecover.cc: Added new option `-m, --merge' which tries to
* lziprecover.cc: Added new option '-m, --merge' which tries to
produce a correct file merging the good parts of two or more
damaged copies.
* lziprecover.cc: Added new option `-R, --repair' for repairing
* lziprecover.cc: Added new option '-R, --repair' for repairing
a 1-byte error in single-member files.
* decoder.cc (decode_member): Detect file errors earlier to
improve efficiency of lziprecover's new repair capability.
This change also prevents (harmless) access to uninitialized
memory when decompressing a corrupt file.
* lziprecover.cc: Added new option `-f, --force'.
* lziprecover.cc: Added new option `-o, --output'.
* lziprecover.cc: Added new option `-s, --split' to select the
until now only operation of splitting multimember files.
* lziprecover.cc: Added new option '-f, --force'.
* lziprecover.cc: Added new option '-o, --output'.
* lziprecover.cc: Added new option '-s, --split' to select the
until now only operation of splitting multi-member files.
* lziprecover.cc: If no operation is specified, warn the user
and do nothing.
@ -56,10 +56,11 @@
2009-01-24 Antonio Diaz Diaz <ant_diaz@teleline.es>
* Version 1.4 released.
* Added `lziprecover', a member recoverer program.
* Added 'lziprecover', a member recoverer program.
* testsuite/unzcrash.cc: Test all 1-byte errors.
Copyright (C) 2009, 2010, 2011 Antonio Diaz Diaz.
Copyright (C) 2009, 2010, 2011, 2012 Antonio Diaz Diaz.
This file is a collection of facts, and thus it is not copyrightable,
but just in case, you have unlimited permission to copy, distribute and

24
INSTALL
View file

@ -18,7 +18,7 @@ This creates the directory ./lziprecover[version] containing the source
from the main archive.
2. Change to lziprecover directory and run configure.
(Try `configure --help' for usage instructions).
(Try 'configure --help' for usage instructions).
cd lziprecover[version]
./configure
@ -27,31 +27,31 @@ from the main archive.
make
4. Optionally, type `make check' to run the tests that come with
4. Optionally, type 'make check' to run the tests that come with
lziprecover.
5. Type `make install' to install the programs and any data files and
5. Type 'make install' to install the programs and any data files and
documentation.
Another way
-----------
You can also compile lziprecover into a separate directory. To do this,
you must use a version of `make' that supports the `VPATH' variable,
such as GNU `make'. `cd' to the directory where you want the object
files and executables to go and run the `configure' script. `configure'
automatically checks for the source code in `.', in `..' and in the
directory that `configure' is in.
you must use a version of 'make' that supports the 'VPATH' variable,
such as GNU 'make'. 'cd' to the directory where you want the object
files and executables to go and run the 'configure' script. 'configure'
automatically checks for the source code in '.', in '..' and in the
directory that 'configure' is in.
`configure' recognizes the option `--srcdir=DIR' to control where to
look for the sources. Usually `configure' can determine that directory
'configure' recognizes the option '--srcdir=DIR' to control where to
look for the sources. Usually 'configure' can determine that directory
automatically.
After running `configure', you can run `make' and `make install' as
After running 'configure', you can run 'make' and 'make install' as
explained above.
Copyright (C) 2009, 2010, 2011 Antonio Diaz Diaz.
Copyright (C) 2009, 2010, 2011, 2012 Antonio Diaz Diaz.
This file is free documentation: you have unlimited permission to copy,
distribute and modify it.

View file

@ -7,6 +7,7 @@ INSTALL_DIR = $(INSTALL) -d -m 755
SHELL = /bin/sh
objs = arg_parser.o decoder.o merge.o range_dec.o repair.o split.o main.o
unzobjs = arg_parser.o unzcrash.o
.PHONY : all install install-info install-man install-strip \
@ -21,9 +22,15 @@ $(progname) : $(objs)
$(progname)_profiled : $(objs)
$(CXX) $(LDFLAGS) -pg -o $@ $(objs)
unzcrash : $(unzobjs)
$(CXX) $(LDFLAGS) -o $@ $(unzobjs)
main.o : main.cc
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -DPROGVERSION=\"$(pkgversion)\" -c -o $@ $<
unzcrash.o : testsuite/unzcrash.cc
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -DPROGVERSION=\"$(pkgversion)\" -c -o $@ $<
%.o : %.cc
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $<
@ -35,6 +42,7 @@ merge.o : lzip.h decoder.h
range_dec.o : lzip.h decoder.h
repair.o : lzip.h
split.o : lzip.h
unzcrash.o : arg_parser.h Makefile
doc : info man
@ -101,6 +109,7 @@ dist : doc
$(DISTNAME)/testsuite/test921-1921.txt \
$(DISTNAME)/testsuite/test_bad[1-5].lz \
$(DISTNAME)/testsuite/test_v[01].lz \
$(DISTNAME)/testsuite/unzcrash.cc \
$(DISTNAME)/*.h \
$(DISTNAME)/*.cc
rm -f $(DISTNAME)
@ -108,6 +117,7 @@ dist : doc
clean :
-rm -f $(progname) $(progname)_profiled $(objs)
-rm -f unzcrash unzcrash.o
distclean : clean
-rm -f Makefile config.status *.tar *.tar.lz

8
NEWS
View file

@ -12,6 +12,12 @@ decompressing only the members containing the desired data, has been
added.
The new option "-l, --list" which prints correct total file sizes and
ratios even for multimember files, has been added.
ratios even for multi-member files, has been added.
"--merge" and "--repair" now remove the output file if recovery fails.
Quote characters in messages have been changed as advised by GNU Coding
Standards.
Configure option "--datadir" has been renamed to "--datarootdir" to
follow GNU Standards.

17
README
View file

@ -11,6 +11,13 @@ the compressors in the lzip family; lzip, plzip, minilzip/lzlib, clzip
and pdlzip. This recovery capability contributes to make the lzip format
one of the best options for long-term data archiving.
Lziprecover is able to efficiently extract a range of bytes from a
multi-member file, because it only decompresses the members containing
the desired data.
Lziprecover can print correct total file sizes and ratios even for
multi-member files.
When recovering data, lziprecover takes as arguments the names of the
damaged files and writes zero or more recovered files depending on the
operation selected and whether the recovery succeeded or not. The
@ -32,3 +39,13 @@ copies.
If the cause of file corruption is damaged media, the combination
GNU ddrescue + lziprecover is the best option for recovering data from
multiple damaged copies.
Copyright (C) 2009, 2010, 2011, 2012 Antonio Diaz Diaz.
This file is free documentation: you have unlimited permission to copy,
distribute and modify it.
The file Makefile.in is a data file used by configure to produce the
Makefile. It has the same copyright owner and permissions that configure
itself.

View file

@ -1,5 +1,6 @@
/* Arg_parser - POSIX/GNU command line argument parser. (C++ version)
Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 Antonio Diaz Diaz.
Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012
Antonio Diaz Diaz.
This library is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -55,30 +56,30 @@ bool Arg_parser::parse_long_option( const char * const opt, const char * const a
if( ambig && !exact )
{
error_ = "option `"; error_ += opt; error_ += "' is ambiguous";
error_ = "option '"; error_ += opt; error_ += "' is ambiguous";
return false;
}
if( index < 0 ) // nothing found
{
error_ = "unrecognized option `"; error_ += opt; error_ += '\'';
error_ = "unrecognized option '"; error_ += opt; error_ += '\'';
return false;
}
++argind;
data.push_back( Record( options[index].code ) );
if( opt[len+2] ) // `--<long_option>=<argument>' syntax
if( opt[len+2] ) // '--<long_option>=<argument>' syntax
{
if( options[index].has_arg == no )
{
error_ = "option `--"; error_ += options[index].name;
error_ = "option '--"; error_ += options[index].name;
error_ += "' doesn't allow an argument";
return false;
}
if( options[index].has_arg == yes && !opt[len+3] )
{
error_ = "option `--"; error_ += options[index].name;
error_ = "option '--"; error_ += options[index].name;
error_ += "' requires an argument";
return false;
}
@ -90,7 +91,7 @@ bool Arg_parser::parse_long_option( const char * const opt, const char * const a
{
if( !arg || !arg[0] )
{
error_ = "option `--"; error_ += options[index].name;
error_ = "option '--"; error_ += options[index].name;
error_ += "' requires an argument";
return false;
}

View file

@ -1,5 +1,6 @@
/* Arg_parser - POSIX/GNU command line argument parser. (C++ version)
Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 Antonio Diaz Diaz.
Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012
Antonio Diaz Diaz.
This library is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -25,12 +26,12 @@
Public License.
*/
/* Arg_parser reads the arguments in `argv' and creates a number of
/* Arg_parser reads the arguments in 'argv' and creates a number of
option codes, option arguments and non-option arguments.
In case of error, `error' returns a non-empty error message.
In case of error, 'error' returns a non-empty error message.
`options' is an array of `struct Option' terminated by an element
'options' is an array of 'struct Option' terminated by an element
containing a code which is zero. A null name means a short-only
option. A code value outside the unsigned char range means a
long-only option.
@ -39,13 +40,13 @@
were specified before all the non-option arguments for the purposes
of parsing, even if the user of your program intermixed option and
non-option arguments. If you want the arguments in the exact order
the user typed them, call `Arg_parser' with `in_order' = true.
the user typed them, call 'Arg_parser' with 'in_order' = true.
The argument `--' terminates all options; any following arguments are
The argument '--' terminates all options; any following arguments are
treated as non-option arguments, even if they begin with a hyphen.
The syntax for optional option arguments is `-<short_option><argument>'
(without whitespace), or `--<long_option>=<argument>'.
The syntax for optional option arguments is '-<short_option><argument>'
(without whitespace), or '--<long_option>=<argument>'.
*/
class Arg_parser
@ -65,7 +66,7 @@ private:
{
int code;
std::string argument;
Record( const int c = 0 ) : code( c ) {}
explicit Record( const int c = 0 ) : code( c ) {}
};
std::string error_;
@ -84,20 +85,20 @@ public:
Arg_parser( const char * const opt, const char * const arg,
const Option options[] );
const std::string & error() const throw() { return error_; }
const std::string & error() const { return error_; }
// The number of arguments parsed (may be different from argc)
int arguments() const throw() { return data.size(); }
int arguments() const { return data.size(); }
// If code( i ) is 0, argument( i ) is a non-option.
// Else argument( i ) is the option's argument (or empty).
int code( const int i ) const throw()
int code( const int i ) const
{
if( i >= 0 && i < arguments() ) return data[i].code;
else return 0;
}
const std::string & argument( const int i ) const throw()
const std::string & argument( const int i ) const
{
if( i >= 0 && i < arguments() ) return data[i].argument;
else return error_;

58
configure vendored
View file

@ -1,6 +1,6 @@
#! /bin/sh
# configure script for Lziprecover - Data recovery tool for lzipped files
# Copyright (C) 2009, 2010, 2011 Antonio Diaz Diaz.
# Copyright (C) 2009, 2010, 2011, 2012 Antonio Diaz Diaz.
#
# This configure script is free software: you have unlimited permission
# to copy, distribute and modify it.
@ -8,7 +8,7 @@
args=
no_create=
pkgname=lziprecover
pkgversion=1.13-rc2
pkgversion=1.13
progname=lziprecover
srctrigger=lzip.h
@ -19,10 +19,9 @@ srcdir=
prefix=/usr/local
exec_prefix='$(prefix)'
bindir='$(exec_prefix)/bin'
datadir='$(prefix)/share'
infodir='$(datadir)/info'
mandir='$(datadir)/man'
sysconfdir='$(prefix)/etc'
datarootdir='$(prefix)/share'
infodir='$(datarootdir)/info'
mandir='$(datarootdir)/man'
CXX=
CPPFLAGS=
CXXFLAGS='-Wall -W -O2'
@ -40,12 +39,12 @@ while [ -n "$1" ] ; do
# Split out the argument for options that take them
case ${option} in
*=*) optarg=`echo ${option} | sed -e 's,^[^=]*=,,'` ;;
*=*) optarg=`echo ${option} | sed -e 's,^[^=]*=,,;s,/$,,'` ;;
esac
# Process the options
case ${option} in
--help | --he* | -h)
--help | -h)
echo "Usage: configure [options]"
echo
echo "Options: [defaults in brackets]"
@ -55,42 +54,31 @@ while [ -n "$1" ] ; do
echo " --prefix=DIR install into DIR [${prefix}]"
echo " --exec-prefix=DIR base directory for arch-dependent files [${exec_prefix}]"
echo " --bindir=DIR user executables directory [${bindir}]"
echo " --datadir=DIR base directory for doc and data [${datadir}]"
echo " --datarootdir=DIR base directory for doc and data [${datarootdir}]"
echo " --infodir=DIR info files directory [${infodir}]"
echo " --mandir=DIR man pages directory [${mandir}]"
echo " --sysconfdir=DIR read-only single-machine data directory [${sysconfdir}]"
echo " CXX=COMPILER C++ compiler to use [g++]"
echo " CPPFLAGS=OPTIONS command line options for the preprocessor [${CPPFLAGS}]"
echo " CXXFLAGS=OPTIONS command line options for the C++ compiler [${CXXFLAGS}]"
echo " LDFLAGS=OPTIONS command line options for the linker [${LDFLAGS}]"
echo
exit 0 ;;
--version | --ve* | -V)
--version | -V)
echo "Configure script for ${pkgname} version ${pkgversion}"
exit 0 ;;
--srcdir* | --sr*)
srcdir=`echo ${optarg} | sed -e 's,/$,,'` ;;
--prefix* | --pr*)
prefix=`echo ${optarg} | sed -e 's,/$,,'` ;;
--exec-prefix* | --ex*)
exec_prefix=`echo ${optarg} | sed -e 's,/$,,'` ;;
--bindir* | --bi*)
bindir=`echo ${optarg} | sed -e 's,/$,,'` ;;
--datadir* | --da*)
datadir=`echo ${optarg} | sed -e 's,/$,,'` ;;
--infodir* | --inf*)
infodir=`echo ${optarg} | sed -e 's,/$,,'` ;;
--mandir* | --ma*)
mandir=`echo ${optarg} | sed -e 's,/$,,'` ;;
--sysconfdir* | --sy*)
sysconfdir=`echo ${optarg} | sed -e 's,/$,,'` ;;
--no-create | --no-c*)
no_create=yes ;;
--srcdir=*) srcdir=${optarg} ;;
--prefix=*) prefix=${optarg} ;;
--exec-prefix=*) exec_prefix=${optarg} ;;
--bindir=*) bindir=${optarg} ;;
--datarootdir=*) datarootdir=${optarg} ;;
--infodir=*) infodir=${optarg} ;;
--mandir=*) mandir=${optarg} ;;
--no-create) no_create=yes ;;
CXX=*) CXX=${optarg} ;;
CXX=*) CXX=${optarg} ;;
CPPFLAGS=*) CPPFLAGS=${optarg} ;;
CXXFLAGS=*) CXXFLAGS=${optarg} ;;
LDFLAGS=*) LDFLAGS=${optarg} ;;
LDFLAGS=*) LDFLAGS=${optarg} ;;
--* | *=* | *-*-*) ;;
*)
@ -154,10 +142,9 @@ echo "VPATH = ${srcdir}"
echo "prefix = ${prefix}"
echo "exec_prefix = ${exec_prefix}"
echo "bindir = ${bindir}"
echo "datadir = ${datadir}"
echo "datarootdir = ${datarootdir}"
echo "infodir = ${infodir}"
echo "mandir = ${mandir}"
echo "sysconfdir = ${sysconfdir}"
echo "CXX = ${CXX}"
echo "CPPFLAGS = ${CPPFLAGS}"
echo "CXXFLAGS = ${CXXFLAGS}"
@ -165,7 +152,7 @@ echo "LDFLAGS = ${LDFLAGS}"
rm -f Makefile
cat > Makefile << EOF
# Makefile for Lziprecover - Data recovery tool for lzipped files
# Copyright (C) 2009, 2010, 2011 Antonio Diaz Diaz.
# Copyright (C) 2009, 2010, 2011, 2012 Antonio Diaz Diaz.
# This file was generated automatically by configure. Do not edit.
#
# This Makefile is free software: you have unlimited permission
@ -178,10 +165,9 @@ VPATH = ${srcdir}
prefix = ${prefix}
exec_prefix = ${exec_prefix}
bindir = ${bindir}
datadir = ${datadir}
datarootdir = ${datarootdir}
infodir = ${infodir}
mandir = ${mandir}
sysconfdir = ${sysconfdir}
CXX = ${CXX}
CPPFLAGS = ${CPPFLAGS}
CXXFLAGS = ${CXXFLAGS}

View file

@ -1,5 +1,5 @@
/* Lziprecover - Data recovery tool for lzipped files
Copyright (C) 2009, 2010, 2011 Antonio Diaz Diaz.
Copyright (C) 2009, 2010, 2011, 2012 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
@ -34,7 +34,7 @@
const CRC32 crc32;
void Pretty_print::operator()( const char * const msg ) const throw()
void Pretty_print::operator()( const char * const msg ) const
{
if( verbosity_ >= 0 )
{
@ -54,7 +54,7 @@ void Pretty_print::operator()( const char * const msg ) const throw()
// Returns the number of bytes really read.
// If (returned value < size) and (errno == 0), means EOF was reached.
//
int readblock( const int fd, uint8_t * const buf, const int size ) throw()
int readblock( const int fd, uint8_t * const buf, const int size )
{
int rest = size;
errno = 0;
@ -73,7 +73,7 @@ int readblock( const int fd, uint8_t * const buf, const int size ) throw()
// Returns the number of bytes really written.
// If (returned value < size), it is always an error.
//
int writeblock( const int fd, const uint8_t * const buf, const int size ) throw()
int writeblock( const int fd, const uint8_t * const buf, const int size )
{
int rest = size;
errno = 0;
@ -82,7 +82,7 @@ int writeblock( const int fd, const uint8_t * const buf, const int size ) throw(
errno = 0;
const int n = write( fd, buf + size - rest, rest );
if( n > 0 ) rest -= n;
else if( errno && errno != EINTR && errno != EAGAIN ) break;
else if( n < 0 && errno != EINTR && errno != EAGAIN ) break;
}
return ( rest > 0 ) ? size - rest : size;
}
@ -129,21 +129,17 @@ bool LZ_decoder::verify_trailer( const Pretty_print & pp ) const
const long long member_size = range_decoder.member_position() + trailer_size;
bool error = false;
for( int i = 0; i < trailer_size && !error; ++i )
const int size = range_decoder.read( trailer.data, trailer_size );
if( size < trailer_size )
{
if( !range_decoder.finished() )
trailer.data[i] = range_decoder.get_byte();
else
error = true;
if( pp.verbosity() >= 0 )
{
error = true;
if( pp.verbosity() >= 0 )
{
pp();
std::fprintf( stderr, "Trailer truncated at trailer position %d;"
" some checks may fail.\n", i );
}
for( ; i < trailer_size; ++i ) trailer.data[i] = 0;
pp();
std::fprintf( stderr, "Trailer truncated at trailer position %d;"
" some checks may fail.\n", size );
}
for( int i = size; i < trailer_size; ++i ) trailer.data[i] = 0;
}
if( member_version == 0 ) trailer.member_size( member_size );
if( !range_decoder.code_is_zero() )
@ -297,7 +293,7 @@ int LZ_decoder::decode_member( const Pretty_print & pp )
if( pp.verbosity() >= 0 )
{
pp();
std::fprintf( stderr, "Unsupported marker code `%d'.\n", len );
std::fprintf( stderr, "Unsupported marker code '%d'.\n", len );
}
return 4;
}

View file

@ -1,5 +1,5 @@
/* Lziprecover - Data recovery tool for lzipped files
Copyright (C) 2009, 2010, 2011 Antonio Diaz Diaz.
Copyright (C) 2009, 2010, 2011, 2012 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
@ -29,8 +29,11 @@ class Range_decoder
bool read_block();
Range_decoder( const Range_decoder & ); // declared as private
void operator=( const Range_decoder & ); // declared as private
public:
Range_decoder( const int ifd )
explicit Range_decoder( const int ifd )
:
partial_member_pos( 0 ),
buffer( new uint8_t[buffer_size] ),
@ -43,12 +46,10 @@ public:
~Range_decoder() { delete[] buffer; }
bool code_is_zero() const throw() { return ( code == 0 ); }
bool code_is_zero() const { return ( code == 0 ); }
bool finished() { return pos >= stream_pos && !read_block(); }
long long member_position() const throw()
{ return partial_member_pos + pos; }
void reset_member_position() throw()
{ partial_member_pos = -pos; }
long long member_position() const { return partial_member_pos + pos; }
void reset_member_position() { partial_member_pos = -pos; }
uint8_t get_byte()
{
@ -56,6 +57,19 @@ public:
return buffer[pos++];
}
int read( uint8_t * const outbuf, const int size )
{
int rest = size;
while( rest > 0 && !finished() )
{
const int rd = std::min( rest, stream_pos - pos );
std::memcpy( outbuf + size - rest, buffer + pos, rd );
pos += rd;
rest -= rd;
}
return ( rest > 0 ) ? size - rest : size;
}
void load()
{
code = 0;
@ -176,7 +190,7 @@ class Literal_decoder
{
Bit_model bm_literal[1<<literal_context_bits][0x300];
int lstate( const int prev_byte ) const throw()
int lstate( const uint8_t prev_byte ) const
{ return ( prev_byte >> ( 8 - literal_context_bits ) ); }
public:
@ -205,18 +219,17 @@ class LZ_decoder
const int member_version;
Range_decoder & range_decoder;
long long stream_position() const throw()
{ return partial_data_pos + stream_pos; }
long long stream_position() const { return partial_data_pos + stream_pos; }
void flush_data();
bool verify_trailer( const Pretty_print & pp ) const;
uint8_t get_prev_byte() const throw()
uint8_t get_prev_byte() const
{
const int i = ( ( pos > 0 ) ? pos : buffer_size ) - 1;
return buffer[i];
}
uint8_t get_byte( const int distance ) const throw()
uint8_t get_byte( const int distance ) const
{
int i = pos - distance - 1;
if( i < 0 ) i += buffer_size;
@ -246,6 +259,9 @@ class LZ_decoder
}
}
LZ_decoder( const LZ_decoder & ); // declared as private
void operator=( const LZ_decoder & ); // declared as private
public:
LZ_decoder( const File_header & header, Range_decoder & rdec, const int ofd,
const long long oskip = 0, const long long oend = LLONG_MAX )
@ -266,10 +282,9 @@ public:
~LZ_decoder() { delete[] buffer; }
uint32_t crc() const throw() { return crc_ ^ 0xFFFFFFFFU; }
uint32_t crc() const { return crc_ ^ 0xFFFFFFFFU; }
long long data_position() const throw()
{ return partial_data_pos + pos; }
long long data_position() const { return partial_data_pos + pos; }
int decode_member( const Pretty_print & pp );
};

View file

@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.37.1.
.TH LZIPRECOVER "1" "November 2011" "Lziprecover 1.13-rc2" "User Commands"
.TH LZIPRECOVER "1" "February 2012" "Lziprecover 1.13" "User Commands"
.SH NAME
Lziprecover \- recovers data from damaged lzip files
.SH SYNOPSIS
@ -46,7 +46,7 @@ suppress all messages
try to repair a small error in file
.TP
\fB\-s\fR, \fB\-\-split\fR
split multimember file in single\-member files
split multi\-member file in single\-member files
.TP
\fB\-t\fR, \fB\-\-test\fR
test compressed file integrity
@ -61,7 +61,7 @@ Report bugs to lzip\-bug@nongnu.org
.br
Lziprecover home page: http://www.nongnu.org/lzip/lziprecover.html
.SH COPYRIGHT
Copyright \(co 2011 Antonio Diaz Diaz.
Copyright \(co 2012 Antonio Diaz Diaz.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
.br
This is free software: you are free to change and redistribute it.

View file

@ -12,7 +12,7 @@ File: lziprecover.info, Node: Top, Next: Introduction, Up: (dir)
Lziprecover Manual
******************
This manual is for Lziprecover (version 1.13-rc2, 20 November 2011).
This manual is for Lziprecover (version 1.13, 24 February 2012).
* Menu:
@ -24,7 +24,7 @@ This manual is for Lziprecover (version 1.13-rc2, 20 November 2011).
* Concept Index:: Index of concepts
Copyright (C) 2009, 2010, 2011 Antonio Diaz Diaz.
Copyright (C) 2009, 2010, 2011, 2012 Antonio Diaz Diaz.
This manual is free documentation: you have unlimited permission to
copy, distribute and modify it.
@ -46,6 +46,13 @@ the compressors in the lzip family; lzip, plzip, minilzip/lzlib, clzip
and pdlzip. This recovery capability contributes to make the lzip format
one of the best options for long-term data archiving.
Lziprecover is able to efficiently extract a range of bytes from a
multi-member file, because it only decompresses the members containing
the desired data.
Lziprecover can print correct total file sizes and ratios even for
multi-member files.
When recovering data, lziprecover takes as arguments the names of the
damaged files and writes zero or more recovered files depending on the
operation selected and whether the recovery succeeded or not. The
@ -111,7 +118,7 @@ The format for running lziprecover is:
`--output' option is used. In order to guarantee the correctness
of the data produced, all members containing any part of the
desired data are decompressed and their integrity is verified.
This operation is more efficient in multimember files because it
This operation is more efficient in multi-member files because it
only decompresses the members containing the desired data.
`-f'
@ -125,7 +132,7 @@ The format for running lziprecover is:
`-l'
`--list'
Print total file sizes and ratios. The values produced are correct
even for multimember files.
even for multi-member files.
`-m'
`--merge'
@ -266,7 +273,7 @@ additional information before, between, or after them.
`Member size (8 bytes)'
Total size of the member, including header and trailer. This
facilitates safe recovery of undamaged members from multimember
facilitates safe recovery of undamaged members from multi-member
files.
@ -291,13 +298,13 @@ show status.
Example 3: Decompress `file.lz' partially until 10KiB of decompressed
data are produced.
lziprecover -cd file.lz | dd bs=1024 count=10
lziprecover -D 10KiB file.lz
Example 4: Decompress `file.lz' partially from decompressed byte 10000
to decompressed byte 15000 (5000 bytes are produced).
lziprecover -cd file.lz | dd bs=1000 skip=10 count=5
lziprecover -D 10000-15000 file.lz
Example 5: Repair a one-byte corruption in the single-member file
@ -317,8 +324,9 @@ the integrity of the resulting files.
lziprecover -tv rec*file.lz
Example 7: Recover a compressed backup from two copies on CD-ROM (see
the GNU ddrescue manual for details about ddrescue)
Example 7: Recover a compressed backup from two copies on CD-ROM with
error-checked merging of copies (*Note GNU ddrescue manual:
(ddrescue)Top, for details about ddrescue).
ddrescue -b2048 /dev/cdrom cdimage1 logfile1
mount -t iso9660 -o loop,ro cdimage1 /mnt/cdimage
@ -391,12 +399,17 @@ Concept Index

Tag Table:
Node: Top231
Node: Introduction898
Node: Invoking Lziprecover2684
Node: File Format7727
Node: Examples9733
Ref: ddrescue-example10986
Node: Problems12765
Node: Concept Index13315
Node: Introduction900
Node: Invoking Lziprecover2937
Node: File Format7982
Node: Examples9989
Ref: ddrescue-example11207
Node: Problems13038
Node: Concept Index13588

End Tag Table

Local Variables:
coding: iso-8859-15
End:

View file

@ -1,12 +1,13 @@
\input texinfo @c -*-texinfo-*-
@c %**start of header
@setfilename lziprecover.info
@documentencoding ISO-8859-15
@settitle Lziprecover Manual
@finalout
@c %**end of header
@set UPDATED 20 November 2011
@set VERSION 1.13-rc2
@set UPDATED 24 February 2012
@set VERSION 1.13
@dircategory Data Compression
@direntry
@ -43,7 +44,7 @@ This manual is for Lziprecover (version @value{VERSION}, @value{UPDATED}).
@end menu
@sp 1
Copyright @copyright{} 2009, 2010, 2011 Antonio Diaz Diaz.
Copyright @copyright{} 2009, 2010, 2011, 2012 Antonio Diaz Diaz.
This manual is free documentation: you have unlimited permission
to copy, distribute and modify it.
@ -64,6 +65,13 @@ the compressors in the lzip family; lzip, plzip, minilzip/lzlib, clzip
and pdlzip. This recovery capability contributes to make the lzip format
one of the best options for long-term data archiving.
Lziprecover is able to efficiently extract a range of bytes from a
multi-member file, because it only decompresses the members containing
the desired data.
Lziprecover can print correct total file sizes and ratios even for
multi-member files.
When recovering data, lziprecover takes as arguments the names of the
damaged files and writes zero or more recovered files depending on the
operation selected and whether the recovery succeeded or not. The
@ -133,7 +141,7 @@ produced bytes are sent to standard output unless the @samp{--output}
option is used. In order to guarantee the correctness of the data
produced, all members containing any part of the desired data are
decompressed and their integrity is verified. This operation is more
efficient in multimember files because it only decompresses the members
efficient in multi-member files because it only decompresses the members
containing the desired data.
@item -f
@ -147,7 +155,7 @@ Keep (don't delete) input files during decompression.
@item -l
@itemx --list
Print total file sizes and ratios. The values produced are correct even
for multimember files.
for multi-member files.
@item -m
@itemx --merge
@ -295,7 +303,7 @@ Size of the uncompressed original data.
@item Member size (8 bytes)
Total size of the member, including header and trailer. This facilitates
safe recovery of undamaged members from multimember files.
safe recovery of undamaged members from multi-member files.
@end table
@ -327,7 +335,7 @@ Example 3: Decompress @samp{file.lz} partially until 10KiB of
decompressed data are produced.
@example
lziprecover -cd file.lz | dd bs=1024 count=10
lziprecover -D 10KiB file.lz
@end example
@sp 1
@ -336,7 +344,7 @@ Example 4: Decompress @samp{file.lz} partially from decompressed byte
10000 to decompressed byte 15000 (5000 bytes are produced).
@example
lziprecover -cd file.lz | dd bs=1000 skip=10 count=5
lziprecover -D 10000-15000 file.lz
@end example
@sp 1
@ -365,8 +373,16 @@ lziprecover -tv rec*file.lz
@sp 1
@anchor{ddrescue-example}
@noindent
Example 7: Recover a compressed backup from two copies on CD-ROM (see
the GNU ddrescue manual for details about ddrescue)
Example 7: Recover a compressed backup from two copies on CD-ROM with
error-checked merging of copies
@ifnothtml
(@xref{Top,GNU ddrescue manual,,ddrescue},
@end ifnothtml
@ifhtml
(See the
@uref{http://www.gnu.org/software/ddrescue/manual/ddrescue_manual.html,,ddrescue manual}
@end ifhtml
for details about ddrescue).
@example
ddrescue -b2048 /dev/cdrom cdimage1 logfile1

102
lzip.h
View file

@ -1,5 +1,5 @@
/* Lziprecover - Data recovery tool for lzipped files
Copyright (C) 2009, 2010, 2011 Antonio Diaz Diaz.
Copyright (C) 2009, 2010, 2011, 2012 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
@ -21,32 +21,32 @@ class State
public:
enum { states = 12 };
State() throw() : st( 0 ) {}
unsigned char operator()() const throw() { return st; }
bool is_char() const throw() { return st < 7; }
State() : st( 0 ) {}
unsigned char operator()() const { return st; }
bool is_char() const { return st < 7; }
void set_char() throw()
void set_char()
{
static const unsigned char next[states] =
{ 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 4, 5 };
st = next[st];
}
void set_match() throw()
void set_match()
{
static const unsigned char next[states] =
{ 7, 7, 7, 7, 7, 7, 7, 10, 10, 10, 10, 10 };
st = next[st];
}
void set_rep() throw()
void set_rep()
{
static const unsigned char next[states] =
{ 8, 8, 8, 8, 8, 8, 8, 11, 11, 11, 11, 11 };
st = next[st];
}
void set_short_rep() throw()
void set_short_rep()
{
static const unsigned char next[states] =
{ 9, 9, 9, 9, 9, 9, 9, 11, 11, 11, 11, 11 };
@ -87,7 +87,7 @@ enum {
max_dis_states = 4 };
inline int get_dis_state( int len ) throw()
inline int get_dis_state( int len )
{
len -= min_match_len;
if( len >= max_dis_states ) len = max_dis_states - 1;
@ -102,7 +102,7 @@ enum { bit_model_move_bits = 5,
struct Bit_model
{
unsigned int probability;
Bit_model() throw() : probability( bit_model_total / 2 ) {}
Bit_model() : probability( bit_model_total / 2 ) {}
};
@ -145,10 +145,10 @@ public:
first_post = true;
}
void reset() const throw() { if( name_.size() ) first_post = true; }
const char * name() const throw() { return name_.c_str(); }
int verbosity() const throw() { return verbosity_; }
void operator()( const char * const msg = 0 ) const throw();
void reset() const { if( name_.size() ) first_post = true; }
const char * name() const { return name_.c_str(); }
int verbosity() const { return verbosity_; }
void operator()( const char * const msg = 0 ) const;
};
@ -168,10 +168,10 @@ public:
}
}
uint32_t operator[]( const uint8_t byte ) const throw() { return data[byte]; }
void update( uint32_t & crc, const uint8_t byte ) const throw()
uint32_t operator[]( const uint8_t byte ) const { return data[byte]; }
void update( uint32_t & crc, const uint8_t byte ) const
{ crc = data[(crc^byte)&0xFF] ^ ( crc >> 8 ); }
void update( uint32_t & crc, const uint8_t * const buffer, const int size ) const throw()
void update( uint32_t & crc, const uint8_t * const buffer, const int size ) const
{
for( int i = 0; i < size; ++i )
crc = data[(crc^buffer[i])&0xFF] ^ ( crc >> 8 );
@ -181,14 +181,15 @@ public:
extern const CRC32 crc32;
inline int real_bits( const int value ) throw()
inline int real_bits( const unsigned int value )
{
int bits = 0;
for( int i = 1, mask = 1; mask > 0; ++i, mask <<= 1 )
if( value & mask ) bits = i;
int bits = 0, i = 1;
unsigned int mask = 1;
for( ; mask > 0; ++i, mask <<= 1 ) if( value & mask ) bits = i;
return bits;
}
const uint8_t magic_string[4] = { 'L', 'Z', 'I', 'P' };
struct File_header
@ -198,16 +199,14 @@ struct File_header
// 5 coded_dict_size
enum { size = 6 };
void set_magic() throw()
{ std::memcpy( data, magic_string, 4 ); data[4] = 1; }
bool verify_magic() const throw()
void set_magic() { std::memcpy( data, magic_string, 4 ); data[4] = 1; }
bool verify_magic() const
{ return ( std::memcmp( data, magic_string, 4 ) == 0 ); }
uint8_t version() const throw() { return data[4]; }
bool verify_version() const throw() { return ( data[4] <= 1 ); }
uint8_t version() const { return data[4]; }
bool verify_version() const { return ( data[4] <= 1 ); }
int dictionary_size() const throw()
int dictionary_size() const
{
int sz = ( 1 << ( data[5] & 0x1F ) );
if( sz > min_dictionary_size && sz <= max_dictionary_size )
@ -215,7 +214,7 @@ struct File_header
return sz;
}
bool dictionary_size( const int sz ) throw()
bool dictionary_size( const int sz )
{
if( sz >= min_dictionary_size && sz <= max_dictionary_size )
{
@ -244,36 +243,36 @@ struct File_trailer
static int size( const int version = 1 )
{ return ( ( version >= 1 ) ? 20 : 12 ); }
uint32_t data_crc() const throw()
uint32_t data_crc() const
{
uint32_t tmp = 0;
for( int i = 3; i >= 0; --i ) { tmp <<= 8; tmp += data[i]; }
return tmp;
}
void data_crc( uint32_t crc ) throw()
void data_crc( uint32_t crc )
{ for( int i = 0; i <= 3; ++i ) { data[i] = (uint8_t)crc; crc >>= 8; } }
long long data_size() const throw()
long long data_size() const
{
long long tmp = 0;
for( int i = 11; i >= 4; --i ) { tmp <<= 8; tmp += data[i]; }
return tmp;
}
void data_size( long long sz ) throw()
void data_size( long long sz )
{
for( int i = 4; i <= 11; ++i ) { data[i] = (uint8_t)sz; sz >>= 8; }
}
long long member_size() const throw()
long long member_size() const
{
long long tmp = 0;
for( int i = 19; i >= 12; --i ) { tmp <<= 8; tmp += data[i]; }
return tmp;
}
void member_size( long long sz ) throw()
void member_size( long long sz )
{
for( int i = 12; i <= 19; ++i ) { data[i] = (uint8_t)sz; sz >>= 8; }
}
@ -283,7 +282,7 @@ struct File_trailer
struct Error
{
const char * const msg;
Error( const char * const s ) throw() : msg( s ) {}
explicit Error( const char * const s ) : msg( s ) {}
};
@ -303,41 +302,40 @@ class Block
long long pos_, size_; // pos + size <= LLONG_MAX
public:
Block( const long long p, const long long s ) throw()
: pos_( p ), size_( s ) {}
Block( const long long p, const long long s ) : pos_( p ), size_( s ) {}
long long pos() const throw() { return pos_; }
long long size() const throw() { return size_; }
long long end() const throw() { return pos_ + size_; }
long long pos() const { return pos_; }
long long size() const { return size_; }
long long end() const { return pos_ + size_; }
void pos( const long long p ) throw() { pos_ = p; }
void size( const long long s ) throw() { size_ = s; }
void pos( const long long p ) { pos_ = p; }
void size( const long long s ) { size_ = s; }
bool overlaps( const Block & b ) const throw()
bool overlaps( const Block & b ) const
{ return ( pos_ < b.end() && b.pos_ < end() ); }
void shift( Block & b ) throw() { ++size_; ++b.pos_; --b.size_; }
void shift( Block & b ) { ++size_; ++b.pos_; --b.size_; }
};
// defined in decoder.cc
int readblock( const int fd, uint8_t * const buf, const int size ) throw();
int writeblock( const int fd, const uint8_t * const buf, const int size ) throw();
int readblock( const int fd, uint8_t * const buf, const int size );
int writeblock( const int fd, const uint8_t * const buf, const int size );
// defined in main.cc
extern int verbosity;
const char * format_num( long long num, long long limit = LLONG_MAX,
const int set_prefix = 0 ) throw();
const int set_prefix = 0 );
int open_instream( const std::string & name, struct stat * const in_statsp,
const bool to_stdout, const bool reg_only = false ) throw();
const bool to_stdout, const bool reg_only = false );
int open_outstream_rw( const std::string & output_filename,
const bool force ) throw();
const bool force );
void show_error( const char * const msg, const int errcode = 0,
const bool help = false ) throw();
const bool help = false );
void internal_error( const char * const msg );
// defined in merge.cc
void cleanup_and_fail( const std::string & output_filename,
const int outfd, const int retval ) throw();
const int outfd, const int retval );
bool copy_file( const int infd, const int outfd,
const long long size = LLONG_MAX );
bool try_decompress( const int fd, const long long file_size,

90
main.cc
View file

@ -1,5 +1,5 @@
/* Lziprecover - Data recovery tool for lzipped files
Copyright (C) 2009, 2010, 2011 Antonio Diaz Diaz.
Copyright (C) 2009, 2010, 2011, 2012 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,6 +38,10 @@
#include <utime.h>
#include <sys/stat.h>
#if defined(__MSVCRT__)
#define fchmod(x,y) 0
#define fchown(x,y,z) 0
#define SIGHUP SIGTERM
#define S_ISSOCK(x) 0
#define S_IRGRP 0
#define S_IWGRP 0
#define S_IROTH 0
@ -57,7 +61,7 @@ namespace {
const char * const Program_name = "Lziprecover";
const char * const program_name = "lziprecover";
const char * const program_year = "2011";
const char * const program_year = "2012";
const char * invocation_name = 0;
#ifdef O_BINARY
@ -82,7 +86,7 @@ mode_t outfd_mode = usr_rw;
bool delete_output_on_interrupt = false;
void show_help() throw()
void show_help()
{
std::printf( "%s - Data recovery tool and decompressor for lzipped files.\n", Program_name );
std::printf( "\nUsage: %s [options] [files]\n", invocation_name );
@ -101,7 +105,7 @@ void show_help() throw()
" -q, --quiet suppress all messages\n"
// " -r, --recover correct errors in file using a recover file\n"
" -R, --repair try to repair a small error in file\n"
" -s, --split split multimember file in single-member files\n"
" -s, --split split multi-member file in single-member files\n"
" -t, --test test compressed file integrity\n"
// " -u, --update convert file from version 0 to version 1\n"
" -v, --verbose be verbose (a 2nd -v gives more)\n"
@ -112,7 +116,7 @@ void show_help() throw()
}
void show_version() throw()
void show_version()
{
std::printf( "%s %s\n", Program_name, PROGVERSION );
std::printf( "Copyright (C) %s Antonio Diaz Diaz.\n", program_year );
@ -122,7 +126,7 @@ void show_version() throw()
}
void one_file( const int argind, const int arguments ) throw()
void one_file( const int argind, const int arguments )
{
if( argind + 1 != arguments )
{
@ -132,7 +136,7 @@ void one_file( const int argind, const int arguments ) throw()
}
void set_mode( Mode & program_mode, const Mode new_mode ) throw()
void set_mode( Mode & program_mode, const Mode new_mode )
{
if( program_mode != m_none && program_mode != new_mode )
{
@ -143,7 +147,7 @@ void set_mode( Mode & program_mode, const Mode new_mode ) throw()
}
int extension_index( const std::string & name ) throw()
int extension_index( const std::string & name )
{
for( int i = 0; known_extensions[i].from; ++i )
{
@ -156,7 +160,7 @@ int extension_index( const std::string & name ) throw()
}
void set_d_outname( const std::string & name, const int i ) throw()
void set_d_outname( const std::string & name, const int i )
{
if( i >= 0 )
{
@ -170,12 +174,12 @@ void set_d_outname( const std::string & name, const int i ) throw()
}
output_filename = name; output_filename += ".out";
if( verbosity >= 1 )
std::fprintf( stderr, "%s: Can't guess original name for `%s' -- using `%s'.\n",
std::fprintf( stderr, "%s: Can't guess original name for '%s' -- using '%s'.\n",
program_name, name.c_str(), output_filename.c_str() );
}
bool open_outstream( const bool force ) throw()
bool open_outstream( const bool force )
{
int flags = O_CREAT | O_WRONLY | o_binary;
if( force ) flags |= O_TRUNC; else flags |= O_EXCL;
@ -184,17 +188,17 @@ bool open_outstream( const bool force ) throw()
if( outfd < 0 && verbosity >= 0 )
{
if( errno == EEXIST )
std::fprintf( stderr, "%s: Output file `%s' already exists, skipping.\n",
std::fprintf( stderr, "%s: Output file '%s' already exists, skipping.\n",
program_name, output_filename.c_str() );
else
std::fprintf( stderr, "%s: Can't create output file `%s': %s.\n",
std::fprintf( stderr, "%s: Can't create output file '%s': %s.\n",
program_name, output_filename.c_str(), std::strerror( errno ) );
}
return ( outfd >= 0 );
}
bool check_tty( const int infd ) throw()
bool check_tty( const int infd )
{
if( isatty( infd ) )
{
@ -205,13 +209,13 @@ bool check_tty( const int infd ) throw()
}
void cleanup_and_fail( const int retval ) throw()
void cleanup_and_fail( const int retval )
{
if( delete_output_on_interrupt )
{
delete_output_on_interrupt = false;
if( verbosity >= 0 )
std::fprintf( stderr, "%s: Deleting output file `%s', if it exists.\n",
std::fprintf( stderr, "%s: Deleting output file '%s', if it exists.\n",
program_name, output_filename.c_str() );
if( outfd >= 0 ) { close( outfd ); outfd = -1; }
if( std::remove( output_filename.c_str() ) != 0 && errno != ENOENT )
@ -247,7 +251,7 @@ void close_and_set_permissions( const struct stat * const in_statsp )
}
std::string insert_fixed( std::string name ) throw()
std::string insert_fixed( std::string name )
{
if( name.size() > 4 && name.compare( name.size() - 4, 4, ".tlz" ) == 0 )
name.insert( name.size() - 4, "_fixed" );
@ -258,7 +262,7 @@ std::string insert_fixed( std::string name ) throw()
}
unsigned char xdigit( const int value ) throw()
unsigned char xdigit( const int value )
{
if( value >= 0 && value <= 9 ) return '0' + value;
if( value >= 10 && value <= 15 ) return 'A' + value - 10;
@ -267,7 +271,7 @@ unsigned char xdigit( const int value ) throw()
void show_trailing_garbage( const uint8_t * const data, const int size,
const Pretty_print & pp, const bool all ) throw()
const Pretty_print & pp, const bool all )
{
std::string garbage_msg;
if( !all ) garbage_msg = "first bytes of ";
@ -277,7 +281,7 @@ void show_trailing_garbage( const uint8_t * const data, const int size,
if( !std::isprint( data[i] ) ) { text = false; break; }
if( text )
{
garbage_msg += '`';
garbage_msg += '\'';
garbage_msg.append( (const char *)data, size );
garbage_msg += '\'';
}
@ -304,10 +308,8 @@ int decompress( const int infd, const Pretty_print & pp, const bool testing )
for( bool first_member = true; ; first_member = false, pp.reset() )
{
File_header header;
int size;
rdec.reset_member_position();
for( size = 0; size < File_header::size && !rdec.finished(); ++size )
header.data[size] = rdec.get_byte();
const int size = rdec.read( header.data, File_header::size );
if( rdec.finished() ) // End Of File
{
if( first_member )
@ -344,8 +346,8 @@ int decompress( const int infd, const Pretty_print & pp, const bool testing )
header.version(),
format_num( header.dictionary_size(), 9999, -1 ) );
}
LZ_decoder decoder( header, rdec, outfd );
LZ_decoder decoder( header, rdec, outfd );
const int result = decoder.decode_member( pp );
partial_file_pos += rdec.member_position();
if( result != 0 )
@ -380,14 +382,14 @@ int decompress( const int infd, const Pretty_print & pp, const bool testing )
}
extern "C" void signal_handler( int ) throw()
extern "C" void signal_handler( int )
{
show_error( "Control-C or similar caught, quitting." );
cleanup_and_fail( 1 );
}
void set_signals() throw()
void set_signals()
{
std::signal( SIGHUP, signal_handler );
std::signal( SIGINT, signal_handler );
@ -401,7 +403,7 @@ int verbosity = 0;
const char * format_num( long long num, long long limit,
const int set_prefix ) throw()
const int set_prefix )
{
const char * const si_prefix[8] =
{ "k", "M", "G", "T", "P", "E", "Z", "Y" };
@ -425,13 +427,13 @@ const char * format_num( long long num, long long limit,
int open_instream( const std::string & name, struct stat * const in_statsp,
const bool to_stdout, const bool reg_only ) throw()
const bool to_stdout, const bool reg_only )
{
int infd = open( name.c_str(), O_RDONLY | o_binary );
if( infd < 0 )
{
if( verbosity >= 0 )
std::fprintf( stderr, "%s: Can't open input file `%s': %s.\n",
std::fprintf( stderr, "%s: Can't open input file '%s': %s.\n",
program_name, name.c_str(), std::strerror( errno ) );
}
else
@ -444,10 +446,10 @@ int open_instream( const std::string & name, struct stat * const in_statsp,
if( i != 0 || ( !S_ISREG( mode ) && ( !to_stdout || !can_read ) ) )
{
if( verbosity >= 0 )
std::fprintf( stderr, "%s: Input file `%s' is not a regular file%s.\n",
std::fprintf( stderr, "%s: Input file '%s' is not a regular file%s.\n",
program_name, name.c_str(),
( can_read && !to_stdout ) ?
" and `--stdout' was not specified" : "" );
" and '--stdout' was not specified" : "" );
close( infd );
infd = -1;
}
@ -457,7 +459,7 @@ int open_instream( const std::string & name, struct stat * const in_statsp,
int open_outstream_rw( const std::string & output_filename,
const bool force ) throw()
const bool force )
{
int flags = O_CREAT | O_RDWR | o_binary;
if( force ) flags |= O_TRUNC; else flags |= O_EXCL;
@ -466,18 +468,18 @@ int open_outstream_rw( const std::string & output_filename,
if( outfd < 0 && verbosity >= 0 )
{
if( errno == EEXIST )
std::fprintf( stderr, "%s: Output file `%s' already exists."
" Use `--force' to overwrite it.\n",
std::fprintf( stderr, "%s: Output file '%s' already exists."
" Use '--force' to overwrite it.\n",
program_name, output_filename.c_str() );
else
std::fprintf( stderr, "%s: Can't create output file `%s': %s.\n",
std::fprintf( stderr, "%s: Can't create output file '%s': %s.\n",
program_name, output_filename.c_str(), std::strerror( errno ) );
}
return outfd;
}
void show_error( const char * const msg, const int errcode, const bool help ) throw()
void show_error( const char * const msg, const int errcode, const bool help )
{
if( verbosity >= 0 )
{
@ -489,7 +491,7 @@ void show_error( const char * const msg, const int errcode, const bool help ) th
std::fprintf( stderr, "\n" );
}
if( help && invocation_name && invocation_name[0] )
std::fprintf( stderr, "Try `%s --help' for more information.\n",
std::fprintf( stderr, "Try '%s --help' for more information.\n",
invocation_name );
}
}
@ -567,6 +569,11 @@ int main( const int argc, const char * const argv[] )
}
} // end process options
#if defined(__MSVCRT__) || defined(__OS2__)
_fsetmode( stdin, "b" );
_fsetmode( stdout, "b" );
#endif
if( program_mode == m_none )
{
show_error( "You must specify the operation to be performed.", 0, true );
@ -607,6 +614,11 @@ int main( const int argc, const char * const argv[] )
case m_test: break;
}
if( program_mode == m_test )
outfd = -1;
else if( program_mode != m_decompress )
internal_error( "invalid decompressor operation" );
bool filenames_given = false;
for( ; argind < parser.arguments(); ++argind )
{
@ -620,10 +632,6 @@ int main( const int argc, const char * const argv[] )
set_signals();
Pretty_print pp( filenames, verbosity );
if( program_mode == m_test )
outfd = -1;
else if( program_mode != m_decompress )
internal_error( "invalid decompressor operation" );
int retval = 0;
for( unsigned int i = 0; i < filenames.size(); ++i )

View file

@ -1,5 +1,5 @@
/* Lziprecover - Data recovery tool for lzipped files
Copyright (C) 2009, 2010, 2011 Antonio Diaz Diaz.
Copyright (C) 2009, 2010, 2011, 2012 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
@ -102,7 +102,7 @@ bool copy_and_diff_file( const std::vector< int > & infd_vector,
}
int ipow( const unsigned int base, const unsigned int exponent ) throw()
int ipow( const unsigned int base, const unsigned int exponent )
{
int result = 1;
for( unsigned int i = 0; i < exponent; ++i )
@ -142,13 +142,14 @@ int open_input_files( const std::vector< std::string > & filenames,
if( tmp < 0 )
{
if( verbosity >= 0 )
std::fprintf( stderr, "File `%s' is not seekable.\n", filenames[i].c_str() );
std::fprintf( stderr, "File '%s' is not seekable.\n", filenames[i].c_str() );
return 1;
}
if( i == 0 )
{
isize = tmp;
if( isize < min_member_size ) { show_error( "Input file is too short." ); return 2; }
if( isize < min_member_size )
{ show_error( "Input file is too short." ); return 2; }
}
else if( isize != tmp )
{ show_error( "Sizes of input files are different." ); return 1; }
@ -165,7 +166,7 @@ int open_input_files( const std::vector< std::string > & filenames,
if( try_decompress( infd_vector[i], isize ) )
{
if( verbosity >= 1 )
std::printf( "File `%s' has no errors. Recovery is not needed.\n",
std::printf( "File '%s' has no errors. Recovery is not needed.\n",
filenames[i].c_str() );
return 0;
}
@ -179,7 +180,7 @@ int open_input_files( const std::vector< std::string > & filenames,
void cleanup_and_fail( const std::string & output_filename,
const int outfd, const int retval ) throw()
const int outfd, const int retval )
{
if( outfd >= 0 ) close( outfd );
if( std::remove( output_filename.c_str() ) != 0 && errno != ENOENT )

View file

@ -1,5 +1,5 @@
/* Lziprecover - Data recovery tool for lzipped files
Copyright (C) 2009, 2010, 2011 Antonio Diaz Diaz.
Copyright (C) 2009, 2010, 2011, 2012 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
@ -44,15 +44,15 @@ public:
const long long mp, const long long ms )
: dblock_( dp, ds ), mblock_( mp, ms ) {}
const Block & dblock() const throw() { return dblock_; }
Block & dblock() throw() { return dblock_; }
const Block & mblock() const throw() { return mblock_; }
Block & mblock() throw() { return mblock_; }
const Block & dblock() const { return dblock_; }
Block & dblock() { return dblock_; }
const Block & mblock() const { return mblock_; }
Block & mblock() { return mblock_; }
};
int seek_read( const int fd, uint8_t * const buf, const int size,
const long long pos ) throw()
const long long pos )
{
if( lseek( fd, pos, SEEK_SET ) == pos )
return readblock( fd, buf, size );
@ -95,30 +95,28 @@ public:
member_vector[i+1].dblock().pos( member_vector[i].dblock().end() );
}
long long data_end() const throw()
long long data_end() const
{ if( member_vector.size() ) return member_vector.back().dblock().end();
else return 0; }
const Member & member( const int i ) const throw()
{ return member_vector[i]; }
const Block & dblock( const int i ) const throw()
const Member & member( const int i ) const { return member_vector[i]; }
const Block & dblock( const int i ) const
{ return member_vector[i].dblock(); }
const Block & mblock( const int i ) const throw()
const Block & mblock( const int i ) const
{ return member_vector[i].mblock(); }
int members() const throw() { return (int)member_vector.size(); }
int members() const { return (int)member_vector.size(); }
};
// Returns the number of chars read, or 0 if error.
//
int parse_long_long( const char * const ptr, long long & value ) throw()
int parse_long_long( const char * const ptr, long long & value )
{
char * tail;
int c = 0;
errno = 0;
value = strtoll( ptr, &tail, 0 );
if( tail == ptr || errno ) return 0;
c = tail - ptr;
int c = tail - ptr;
if( ptr[c] )
{
@ -133,8 +131,8 @@ int parse_long_long( const char * const ptr, long long & value ) throw()
case 'T': exponent = 4; break;
case 'G': exponent = 3; break;
case 'M': exponent = 2; break;
case 'K': if( factor == 1024 ) exponent = 1; break;
case 'k': if( factor == 1000 ) exponent = 1; break;
case 'K': if( factor == 1024 ) exponent = 1; else return 0; break;
case 'k': if( factor == 1000 ) exponent = 1; else return 0; break;
}
if( exponent > 0 )
{
@ -154,7 +152,7 @@ int parse_long_long( const char * const ptr, long long & value ) throw()
// Recognized formats: <begin> <begin>-<end> <begin>,<size>
//
void parse_range( const char * const ptr, Block & range ) throw()
void parse_range( const char * const ptr, Block & range )
{
long long value = 0;
int c = parse_long_long( ptr, value ); // pos
@ -176,7 +174,7 @@ void parse_range( const char * const ptr, Block & range ) throw()
}
bool safe_seek( const int fd, const long long pos ) throw()
bool safe_seek( const int fd, const long long pos )
{
if( lseek( fd, pos, SEEK_SET ) == pos ) return true;
show_error( "Seek error", errno ); return false;

View file

@ -1,5 +1,5 @@
/* Lziprecover - Data recovery tool for lzipped files
Copyright (C) 2009, 2010, 2011 Antonio Diaz Diaz.
Copyright (C) 2009, 2010, 2011, 2012 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

View file

@ -1,5 +1,5 @@
/* Lziprecover - Data recovery tool for lzipped files
Copyright (C) 2009, 2010, 2011 Antonio Diaz Diaz.
Copyright (C) 2009, 2010, 2011, 2012 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
@ -62,7 +62,7 @@ bool next_filename( std::string & output_filename )
// Search forward from 'pos' for "LZIP" (Boyer-Moore algorithm)
// Return pos of found string or 'pos+size' if not found.
//
int find_magic( const uint8_t * const buffer, const int pos, const int size ) throw()
int find_magic( const uint8_t * const buffer, const int pos, const int size )
{
const unsigned char table[256] = {
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,

View file

@ -1,6 +1,6 @@
#! /bin/sh
# check script for Lziprecover - Data recovery tool for lzipped files
# Copyright (C) 2009, 2010, 2011 Antonio Diaz Diaz.
# Copyright (C) 2009, 2010, 2011, 2012 Antonio Diaz Diaz.
#
# This script is free software: you have unlimited permission
# to copy, distribute and modify it.
@ -9,11 +9,12 @@ LC_ALL=C
export LC_ALL
objdir=`pwd`
testdir=`cd "$1" ; pwd`
LZIP="${objdir}"/lziprecover
LZIPRECOVER="${objdir}"/lziprecover
framework_failure() { echo "failure in testing framework" ; exit 1 ; }
if [ ! -x "${LZIPRECOVER}" ] ; then
echo "${LZIPRECOVER}: cannot execute"
if [ ! -x "${LZIP}" ] ; then
echo "${LZIP}: cannot execute"
exit 1
fi
@ -40,7 +41,19 @@ fail=0
printf "testing lziprecover-%s..." "$2"
"${LZIPRECOVER}" -D 921-1921 -o copy ${in_lz} || fail=1
"${LZIP}" -t "${testdir}"/test_v0.lz || fail=1
printf .
"${LZIP}" -cd "${testdir}"/test_v0.lz > copy || fail=1
cmp ${in} copy || fail=1
printf .
"${LZIP}" -t "${testdir}"/test_v1.lz || fail=1
printf .
"${LZIP}" -cd "${testdir}"/test_v1.lz > copy || fail=1
cmp ${in} copy || fail=1
printf .
"${LZIPRECOVER}" -D 921-1921 -fo copy ${in_lz} || fail=1
cmp ${inD} copy || fail=1
printf .
"${LZIPRECOVER}" -D 921,1000 ${in_lz} > copy || fail=1
@ -102,6 +115,11 @@ for i in 1 2 3 ; do
printf .
done
cat ${in_lz} > anyothername || framework_failure
"${LZIP}" -d anyothername || fail=1
cmp ${in} anyothername.out || fail=1
printf .
echo
if [ ${fail} = 0 ] ; then
echo "tests completed successfully."

353
testsuite/unzcrash.cc Normal file
View file

@ -0,0 +1,353 @@
/* Unzcrash - A test program written to test robustness to
decompression of corrupted data.
Inspired by unzcrash.c from Julian Seward's bzip2.
Copyright (C) 2008, 2009, 2010, 2011, 2012 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 3 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.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cerrno>
#include <climits>
#include <csignal>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <vector>
#include <stdint.h>
#include <unistd.h>
#include "../arg_parser.h"
#if CHAR_BIT != 8
#error "Environments where CHAR_BIT != 8 are not supported."
#endif
#ifndef LLONG_MAX
#define LLONG_MAX 0x7FFFFFFFFFFFFFFFLL
#endif
#ifndef LLONG_MIN
#define LLONG_MIN (-LLONG_MAX - 1LL)
#endif
#ifndef ULLONG_MAX
#define ULLONG_MAX 0xFFFFFFFFFFFFFFFFULL
#endif
namespace {
const char * const Program_name = "Unzcrash";
const char * const program_name = "unzcrash";
const char * const program_year = "2012";
const char * invocation_name = 0;
int verbosity = 0;
void show_help()
{
std::printf( "%s - A test program written to test robustness to\n", Program_name );
std::printf( "decompression of corrupted data.\n"
"\nUsage: %s [options] \"lzip -tv\" filename.lz\n", invocation_name );
std::printf( "\nThis program reads the specified file and then repeatedly decompresses\n"
"it, increasing 256 times each byte of the compressed data, so as to test\n"
"all possible one-byte errors. This should not cause any invalid memory\n"
"accesses. If it does, please, report it as a bug.\n"
"\nOptions:\n"
" -h, --help display this help and exit\n"
" -V, --version output version information and exit\n"
" -b, --bits=<n>[,<n>]... test <n>-bit errors instead of full byte\n"
" -p, --position=<bytes> first byte position to test\n"
" -q, --quiet suppress all messages\n"
" -s, --size=<bytes> number of byte positions to test\n"
" -v, --verbose be verbose (a 2nd -v gives more)\n"
"\nReport bugs to lzip-bug@nongnu.org\n"
"Lzip home page: http://www.nongnu.org/lzip/lzip.html\n" );
}
void show_version()
{
std::printf( "%s %s\n", Program_name, PROGVERSION );
std::printf( "Copyright (C) %s Antonio Diaz Diaz.\n", program_year );
std::printf( "License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n"
"This is free software: you are free to change and redistribute it.\n"
"There is NO WARRANTY, to the extent permitted by law.\n" );
}
void show_error( const char * const msg, const int errcode = 0,
const bool help = false )
{
if( verbosity >= 0 )
{
if( msg && msg[0] )
{
std::fprintf( stderr, "%s: %s", program_name, msg );
if( errcode > 0 )
std::fprintf( stderr, ": %s", std::strerror( errcode ) );
std::fprintf( stderr, "\n" );
}
if( help && invocation_name && invocation_name[0] )
std::fprintf( stderr, "Try '%s --help' for more information.\n",
invocation_name );
}
}
void internal_error( const char * const msg )
{
if( verbosity >= 0 )
std::fprintf( stderr, "%s: internal error: %s.\n", program_name, msg );
std::exit( 3 );
}
long long getnum( const char * const ptr,
const long long llimit = LLONG_MIN + 1,
const long long ulimit = LLONG_MAX )
{
errno = 0;
char *tail;
long long result = strtoll( ptr, &tail, 0 );
if( tail == ptr )
{
show_error( "Bad or missing numerical argument.", 0, true );
std::exit( 1 );
}
if( !errno && tail[0] )
{
int factor = ( tail[1] == 'i' ) ? 1024 : 1000;
int exponent = 0;
bool bad_multiplier = false;
switch( tail[0] )
{
case ' ': break;
case 'Y': exponent = 8; break;
case 'Z': exponent = 7; break;
case 'E': exponent = 6; break;
case 'P': exponent = 5; break;
case 'T': exponent = 4; break;
case 'G': exponent = 3; break;
case 'M': exponent = 2; break;
case 'K': if( factor == 1024 ) exponent = 1; else bad_multiplier = true;
break;
case 'k': if( factor == 1000 ) exponent = 1; else bad_multiplier = true;
break;
default : bad_multiplier = true;
}
if( bad_multiplier )
{
show_error( "Bad multiplier in numerical argument.", 0, true );
std::exit( 1 );
}
for( int i = 0; i < exponent; ++i )
{
if( LLONG_MAX / factor >= llabs( result ) ) result *= factor;
else { errno = ERANGE; break; }
}
}
if( !errno && ( result < llimit || result > ulimit ) ) errno = ERANGE;
if( errno )
{
show_error( "Numerical argument out of limits." );
std::exit( 1 );
}
return result;
}
class Bitset8 // 8 value bitset (1..8)
{
bool data[8];
static bool valid_digit( const unsigned char ch )
{ return ( ch >= '1' && ch <= '8' ); }
public:
Bitset8() { for( int i = 0; i < 8; ++i ) data[i] = true; }
bool includes( const int i ) const
{ return ( i >= 1 && i <= 8 && data[i-1] ); }
// Recognized formats: 1 1,2,3 1-4 1,3-5,8
bool parse( const char * p )
{
for( int i = 0; i < 8; ++i ) data[i] = false;
while( true )
{
const unsigned char ch1 = *p++;
if( !valid_digit( ch1 ) ) break;
if( *p != '-' ) data[ch1-'1'] = true;
else
{
++p;
if( !valid_digit( *p ) || ch1 > *p ) break;
for( int c = ch1; c <= *p; ++c ) data[c-'1'] = true;
++p;
}
if( *p == 0 ) return true;
if( *p == ',' ) ++p; else break;
}
show_error( "Invalid value or range." );
return false;
}
// number of n-bit errors per byte (n=0..8): 1 8 28 56 70 56 28 8 1
void print() const
{
std::fflush( stderr );
int c = 0;
for( int i = 0; i < 8; ++i ) if( data[i] ) ++c;
if( c == 8 ) std::printf( "Testing full byte.\n" );
else if( c == 0 ) std::printf( "Nothing to test.\n" );
else
{
std::printf( "Testing " );
for( int i = 0; i < 8; ++i )
if( data[i] )
{
std::printf( "%d", i + 1 );
if( --c ) std::printf( "," );
}
std::printf( " bit errors.\n" );
}
std::fflush( stdout );
}
};
int differing_bits( const uint8_t byte1, const uint8_t byte2 )
{
int count = 0;
uint8_t dif = byte1 ^ byte2;
while( dif )
{ count += ( dif & 1 ); dif >>= 1; }
return count;
}
} // end namespace
int main( const int argc, const char * const argv[] )
{
enum { buffer_size = 3 << 20 };
Bitset8 bits; // if Bitset8::parse not called test full byte
int pos = 0;
int max_size = buffer_size;
invocation_name = argv[0];
const Arg_parser::Option options[] =
{
{ 'h', "help", Arg_parser::no },
{ 'b', "bits", Arg_parser::yes },
{ 'p', "position", Arg_parser::yes },
{ 'q', "quiet", Arg_parser::no },
{ 's', "size", Arg_parser::yes },
{ 'v', "verbose", Arg_parser::no },
{ 'V', "version", Arg_parser::no },
{ 0 , 0, Arg_parser::no } };
const Arg_parser parser( argc, argv, options );
if( parser.error().size() ) // bad option
{ show_error( parser.error().c_str(), 0, true ); return 1; }
int argind = 0;
for( ; argind < parser.arguments(); ++argind )
{
const int code = parser.code( argind );
if( !code ) break; // no more options
const char * const arg = parser.argument( argind ).c_str();
switch( code )
{
case 'h': show_help(); return 0;
case 'b': if( !bits.parse( arg ) ) return 1; break;
case 'p': pos = getnum( arg, 0, buffer_size - 1 ); break;
case 'q': verbosity = -1; break;
case 's': max_size = getnum( arg, 1, buffer_size ); break;
case 'v': if( verbosity < 4 ) ++verbosity; break;
case 'V': show_version(); return 0;
default : internal_error( "uncaught option" );
}
} // end process options
if( argind + 2 != parser.arguments() )
{
if( verbosity >= 0 )
std::fprintf( stderr, "Usage: %s \"lzip -tv\" filename.lz\n",
invocation_name );
return 1;
}
FILE *f = std::fopen( parser.argument( argind + 1 ).c_str(), "rb" );
if( !f )
{
if( verbosity >= 0 )
std::fprintf( stderr, "Can't open file '%s' for reading\n",
parser.argument( argind + 1 ).c_str() );
return 1;
}
uint8_t * const buffer = new uint8_t[buffer_size];
const int size = std::fread( buffer, 1, buffer_size, f );
if( size >= buffer_size )
{
if( verbosity >= 0 )
std::fprintf( stderr, "input file '%s' is too big.\n",
parser.argument( argind + 1 ).c_str() );
return 1;
}
std::fclose( f );
f = popen( parser.argument( argind ).c_str(), "w" );
if( !f )
{ show_error( "Can't open pipe", errno ); return 1; }
const int wr = std::fwrite( buffer, 1, size, f );
if( wr != size || pclose( f ) != 0 )
{
if( verbosity >= 0 )
std::fprintf( stderr, "Could not run '%s' : %s.\n",
parser.argument( argind ).c_str(), std::strerror( errno ) );
return 1;
}
std::signal( SIGPIPE, SIG_IGN );
if( verbosity >= 1 ) bits.print();
const int end = ( ( pos + max_size < size ) ? pos + max_size : size );
for( int i = pos; i < end; ++i )
{
if( verbosity >= 0 )
std::fprintf( stderr, "byte %d\n", i );
const uint8_t byte = buffer[i];
for( int j = 0; j < 255; ++j )
{
++buffer[i];
if( bits.includes( differing_bits( byte, buffer[i] ) ) )
{
f = popen( parser.argument( argind ).c_str(), "w" );
if( !f )
{ show_error( "Can't open pipe", errno ); return 1; }
std::fwrite( buffer, 1, size, f );
if( pclose( f ) == 0 && verbosity >= 0 )
std::fprintf( stderr, "0x%02X (0x%02X+0x%02X) passed the test\n",
buffer[i], byte, j + 1 );
}
}
buffer[i] = byte;
}
delete[] buffer;
return 0;
}