1
0
Fork 0

Adding upstream version 1.17~pre1.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-02-21 11:23:10 +01:00
parent 15503a1dcd
commit c230441360
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
17 changed files with 419 additions and 186 deletions

View file

@ -1,3 +1,11 @@
2014-10-16 Antonio Diaz Diaz <antonio@gnu.org>
* Version 1.17-pre1 released.
* merge.cc: New block selection algorithm makes merge much faster.
* Makefile.in: Added new targets 'install*-compress'.
* testsuite/unzcrash.cc: Moved to top directory.
* Added chapter 'File names' to the manual.
2014-08-29 Antonio Diaz Diaz <antonio@gnu.org> 2014-08-29 Antonio Diaz Diaz <antonio@gnu.org>
* Version 1.16 released. * Version 1.16 released.

View file

@ -32,6 +32,10 @@ the main archive.
5. Type 'make install' to install the program and any data files and 5. Type 'make install' to install the program and any data files and
documentation. documentation.
Or type 'make install-compress', which additionally compresses the
info manual and the man page after installation. (Installing
compressed docs may become the default in the future).
You can install only the program, the info manual or the man page by You can install only the program, the info manual or the man page by
typing 'make install-bin', 'make install-info' or 'make install-man' typing 'make install-bin', 'make install-info' or 'make install-man'
respectively. respectively.

View file

@ -11,7 +11,9 @@ objs = arg_parser.o file_index.o merge.o mtester.o range_dec.o repair.o \
unzobjs = arg_parser.o unzcrash.o unzobjs = arg_parser.o unzcrash.o
.PHONY : all install install-bin install-info install-man install-strip \ .PHONY : all install install-bin install-info install-man \
install-strip install-compress install-strip-compress \
install-bin-strip install-info-compress install-man-compress \
install-as-lzip uninstall uninstall-bin uninstall-info uninstall-man \ install-as-lzip uninstall uninstall-bin uninstall-info uninstall-man \
doc info man check dist clean distclean doc info man check dist clean distclean
@ -20,16 +22,13 @@ all : $(progname)
$(progname) : $(objs) $(progname) : $(objs)
$(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $(objs) $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $(objs)
$(progname)_profiled : $(objs)
$(CXX) $(CXXFLAGS) $(LDFLAGS) -pg -o $@ $(objs)
unzcrash : $(unzobjs) unzcrash : $(unzobjs)
$(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $(unzobjs) $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $(unzobjs)
main.o : main.cc main.o : main.cc
$(CXX) $(CXXFLAGS) $(CPPFLAGS) -DPROGVERSION=\"$(pkgversion)\" -c -o $@ $< $(CXX) $(CXXFLAGS) $(CPPFLAGS) -DPROGVERSION=\"$(pkgversion)\" -c -o $@ $<
unzcrash.o : testsuite/unzcrash.cc unzcrash.o : unzcrash.cc
$(CXX) $(CXXFLAGS) $(CPPFLAGS) -DPROGVERSION=\"$(pkgversion)\" -c -o $@ $< $(CXX) $(CXXFLAGS) $(CPPFLAGS) -DPROGVERSION=\"$(pkgversion)\" -c -o $@ $<
%.o : %.cc %.o : %.cc
@ -67,38 +66,49 @@ check : all
@$(VPATH)/testsuite/check.sh $(VPATH)/testsuite $(pkgversion) @$(VPATH)/testsuite/check.sh $(VPATH)/testsuite $(pkgversion)
install : install-bin install-info install-man install : install-bin install-info install-man
install-strip : install-bin-strip install-info install-man
install-compress : install-bin install-info-compress install-man-compress
install-strip-compress : install-bin-strip install-info-compress install-man-compress
install-bin : all install-bin : all
if [ ! -d "$(DESTDIR)$(bindir)" ] ; then $(INSTALL_DIR) "$(DESTDIR)$(bindir)" ; fi if [ ! -d "$(DESTDIR)$(bindir)" ] ; then $(INSTALL_DIR) "$(DESTDIR)$(bindir)" ; fi
$(INSTALL_PROGRAM) ./$(progname) "$(DESTDIR)$(bindir)/$(progname)" $(INSTALL_PROGRAM) ./$(progname) "$(DESTDIR)$(bindir)/$(progname)"
install-bin-strip : all
$(MAKE) INSTALL_PROGRAM='$(INSTALL_PROGRAM) -s' install-bin
install-info : install-info :
if [ ! -d "$(DESTDIR)$(infodir)" ] ; then $(INSTALL_DIR) "$(DESTDIR)$(infodir)" ; fi if [ ! -d "$(DESTDIR)$(infodir)" ] ; then $(INSTALL_DIR) "$(DESTDIR)$(infodir)" ; fi
-rm -f "$(DESTDIR)$(infodir)/$(pkgname).info"*
$(INSTALL_DATA) $(VPATH)/doc/$(pkgname).info "$(DESTDIR)$(infodir)/$(pkgname).info" $(INSTALL_DATA) $(VPATH)/doc/$(pkgname).info "$(DESTDIR)$(infodir)/$(pkgname).info"
-install-info --info-dir="$(DESTDIR)$(infodir)" "$(DESTDIR)$(infodir)/$(pkgname).info" -install-info --info-dir="$(DESTDIR)$(infodir)" "$(DESTDIR)$(infodir)/$(pkgname).info"
install-info-compress : install-info
lzip -v -9 "$(DESTDIR)$(infodir)/$(pkgname).info"
install-man : install-man :
if [ ! -d "$(DESTDIR)$(mandir)/man1" ] ; then $(INSTALL_DIR) "$(DESTDIR)$(mandir)/man1" ; fi if [ ! -d "$(DESTDIR)$(mandir)/man1" ] ; then $(INSTALL_DIR) "$(DESTDIR)$(mandir)/man1" ; fi
-rm -f "$(DESTDIR)$(mandir)/man1/$(progname).1"*
$(INSTALL_DATA) $(VPATH)/doc/$(progname).1 "$(DESTDIR)$(mandir)/man1/$(progname).1" $(INSTALL_DATA) $(VPATH)/doc/$(progname).1 "$(DESTDIR)$(mandir)/man1/$(progname).1"
install-strip : all install-man-compress : install-man
$(MAKE) INSTALL_PROGRAM='$(INSTALL_PROGRAM) -s' install lzip -v -9 "$(DESTDIR)$(mandir)/man1/$(progname).1"
install-as-lzip : install install-as-lzip : install
-rm -f "$(DESTDIR)$(bindir)/lzip" -rm -f "$(DESTDIR)$(bindir)/lzip"
cd "$(DESTDIR)$(bindir)" && ln -s $(progname) lzip cd "$(DESTDIR)$(bindir)" && ln -s $(progname) lzip
uninstall : uninstall-bin uninstall-info uninstall-man uninstall : uninstall-man uninstall-info uninstall-bin
uninstall-bin : uninstall-bin :
-rm -f "$(DESTDIR)$(bindir)/$(progname)" -rm -f "$(DESTDIR)$(bindir)/$(progname)"
uninstall-info : uninstall-info :
-install-info --info-dir="$(DESTDIR)$(infodir)" --remove "$(DESTDIR)$(infodir)/$(pkgname).info" -install-info --info-dir="$(DESTDIR)$(infodir)" --remove "$(DESTDIR)$(infodir)/$(pkgname).info"
-rm -f "$(DESTDIR)$(infodir)/$(pkgname).info" -rm -f "$(DESTDIR)$(infodir)/$(pkgname).info"*
uninstall-man : uninstall-man :
-rm -f "$(DESTDIR)$(mandir)/man1/$(progname).1" -rm -f "$(DESTDIR)$(mandir)/man1/$(progname).1"*
dist : doc dist : doc
ln -sf $(VPATH) $(DISTNAME) ln -sf $(VPATH) $(DISTNAME)
@ -122,14 +132,13 @@ dist : doc
$(DISTNAME)/testsuite/test.txt.lz \ $(DISTNAME)/testsuite/test.txt.lz \
$(DISTNAME)/testsuite/test21723.txt \ $(DISTNAME)/testsuite/test21723.txt \
$(DISTNAME)/testsuite/test_bad[1-5].lz \ $(DISTNAME)/testsuite/test_bad[1-5].lz \
$(DISTNAME)/testsuite/unzcrash.cc \
$(DISTNAME)/*.h \ $(DISTNAME)/*.h \
$(DISTNAME)/*.cc $(DISTNAME)/*.cc
rm -f $(DISTNAME) rm -f $(DISTNAME)
lzip -v -9 $(DISTNAME).tar lzip -v -9 $(DISTNAME).tar
clean : clean :
-rm -f $(progname) $(progname)_profiled $(objs) -rm -f $(progname) $(objs)
-rm -f unzcrash unzcrash.o -rm -f unzcrash unzcrash.o
distclean : clean distclean : clean

21
NEWS
View file

@ -1,14 +1,13 @@
Changes in version 1.16: Changes in version 1.17:
Repairing of single-byte errors is now about 10 times faster depending Merging files now uses an algorithm similar to the ones used to solve
on file size and position of error. the "Master Mind" game, which makes it much faster. Up to 2 orders of
magnitude faster depending on number of files and number of errors.
Please, report as a bug any files correctly merged by lziprecover 1.16
that this version can't merge.
Copying of file dates, permissions, and ownership now behaves like "cp -p". The targets "install-compress", "install-strip-compress",
(If the user ID or the group ID can't be duplicated, the file permission "install-info-compress" and "install-man-compress" have been added to
bits S_ISUID and S_ISGID are cleared). the Makefile.
Some minor improvements have been made. The chapter "File names" has been added to the manual.
"lziprecover.texinfo" has been renamed to "lziprecover.texi".
The license has been changed to GPL version 2 or later.

17
README
View file

@ -2,11 +2,13 @@ Description
Lziprecover is a data recovery tool and decompressor for files in the Lziprecover is a data recovery tool and decompressor for files in the
lzip compressed data format (.lz), able to repair slightly damaged lzip compressed data format (.lz), able to repair slightly damaged
files, recover badly damaged files from two or more copies, extract data files, produce a correct file by merging the good parts of two or more
from damaged files, decompress files and test integrity of files. damaged copies, extract data from damaged files, decompress files and
test integrity of files.
The lzip file format is designed for long-term data archiving, taking The lzip file format is designed for data sharing and long-term
into account both data integrity and decoder availability: archiving, taking into account both data integrity and decoder
availability:
* The lzip format provides very safe integrity checking and some data * The lzip format provides very safe integrity checking and some data
recovery means. The lziprecover program can repair bit-flip errors recovery means. The lziprecover program can repair bit-flip errors
@ -21,8 +23,8 @@ into account both data integrity and decoder availability:
extract the data from a lzip file long after quantum computers extract the data from a lzip file long after quantum computers
eventually render LZMA obsolete. eventually render LZMA obsolete.
* Additionally lzip is copylefted, which guarantees that it will * Additionally the lzip reference implementation is copylefted, which
remain free forever. guarantees that it will remain free forever.
A nice feature of the lzip format is that a corrupt byte is easier to A nice feature of the lzip format is that a corrupt byte is easier to
repair the nearer it is from the beginning of the file. Therefore, with repair the nearer it is from the beginning of the file. Therefore, with
@ -61,7 +63,8 @@ of them with one damaged area affecting 1 percent of the copy, the
probability of obtaining a correct file is about 98 percent. With three probability of obtaining a correct file is about 98 percent. With three
such copies the probability rises to 99.97 percent. For large files (a such copies the probability rises to 99.97 percent. For large files (a
few MB) with small errors (one sector damaged per copy), the probability few MB) with small errors (one sector damaged per copy), the probability
approaches 100 percent even with only two copies. approaches 100 percent even with only two copies. (Supposing that the
errors are randomly located inside each copy).
Lziprecover is not a replacement for regular backups, but a last line of Lziprecover is not a replacement for regular backups, but a last line of
defense for the case where the backups are also damaged. defense for the case where the backups are also damaged.

2
configure vendored
View file

@ -6,7 +6,7 @@
# to copy, distribute and modify it. # to copy, distribute and modify it.
pkgname=lziprecover pkgname=lziprecover
pkgversion=1.16 pkgversion=1.17-pre1
progname=lziprecover progname=lziprecover
srctrigger=doc/${pkgname}.texi srctrigger=doc/${pkgname}.texi

View file

@ -60,8 +60,7 @@ long readblock( const int fd, uint8_t * const buf, const long size )
errno = 0; errno = 0;
while( sz < size ) while( sz < size )
{ {
const int psz = std::min( 65536L, size - sz ); const int n = read( fd, buf + sz, std::min( 1L << 20, size - sz ) );
const int n = read( fd, buf + sz, psz );
if( n > 0 ) sz += n; if( n > 0 ) sz += n;
else if( n == 0 ) break; // EOF else if( n == 0 ) break; // EOF
else if( errno != EINTR ) break; else if( errno != EINTR ) break;

View file

@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.46.1. .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.46.1.
.TH LZIPRECOVER "1" "August 2014" "lziprecover 1.16" "User Commands" .TH LZIPRECOVER "1" "October 2014" "lziprecover 1.17-pre1" "User Commands"
.SH NAME .SH NAME
lziprecover \- recovers data from damaged lzip files lziprecover \- recovers data from damaged lzip files
.SH SYNOPSIS .SH SYNOPSIS
@ -7,6 +7,13 @@ lziprecover \- recovers data from damaged lzip files
[\fI\,options\/\fR] [\fI\,files\/\fR] [\fI\,options\/\fR] [\fI\,files\/\fR]
.SH DESCRIPTION .SH DESCRIPTION
Lziprecover \- Data recovery tool and decompressor for the lzip format. Lziprecover \- Data recovery tool and decompressor for the lzip format.
Lziprecover can repair perfectly most files with small errors (up to one
single\-byte error per member), without the need of any extra redundance
at all. Losing an entire archive just because of a corrupt byte near the
beginning is a thing of the past.
Lziprecover can also produce a correct file by merging the good parts of
two or more damaged copies, extract data from damaged files, decompress
files and test integrity of files.
.SH OPTIONS .SH OPTIONS
.TP .TP
\fB\-h\fR, \fB\-\-help\fR \fB\-h\fR, \fB\-\-help\fR

View file

@ -12,7 +12,7 @@ File: lziprecover.info, Node: Top, Next: Introduction, Up: (dir)
Lziprecover Manual Lziprecover Manual
****************** ******************
This manual is for Lziprecover (version 1.16, 29 August 2014). This manual is for Lziprecover (version 1.17-pre1, 16 October 2014).
* Menu: * Menu:
@ -20,6 +20,7 @@ This manual is for Lziprecover (version 1.16, 29 August 2014).
* Invoking lziprecover:: Command line interface * Invoking lziprecover:: Command line interface
* Repairing files:: Fixing bit-flip and similar errors * Repairing files:: Fixing bit-flip and similar errors
* Merging files:: Fixing several damaged copies * Merging files:: Fixing several damaged copies
* File names:: Names of the files produced by lziprecover
* File format:: Detailed format of the compressed file * File format:: Detailed format of the compressed file
* Examples:: A small tutorial with examples * Examples:: A small tutorial with examples
* Unzcrash:: Testing the robustness of decompressors * Unzcrash:: Testing the robustness of decompressors
@ -40,11 +41,13 @@ File: lziprecover.info, Node: Introduction, Next: Invoking lziprecover, Prev:
Lziprecover is a data recovery tool and decompressor for files in the Lziprecover is a data recovery tool and decompressor for files in the
lzip compressed data format (.lz), able to repair slightly damaged lzip compressed data format (.lz), able to repair slightly damaged
files, recover badly damaged files from two or more copies, extract data files, produce a correct file by merging the good parts of two or more
from damaged files, decompress files and test integrity of files. damaged copies, extract data from damaged files, decompress files and
test integrity of files.
The lzip file format is designed for long-term data archiving, taking The lzip file format is designed for data sharing and long-term
into account both data integrity and decoder availability: archiving, taking into account both data integrity and decoder
availability:
* The lzip format provides very safe integrity checking and some data * The lzip format provides very safe integrity checking and some data
recovery means. The lziprecover program can repair bit-flip errors recovery means. The lziprecover program can repair bit-flip errors
@ -59,8 +62,8 @@ into account both data integrity and decoder availability:
archaeologist to extract the data from a lzip file long after archaeologist to extract the data from a lzip file long after
quantum computers eventually render LZMA obsolete. quantum computers eventually render LZMA obsolete.
* Additionally lzip is copylefted, which guarantees that it will * Additionally the lzip reference implementation is copylefted, which
remain free forever. guarantees that it will remain free forever.
A nice feature of the lzip format is that a corrupt byte is easier to A nice feature of the lzip format is that a corrupt byte is easier to
repair the nearer it is from the beginning of the file. Therefore, with repair the nearer it is from the beginning of the file. Therefore, with
@ -168,11 +171,12 @@ The format for running lziprecover is:
'-m' '-m'
'--merge' '--merge'
Try to produce a correct file merging the good parts of two or more Try to produce a correct file by merging the good parts of two or
damaged copies. If successful, a repaired copy is written to the more damaged copies. If successful, a repaired copy is written to
file 'FILE_fixed.lz'. The exit status is 0 if a correct file could the file 'FILE_fixed.lz'. The exit status is 0 if a correct file
be produced, 2 otherwise. See the chapter 'Merging files' (*note could be produced, 2 otherwise. See the chapter 'Merging files'
Merging files::) for a complete description of the merge mode. (*note Merging files::) for a complete description of the merge
mode.
'-o FILE' '-o FILE'
'--output=FILE' '--output=FILE'
@ -203,11 +207,11 @@ The format for running lziprecover is:
undamaged, and try to repair or partially decompress those which undamaged, and try to repair or partially decompress those which
are damaged. are damaged.
The names of the files produced are in the form 'rec01FILE.lz', The names of the files produced are in the form 'rec01FILE',
'rec02FILE.lz', etc, and are designed so that the use of wildcards 'rec02FILE', etc, and are designed so that the use of wildcards in
in subsequent processing, for example, subsequent processing, for example,
'lziprecover -cd rec*FILE.lz > recovered_data', processes the 'lziprecover -cd rec*FILE > recovered_data', processes the files
files in the correct order. The number of digits used in the names in the correct order. The number of digits used in the names
varies depending on the number of members in 'FILE'. varies depending on the number of members in 'FILE'.
'-t' '-t'
@ -253,17 +257,26 @@ File: lziprecover.info, Node: Repairing files, Next: Merging files, Prev: Inv
3 Repairing files 3 Repairing files
***************** *****************
Lziprecover is usually able to repair files with small errors (up to one Lziprecover can repair perfectly most files with small errors (up to one
byte error per member). The error may be located anywhere in the file single-byte error per member), without the need of any extra redundance
except in the header (first 6 bytes of each member) or in the 'Member at all. If the reparation is successful, the repaired file will be
size' field of the trailer (last 8 bytes of each member). This makes identical bit for bit to the original.
lzip files resistant to bit-flip, one of the most common forms of data
corruption. The error may be located anywhere in the file except in the header
(first 6 bytes of each member) or in the 'Member size' field of the
trailer (last 8 bytes of each member). This makes lzip files resistant
to bit-flip, one of the most common forms of data corruption.
Bit-flip happens when one bit in the file is changed from 0 to 1 or Bit-flip happens when one bit in the file is changed from 0 to 1 or
vice versa. It may be caused by bad RAM or even by natural radiation. I vice versa. It may be caused by bad RAM or even by natural radiation. I
have seen a case of bit-flip in a file stored on an USB flash drive. have seen a case of bit-flip in a file stored on an USB flash drive.
One byte may seem small, but most file corruptions not produced by
I/O errors just affect one byte, or even one bit, of the file. Also,
unlike magnetic media, where errors usually affect a whole sector,
solid-state storage devices tend to produce single-byte errors, making
of lzip the perfect format for data stored on such devices.
Repairing a file can take some time. Small files or files with the Repairing a file can take some time. Small files or files with the
error located near the beginning can be repaired in a few seconds. But error located near the beginning can be repaired in a few seconds. But
repairing a large file compressed with a large dictionary size and with repairing a large file compressed with a large dictionary size and with
@ -274,14 +287,14 @@ cause much more loss of data than errors located near the end. So
lziprecover repairs more efficiently the worst errors. lziprecover repairs more efficiently the worst errors.
 
File: lziprecover.info, Node: Merging files, Next: File format, Prev: Repairing files, Up: Top File: lziprecover.info, Node: Merging files, Next: File names, Prev: Repairing files, Up: Top
4 Merging files 4 Merging files
*************** ***************
If you have several copies of a file but all of them are too damaged to If you have several copies of a file but all of them are too damaged to
repair them (*note Repairing files::), lziprecover can try to produce a repair them (*note Repairing files::), lziprecover can try to produce a
correct file merging the good parts of the damaged copies. correct file by merging the good parts of the damaged copies.
The merge may succeed even if some copies of the file have all the The merge may succeed even if some copies of the file have all the
headers and trailers damaged, as long as there is at least one copy of headers and trailers damaged, as long as there is at least one copy of
@ -293,14 +306,14 @@ is damaged in all copies), or are adjacent and the boundary can't be
determined, or if the copies have too many damaged areas. determined, or if the copies have too many damaged areas.
All the copies must have the same size. If some of them have been All the copies must have the same size. If some of them have been
truncated and are therefore smaller than they should, you can extend truncated and are therefore smaller than they should, they can be
them to the correct size with the following command before merging them extended to the correct size with the following command before merging
with the other copies: them with the other copies:
ddrescue --extend-outfile=<correct_size> small_file.lz extended_file.lz ddrescue --extend-outfile=<correct_size> small_file.lz extended_file.lz
If some of the copies have got garbage data at the end and are If some of the copies have got garbage data at the end and are
therefore larger than they should, you can reduce their sizes to the therefore larger than they should, their sizes can be reduced to the
correct value with the following command before merging them with the correct value with the following command before merging them with the
other copies: other copies:
@ -311,12 +324,24 @@ each of them with one damaged area affecting 1 percent of the copy, the
probability of obtaining a correct file is about 98 percent. With three probability of obtaining a correct file is about 98 percent. With three
such copies the probability rises to 99.97 percent. For large files (a such copies the probability rises to 99.97 percent. For large files (a
few MB) with small errors (one sector damaged per copy), the probability few MB) with small errors (one sector damaged per copy), the probability
approaches 100 percent even with only two copies. approaches 100 percent even with only two copies. (Supposing that the
errors are randomly located inside each copy).
 
File: lziprecover.info, Node: File format, Next: Examples, Prev: Merging files, Up: Top File: lziprecover.info, Node: File names, Next: File format, Prev: Merging files, Up: Top
5 File format 5 Names of the files produced by lziprecover
********************************************
The name of the fixed file produced by '--merge' and '--repair' is made
by appending the string '_fixed.lz' to the original file name. If the
original file name ends with one of the extensions '.tar.lz', '.lz' or
'.tlz', the string '_fixed' is inserted before the extension.

File: lziprecover.info, Node: File format, Next: Examples, Prev: File names, Up: Top
6 File format
************* *************
Perfection is reached, not when there is no longer anything to add, but Perfection is reached, not when there is no longer anything to add, but
@ -389,7 +414,7 @@ additional information before, between, or after them.
 
File: lziprecover.info, Node: Examples, Next: Unzcrash, Prev: File format, Up: Top File: lziprecover.info, Node: Examples, Next: Unzcrash, Prev: File format, Up: Top
6 A small tutorial with examples 7 A small tutorial with examples
******************************** ********************************
Example 1: Restore a regular file from its compressed version Example 1: Restore a regular file from its compressed version
@ -460,7 +485,7 @@ correct file produced is saved in 'big_db_00001.lz'.
 
File: lziprecover.info, Node: Unzcrash, Next: Problems, Prev: Examples, Up: Top File: lziprecover.info, Node: Unzcrash, Next: Problems, Prev: Examples, Up: Top
7 Testing the robustness of decompressors 8 Testing the robustness of decompressors
***************************************** *****************************************
The lziprecover package also includes unzcrash, a program written to The lziprecover package also includes unzcrash, a program written to
@ -476,9 +501,9 @@ memory accesses. If it does, please, report it as a bug.
Unzcrash really executes as a subprocess the shell command specified Unzcrash really executes as a subprocess the shell command specified
in the first non-option argument, and then writes the file specified in in the first non-option argument, and then writes the file specified in
the second non-option argument to the standard input of the subprocess, the second non-option argument to the standard input of the subprocess,
modifying the corresponding byte each time. Therefore you can use modifying the corresponding byte each time. Therefore unzcrash can be
unzcrash to test any decompressor (not only lzip), or even other decoder used to test any decompressor (not only lzip), or even other decoder
programs with a suitable command line syntax. programs having a suitable command line syntax.
The format for running unzcrash is: The format for running unzcrash is:
@ -537,7 +562,7 @@ caused unzcrash to panic.
 
File: lziprecover.info, Node: Problems, Next: Concept index, Prev: Unzcrash, Up: Top File: lziprecover.info, Node: Problems, Next: Concept index, Prev: Unzcrash, Up: Top
8 Reporting bugs 9 Reporting bugs
**************** ****************
There are probably bugs in lziprecover. There are certainly errors and There are probably bugs in lziprecover. There are certainly errors and
@ -561,6 +586,7 @@ Concept index
* bugs: Problems. (line 6) * bugs: Problems. (line 6)
* examples: Examples. (line 6) * examples: Examples. (line 6)
* file format: File format. (line 6) * file format: File format. (line 6)
* file names: File names. (line 6)
* getting help: Problems. (line 6) * getting help: Problems. (line 6)
* introduction: Introduction. (line 6) * introduction: Introduction. (line 6)
* invoking: Invoking lziprecover. (line 6) * invoking: Invoking lziprecover. (line 6)
@ -572,16 +598,17 @@ Concept index
 
Tag Table: Tag Table:
Node: Top231 Node: Top231
Node: Introduction1077 Node: Introduction1153
Node: Invoking lziprecover4105 Node: Invoking lziprecover4249
Node: Repairing files9543 Node: Repairing files9686
Node: Merging files10733 Node: Merging files11371
Node: File format12504 Node: File names13212
Node: Examples15014 Node: File format13676
Ref: ddrescue-example16215 Node: Examples16183
Node: Unzcrash17324 Ref: ddrescue-example17384
Node: Problems19876 Node: Unzcrash18493
Node: Concept index20426 Node: Problems21047
Node: Concept index21597
 
End Tag Table End Tag Table

View file

@ -6,8 +6,8 @@
@finalout @finalout
@c %**end of header @c %**end of header
@set UPDATED 29 August 2014 @set UPDATED 16 October 2014
@set VERSION 1.16 @set VERSION 1.17-pre1
@dircategory Data Compression @dircategory Data Compression
@direntry @direntry
@ -39,6 +39,7 @@ This manual is for Lziprecover (version @value{VERSION}, @value{UPDATED}).
* Invoking lziprecover:: Command line interface * Invoking lziprecover:: Command line interface
* Repairing files:: Fixing bit-flip and similar errors * Repairing files:: Fixing bit-flip and similar errors
* Merging files:: Fixing several damaged copies * Merging files:: Fixing several damaged copies
* File names:: Names of the files produced by lziprecover
* File format:: Detailed format of the compressed file * File format:: Detailed format of the compressed file
* Examples:: A small tutorial with examples * Examples:: A small tutorial with examples
* Unzcrash:: Testing the robustness of decompressors * Unzcrash:: Testing the robustness of decompressors
@ -59,11 +60,13 @@ to copy, distribute and modify it.
Lziprecover is a data recovery tool and decompressor for files in the Lziprecover is a data recovery tool and decompressor for files in the
lzip compressed data format (.lz), able to repair slightly damaged lzip compressed data format (.lz), able to repair slightly damaged
files, recover badly damaged files from two or more copies, extract data files, produce a correct file by merging the good parts of two or more
from damaged files, decompress files and test integrity of files. damaged copies, extract data from damaged files, decompress files and
test integrity of files.
The lzip file format is designed for long-term data archiving, taking The lzip file format is designed for data sharing and long-term
into account both data integrity and decoder availability: archiving, taking into account both data integrity and decoder
availability:
@itemize @bullet @itemize @bullet
@item @item
@ -82,8 +85,8 @@ data from a lzip file long after quantum computers eventually render
LZMA obsolete. LZMA obsolete.
@item @item
Additionally lzip is copylefted, which guarantees that it will remain Additionally the lzip reference implementation is copylefted, which
free forever. guarantees that it will remain free forever.
@end itemize @end itemize
A nice feature of the lzip format is that a corrupt byte is easier to A nice feature of the lzip format is that a corrupt byte is easier to
@ -196,7 +199,7 @@ information about the members in the file.
@item -m @item -m
@itemx --merge @itemx --merge
Try to produce a correct file merging the good parts of two or more Try to produce a correct file by merging the good parts of two or more
damaged copies. If successful, a repaired copy is written to the file damaged copies. If successful, a repaired copy is written to the file
@samp{@var{file}_fixed.lz}. The exit status is 0 if a correct file could @samp{@var{file}_fixed.lz}. The exit status is 0 if a correct file could
be produced, 2 otherwise. See the chapter @samp{Merging files} be produced, 2 otherwise. See the chapter @samp{Merging files}
@ -231,12 +234,12 @@ Search for members in @samp{@var{file}} and write each member in its own
integrity of the resulting files, decompress those which are undamaged, integrity of the resulting files, decompress those which are undamaged,
and try to repair or partially decompress those which are damaged. and try to repair or partially decompress those which are damaged.
The names of the files produced are in the form The names of the files produced are in the form @samp{rec01@var{file}},
@samp{rec01@var{file}.lz}, @samp{rec02@var{file}.lz}, etc, and are @samp{rec02@var{file}}, etc, and are designed so that the use of
designed so that the use of wildcards in subsequent processing, for wildcards in subsequent processing, for example, @w{@samp{lziprecover
example, @w{@samp{lziprecover -cd rec*@var{file}.lz > recovered_data}}, -cd rec*@var{file} > recovered_data}}, processes the files in the
processes the files in the correct order. The number of digits used in correct order. The number of digits used in the names varies depending
the names varies depending on the number of members in @samp{@var{file}}. on the number of members in @samp{@var{file}}.
@item -t @item -t
@itemx --test @itemx --test
@ -282,17 +285,26 @@ caused lziprecover to panic.
@chapter Repairing files @chapter Repairing files
@cindex repairing files @cindex repairing files
Lziprecover is usually able to repair files with small errors (up to one Lziprecover can repair perfectly most files with small errors (up to one
byte error per member). The error may be located anywhere in the file single-byte error per member), without the need of any extra redundance
except in the header (first 6 bytes of each member) or in the at all. If the reparation is successful, the repaired file will be
@samp{Member size} field of the trailer (last 8 bytes of each member). identical bit for bit to the original.
This makes lzip files resistant to bit-flip, one of the most common
forms of data corruption. The error may be located anywhere in the file except in the header
(first 6 bytes of each member) or in the @samp{Member size} field of the
trailer (last 8 bytes of each member). This makes lzip files resistant
to bit-flip, one of the most common forms of data corruption.
Bit-flip happens when one bit in the file is changed from 0 to 1 or vice Bit-flip happens when one bit in the file is changed from 0 to 1 or vice
versa. It may be caused by bad RAM or even by natural radiation. I have versa. It may be caused by bad RAM or even by natural radiation. I have
seen a case of bit-flip in a file stored on an USB flash drive. seen a case of bit-flip in a file stored on an USB flash drive.
One byte may seem small, but most file corruptions not produced by I/O
errors just affect one byte, or even one bit, of the file. Also, unlike
magnetic media, where errors usually affect a whole sector, solid-state
storage devices tend to produce single-byte errors, making of lzip the
perfect format for data stored on such devices.
Repairing a file can take some time. Small files or files with the error Repairing a file can take some time. Small files or files with the error
located near the beginning can be repaired in a few seconds. But located near the beginning can be repaired in a few seconds. But
repairing a large file compressed with a large dictionary size and with repairing a large file compressed with a large dictionary size and with
@ -309,7 +321,7 @@ repairs more efficiently the worst errors.
If you have several copies of a file but all of them are too damaged to If you have several copies of a file but all of them are too damaged to
repair them (@pxref{Repairing files}), lziprecover can try to produce a repair them (@pxref{Repairing files}), lziprecover can try to produce a
correct file merging the good parts of the damaged copies. correct file by merging the good parts of the damaged copies.
The merge may succeed even if some copies of the file have all the The merge may succeed even if some copies of the file have all the
headers and trailers damaged, as long as there is at least one copy of headers and trailers damaged, as long as there is at least one copy of
@ -321,16 +333,16 @@ damaged in all copies), or are adjacent and the boundary can't be
determined, or if the copies have too many damaged areas. determined, or if the copies have too many damaged areas.
All the copies must have the same size. If some of them have been All the copies must have the same size. If some of them have been
truncated and are therefore smaller than they should, you can extend truncated and are therefore smaller than they should, they can be
them to the correct size with the following command before merging them extended to the correct size with the following command before merging
with the other copies: them with the other copies:
@example @example
ddrescue --extend-outfile=<correct_size> small_file.lz extended_file.lz ddrescue --extend-outfile=<correct_size> small_file.lz extended_file.lz
@end example @end example
If some of the copies have got garbage data at the end and are therefore If some of the copies have got garbage data at the end and are therefore
larger than they should, you can reduce their sizes to the correct value larger than they should, their sizes can be reduced to the correct value
with the following command before merging them with the other copies: with the following command before merging them with the other copies:
@example @example
@ -342,7 +354,19 @@ of them with one damaged area affecting 1 percent of the copy, the
probability of obtaining a correct file is about 98 percent. With three probability of obtaining a correct file is about 98 percent. With three
such copies the probability rises to 99.97 percent. For large files (a such copies the probability rises to 99.97 percent. For large files (a
few MB) with small errors (one sector damaged per copy), the probability few MB) with small errors (one sector damaged per copy), the probability
approaches 100 percent even with only two copies. approaches 100 percent even with only two copies. (Supposing that the
errors are randomly located inside each copy).
@node File names
@chapter Names of the files produced by lziprecover
@cindex file names
The name of the fixed file produced by @samp{--merge} and
@samp{--repair} is made by appending the string @samp{_fixed.lz} to the
original file name. If the original file name ends with one of the
extensions @samp{.tar.lz}, @samp{.lz} or @samp{.tlz}, the string
@samp{_fixed} is inserted before the extension.
@node File format @node File format
@ -541,9 +565,9 @@ accesses. If it does, please, report it as a bug.
Unzcrash really executes as a subprocess the shell command specified in Unzcrash really executes as a subprocess the shell command specified in
the first non-option argument, and then writes the file specified in the the first non-option argument, and then writes the file specified in the
second non-option argument to the standard input of the subprocess, second non-option argument to the standard input of the subprocess,
modifying the corresponding byte each time. Therefore you can use modifying the corresponding byte each time. Therefore unzcrash can be
unzcrash to test any decompressor (not only lzip), or even other decoder used to test any decompressor (not only lzip), or even other decoder
programs with a suitable command line syntax. programs having a suitable command line syntax.
The format for running unzcrash is: The format for running unzcrash is:

15
main.cc
View file

@ -92,7 +92,14 @@ bool delete_output_on_interrupt = false;
void show_help() void show_help()
{ {
std::printf( "%s - Data recovery tool and decompressor for the lzip format.\n", Program_name ); std::printf( "%s - Data recovery tool and decompressor for the lzip format.\n", Program_name );
std::printf( "\nUsage: %s [options] [files]\n", invocation_name ); std::printf( "Lziprecover 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"
"Lziprecover 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"
"\nUsage: %s [options] [files]\n", invocation_name );
std::printf( "\nOptions:\n" std::printf( "\nOptions:\n"
" -h, --help display this help and exit\n" " -h, --help display this help and exit\n"
" -V, --version output version information and exit\n" " -V, --version output version information and exit\n"
@ -304,10 +311,12 @@ void close_and_set_permissions( const struct stat * const in_statsp )
std::string insert_fixed( std::string name ) std::string insert_fixed( std::string name )
{ {
if( name.size() > 4 && name.compare( name.size() - 4, 4, ".tlz" ) == 0 ) if( name.size() > 7 && name.compare( name.size() - 7, 7, ".tar.lz" ) == 0 )
name.insert( name.size() - 4, "_fixed" ); name.insert( name.size() - 7, "_fixed" );
else if( name.size() > 3 && name.compare( name.size() - 3, 3, ".lz" ) == 0 ) else if( name.size() > 3 && name.compare( name.size() - 3, 3, ".lz" ) == 0 )
name.insert( name.size() - 3, "_fixed" ); name.insert( name.size() - 3, "_fixed" );
else if( name.size() > 4 && name.compare( name.size() - 4, 4, ".tlz" ) == 0 )
name.insert( name.size() - 4, "_fixed" );
else name += "_fixed.lz"; else name += "_fixed.lz";
return name; return name;
} }

200
merge.cc
View file

@ -78,18 +78,22 @@ void combine( std::vector< Block > & block_vector, std::vector< Block > & bv )
// positions in 'block_vector' are absolute file positions. // positions in 'block_vector' are absolute file positions.
bool diff_member( const long long mpos, const long long msize, bool diff_member( const long long mpos, const long long msize,
const std::vector< int > & infd_vector, const std::vector< int > & infd_vector,
std::vector< Block > & block_vector ) std::vector< Block > & block_vector,
std::vector< int > & color_vector )
{ {
const int files = infd_vector.size(); const int files = infd_vector.size();
const int buffer_size = 65536; const int buffer_size = 65536;
uint8_t * const buffer1 = new uint8_t[buffer_size]; uint8_t * const buffer1 = new uint8_t[buffer_size];
uint8_t * const buffer2 = new uint8_t[buffer_size]; uint8_t * const buffer2 = new uint8_t[buffer_size];
int next_color = 1;
bool error = false; bool error = false;
for( int i1 = 0; i1 + 1 < files && !error; ++i1 ) for( int i1 = 0; i1 < files && !error; ++i1 )
{ {
for( int i2 = i1 + 1; i2 < files && !error; ++i2 ) for( int i2 = i1 + 1; i2 < files && !error; ++i2 )
{ {
if( color_vector[i1] != 0 && color_vector[i1] == color_vector[i2] )
continue;
std::vector< Block > bv; std::vector< Block > bv;
long long partial_pos = 0; long long partial_pos = 0;
const int fd1 = infd_vector[i1], fd2 = infd_vector[i2]; const int fd1 = infd_vector[i1], fd2 = infd_vector[i2];
@ -98,7 +102,7 @@ bool diff_member( const long long mpos, const long long msize,
if( !safe_seek( fd1, mpos ) || !safe_seek( fd2, mpos ) ) if( !safe_seek( fd1, mpos ) || !safe_seek( fd2, mpos ) )
{ error = true; break; } { error = true; break; }
while( msize > partial_pos ) while( partial_pos < msize )
{ {
const int size = std::min( (long long)buffer_size, msize - partial_pos ); const int size = std::min( (long long)buffer_size, msize - partial_pos );
const int rd = readblock( fd1, buffer1, size ); const int rd = readblock( fd1, buffer1, size );
@ -133,21 +137,32 @@ bool diff_member( const long long mpos, const long long msize,
Block b( mpos + begin, partial_pos - prev_equal - begin ); Block b( mpos + begin, partial_pos - prev_equal - begin );
bv.push_back( b ); bv.push_back( b );
} }
if( bv.empty() ) // members are identical, set to same color
{
if( color_vector[i1] == 0 )
{
if( color_vector[i2] != 0 ) color_vector[i1] = color_vector[i2];
else color_vector[i1] = color_vector[i2] = next_color++;
}
else if( color_vector[i2] == 0 ) color_vector[i2] = color_vector[i1];
else internal_error( "different colors assigned to identical members." );
}
combine( block_vector, bv ); combine( block_vector, bv );
} }
if( color_vector[i1] == 0 ) color_vector[i1] = next_color++;
} }
delete[] buffer2; delete[] buffer1; delete[] buffer2; delete[] buffer1;
return !error; return !error;
} }
int ipow( const unsigned base, const unsigned exponent ) long ipow( const unsigned base, const unsigned exponent )
{ {
unsigned result = 1; unsigned long result = 1;
for( unsigned i = 0; i < exponent; ++i ) for( unsigned i = 0; i < exponent; ++i )
{ {
if( INT_MAX / base >= result ) result *= base; if( LONG_MAX / base >= result ) result *= base;
else { result = INT_MAX; break; } else { result = LONG_MAX; break; }
} }
return result; return result;
} }
@ -239,6 +254,116 @@ int open_input_files( const std::vector< std::string > & filenames,
return -1; return -1;
} }
bool try_merge_member( const long long mpos, const long long msize,
const std::vector< Block > & block_vector,
const std::vector< int > & color_vector,
const std::vector< int > & infd_vector,
const std::string & output_filename,
const int outfd, const int verbosity )
{
const int blocks = block_vector.size();
const int files = infd_vector.size();
const long variations = ipow( files, blocks );
if( variations >= LONG_MAX )
{
if( files > 2 )
show_error( "Too many damaged blocks. Try merging fewer files." );
else
show_error( "Too many damaged blocks. Merging is not possible." );
cleanup_and_fail( output_filename, outfd, 2 );
}
int bi = 0; // block index
std::vector< int > file_idx( blocks, 0 ); // file to read each block from
while( bi >= 0 )
{
if( verbosity >= 1 )
{
long var = 0;
for( int i = 0; i < blocks; ++i )
var = ( var * files ) + file_idx[i];
std::printf( "Trying variation %ld of %ld \r", var + 1, variations );
std::fflush( stdout );
}
while( bi < blocks )
{
const int infd = infd_vector[file_idx[bi]];
if( !safe_seek( infd, block_vector[bi].pos() ) ||
!safe_seek( outfd, block_vector[bi].pos() ) ||
!copy_file( infd, outfd, block_vector[bi].size() ) )
cleanup_and_fail( output_filename, outfd, 1 );
++bi;
}
if( !safe_seek( outfd, mpos ) )
cleanup_and_fail( output_filename, outfd, 1 );
long long failure_pos = 0;
if( try_decompress_member( outfd, msize, &failure_pos ) ) return true;
while( bi > 0 && mpos + failure_pos < block_vector[bi-1].pos() ) --bi;
while( --bi >= 0 )
{
while( ++file_idx[bi] < files )
{
const int color = color_vector[file_idx[bi]];
bool done = true;
for( int i = file_idx[bi] - 1; i >= 0; --i )
if( color_vector[i] == color ) { done = false; break; }
if( done ) break;
}
if( file_idx[bi] < files ) break;
file_idx[bi] = 0;
}
}
return false;
}
bool try_merge_member1( const long long mpos, const long long msize,
const std::vector< Block > & block_vector,
const std::vector< int > & color_vector,
const std::vector< int > & infd_vector,
const std::string & output_filename,
const int outfd, const int verbosity )
{
if( block_vector.size() != 1 || block_vector[0].size() <= 1 ) return false;
const long long pos = block_vector[0].pos();
const long long size = block_vector[0].size();
const int files = infd_vector.size();
const int variations = files * ( files - 1 );
uint8_t byte;
for( int i1 = 0; i1 < files; ++i1 )
for( int i2 = 0; i2 < files; ++i2 )
{
if( i1 == i2 || color_vector[i1] == color_vector[i2] ) continue;
const int infd = infd_vector[i1];
if( !safe_seek( infd, pos ) ||
!safe_seek( infd_vector[i2], pos ) ||
!safe_seek( outfd, pos ) ||
!copy_file( infd_vector[i2], outfd, size ) )
cleanup_and_fail( output_filename, outfd, 1 );
const int var = ( i1 * ( files - 1 ) ) + i2 - ( i2 > i1 ) + 1;
for( long long i = 0; i < size; ++i )
{
if( verbosity >= 1 )
{
std::printf( "Trying variation %d of %d, position %lld \r",
var, variations, pos + i );
std::fflush( stdout );
}
if( !safe_seek( outfd, pos + i ) ||
readblock( infd, &byte, 1 ) != 1 ||
writeblock( outfd, &byte, 1 ) != 1 ||
!safe_seek( outfd, mpos ) )
cleanup_and_fail( output_filename, outfd, 1 );
long long failure_pos = 0;
if( try_decompress_member( outfd, msize, &failure_pos ) ) return true;
if( mpos + failure_pos <= pos + i ) break;
}
}
return false;
}
} // end namespace } // end namespace
@ -327,7 +452,8 @@ int merge_files( const std::vector< std::string > & filenames,
const long long msize = file_index.mblock( j ).size(); const long long msize = file_index.mblock( j ).size();
// vector of data blocks differing among the copies of the current member // vector of data blocks differing among the copies of the current member
std::vector< Block > block_vector; std::vector< Block > block_vector;
if( !diff_member( mpos, msize, infd_vector, block_vector ) || std::vector< int > color_vector( files, 0 );
if( !diff_member( mpos, msize, infd_vector, block_vector, color_vector ) ||
!safe_seek( outfd, mpos ) ) !safe_seek( outfd, mpos ) )
cleanup_and_fail( output_filename, outfd, 1 ); cleanup_and_fail( output_filename, outfd, 1 );
@ -335,63 +461,33 @@ int merge_files( const std::vector< std::string > & filenames,
{ {
if( file_index.members() > 1 && try_decompress_member( outfd, msize ) ) if( file_index.members() > 1 && try_decompress_member( outfd, msize ) )
continue; continue;
show_error( "Input files are (partially) identical. Recovery is not possible." ); show_error( "Input files are (partially) identical. Merging is not possible." );
cleanup_and_fail( output_filename, outfd, 2 ); cleanup_and_fail( output_filename, outfd, 2 );
} }
const int size0 = block_vector[0].size();
const bool single_block = ( block_vector.size() == 1 );
if( ipow( files, block_vector.size() ) >= INT_MAX ||
( single_block && ipow( files, 2 ) >= INT_MAX / size0 ) )
{ show_error( "Input files are too damaged. Recovery is not possible." );
cleanup_and_fail( output_filename, outfd, 2 ); }
const int shifts = ( single_block && size0 > 1 ) ? size0 - 1 : 1;
if( single_block && size0 > 1 )
{
Block b( block_vector[0].pos() + 1, size0 - 1 );
block_vector[0].size( 1 );
block_vector.push_back( b );
}
if( verbosity >= 1 && file_index.members() > 1 ) if( verbosity >= 1 && file_index.members() > 1 )
{ {
std::printf( "Merging member %ld\n", j + 1 ); std::printf( "Merging member %ld of %ld\n",
j + 1, (long)file_index.members() );
std::fflush( stdout ); std::fflush( stdout );
} }
const int base_variations = ipow( files, block_vector.size() );
const int variations = base_variations * shifts;
bool done = false; bool done = false;
for( int var = 0; var < variations; ++var ) if( file_index.members() > 1 || block_vector.size() > 1 )
{ {
if( verbosity >= 1 ) done = try_merge_member( mpos, msize, block_vector, color_vector,
{ infd_vector, output_filename, outfd, verbosity );
std::printf( "Trying variation %d of %d \r", var + 1, variations ); if( !done && verbosity >= 1 ) std::fputs( "\n", stdout );
std::fflush( stdout );
}
int tmp = var;
for( unsigned i = 0; i < block_vector.size(); ++i )
{
const int infd = infd_vector[tmp % files];
tmp /= files;
if( !safe_seek( infd, block_vector[i].pos() ) ||
!safe_seek( outfd, block_vector[i].pos() ) ||
!copy_file( infd, outfd, block_vector[i].size() ) )
cleanup_and_fail( output_filename, outfd, 1 );
}
if( !safe_seek( outfd, mpos ) )
cleanup_and_fail( output_filename, outfd, 1 );
if( try_decompress_member( outfd, msize ) )
{ done = true; break; }
if( var > 0 && var % base_variations == 0 )
block_vector[0].shift( block_vector[1] );
} }
if( verbosity >= 1 ) std::printf( "\n" ); if( !done )
done = try_merge_member1( mpos, msize, block_vector, color_vector,
infd_vector, output_filename, outfd, verbosity );
if( verbosity >= 1 ) std::fputs( "\n", stdout );
if( !done ) if( !done )
{ {
if( verbosity >= 2 ) if( verbosity >= 2 )
for( unsigned i = 0; i < block_vector.size(); ++i ) for( unsigned i = 0; i < block_vector.size(); ++i )
std::fprintf( stderr, "area %2d from offset %6lld to %6lld\n", i + 1, std::fprintf( stderr, "area %2d from position %6lld to %6lld\n", i + 1,
block_vector[i].pos(), block_vector[i].end() - 1 ); block_vector[i].pos(), block_vector[i].end() - 1 );
show_error( "Some error areas overlap. Can't recover input file." ); show_error( "Some error areas overlap. Can't recover input file." );
cleanup_and_fail( output_filename, outfd, 2 ); cleanup_and_fail( output_filename, outfd, 2 );
@ -404,6 +500,6 @@ int merge_files( const std::vector< std::string > & filenames,
cleanup_and_fail( output_filename, -1, 1 ); cleanup_and_fail( output_filename, -1, 1 );
} }
if( verbosity >= 1 ) if( verbosity >= 1 )
std::printf( "Input files merged successfully.\n" ); std::fputs( "Input files merged successfully.\n", stdout );
return 0; return 0;
} }

View file

@ -71,8 +71,8 @@ int repair_file( const std::string & input_filename,
if( verbosity >= 1 ) // damaged member found if( verbosity >= 1 ) // damaged member found
{ {
std::printf( "Repairing member %ld (failure pos = %llu)\n", std::printf( "Repairing member %ld of %ld (failure pos = %llu)\n",
i + 1, mpos + failure_pos ); i + 1, (long)file_index.members(), mpos + failure_pos );
std::fflush( stdout ); std::fflush( stdout );
} }
uint8_t * const mbuffer = read_member( infd, mpos, msize ); uint8_t * const mbuffer = read_member( infd, mpos, msize );
@ -80,7 +80,7 @@ int repair_file( const std::string & input_filename,
cleanup_and_fail( output_filename, outfd, 1 ); cleanup_and_fail( output_filename, outfd, 1 );
long pos = failure_pos; long pos = failure_pos;
bool done = false; bool done = false;
while( pos >= File_header::size && pos > failure_pos - 20000 && !done ) while( pos >= File_header::size && pos > failure_pos - 40000 && !done )
{ {
const long min_pos = std::max( (long)File_header::size, pos - 1000 ); const long min_pos = std::max( (long)File_header::size, pos - 1000 );
const LZ_mtester * master = prepare_master( mbuffer, msize, min_pos - 16 ); const LZ_mtester * master = prepare_master( mbuffer, msize, min_pos - 16 );
@ -118,7 +118,7 @@ int repair_file( const std::string & input_filename,
delete master; delete master;
} }
delete[] mbuffer; delete[] mbuffer;
if( verbosity >= 1 ) std::printf( "\n" ); if( verbosity >= 1 ) std::fputs( "\n", stdout );
if( !done ) if( !done )
{ {
show_error( "Can't repair input file. Error is probably larger than 1 byte." ); show_error( "Can't repair input file. Error is probably larger than 1 byte." );
@ -129,7 +129,7 @@ int repair_file( const std::string & input_filename,
if( outfd < 0 ) if( outfd < 0 )
{ {
if( verbosity >= 1 ) if( verbosity >= 1 )
std::printf( "Input file has no errors. Recovery is not needed.\n" ); std::fputs( "Input file has no errors. Recovery is not needed.\n", stdout );
return 0; return 0;
} }
if( close( outfd ) != 0 ) if( close( outfd ) != 0 )
@ -138,6 +138,6 @@ int repair_file( const std::string & input_filename,
cleanup_and_fail( output_filename, -1, 1 ); cleanup_and_fail( output_filename, -1, 1 );
} }
if( verbosity >= 1 ) if( verbosity >= 1 )
std::printf( "Copy of input file repaired successfully.\n" ); std::fputs( "Copy of input file repaired successfully.\n", stdout );
return 0; return 0;
} }

View file

@ -43,9 +43,8 @@ void first_filename( const std::string & input_filename,
output_filename = input_filename; output_filename = input_filename;
int b = output_filename.size(); int b = output_filename.size();
while( b > 0 && output_filename[b-1] != '/' ) --b; while( b > 0 && output_filename[b-1] != '/' ) --b;
output_filename.insert( b, 1, '1' ); output_filename.insert( b, "rec1" );
if( max_digits > 1 ) output_filename.insert( b, max_digits - 1, '0' ); if( max_digits > 1 ) output_filename.insert( b + 3, max_digits - 1, '0' );
output_filename.insert( b, "rec" );
} }

View file

@ -49,7 +49,7 @@ fail=0
# fox5_bad3.lz: [100-299] --> zeroed; # fox5_bad3.lz: [100-299] --> zeroed;
# fox5_bad4.lz: [250-349] --> zeroed; # fox5_bad4.lz: [250-349] --> zeroed;
# fox5_bad5.lz: [300-399] --> zeroed; # fox5_bad5.lz: [300-399] --> zeroed;
# test_bad1.lz: byte at offset 67 changed from 0x70 to 0x79 # test_bad1.lz: byte at offset 66 changed from 0xA6 to 0x46
# test_bad2.lz: [ 34- 65] --> copy of bytes [ 68- 99] # test_bad2.lz: [ 34- 65] --> copy of bytes [ 68- 99]
# test_bad3.lz: [ 512-1535] --> zeroed; [2560-3583] --> zeroed # test_bad3.lz: [ 512-1535] --> zeroed; [2560-3583] --> zeroed
# test_bad4.lz: [3072-4095] --> random data; [4608-5631] --> zeroed # test_bad4.lz: [3072-4095] --> random data; [4608-5631] --> zeroed
@ -180,6 +180,24 @@ cmp "${in_lz}" copy.lz || fail=1
cmp "${in_lz}" copy.lz || fail=1 cmp "${in_lz}" copy.lz || fail=1
printf . printf .
cat "${bad1_lz}" "${in_lz}" "${bad1_lz}" "${bad1_lz}" > bad11.lz || framework_failure
cat "${bad1_lz}" "${in_lz}" "${bad2_lz}" "${in_lz}" > bad12.lz || framework_failure
cat "${bad2_lz}" "${in_lz}" "${bad2_lz}" "${bad2_lz}" > bad22.lz || framework_failure
cat "${in_lz}" "${in_lz}" "${in_lz}" "${in_lz}" > copy4.lz || framework_failure
"${LZIPRECOVER}" -mf -o out4.lz bad11.lz bad12.lz bad22.lz || fail=1
cmp out4.lz copy4.lz || fail=1
"${LZIPRECOVER}" -mf -o out4.lz bad11.lz bad22.lz bad12.lz || fail=1
cmp out4.lz copy4.lz || fail=1
"${LZIPRECOVER}" -mf -o out4.lz bad12.lz bad11.lz bad22.lz || fail=1
cmp out4.lz copy4.lz || fail=1
"${LZIPRECOVER}" -mf -o out4.lz bad12.lz bad22.lz bad11.lz || fail=1
cmp out4.lz copy4.lz || fail=1
"${LZIPRECOVER}" -mf -o out4.lz bad22.lz bad11.lz bad12.lz || fail=1
cmp out4.lz copy4.lz || fail=1
"${LZIPRECOVER}" -mf -o out4.lz bad22.lz bad12.lz bad11.lz || fail=1
cmp out4.lz copy4.lz || fail=1
printf .
for i in "${bad1_lz}" "${bad2_lz}" ; do for i in "${bad1_lz}" "${bad2_lz}" ; do
for j in "${bad3_lz}" "${bad4_lz}" "${bad5_lz}" ; do for j in "${bad3_lz}" "${bad4_lz}" "${bad5_lz}" ; do
"${LZIPRECOVER}" -mf -o copy.lz "${i}" "${j}" || fail=1 "${LZIPRECOVER}" -mf -o copy.lz "${i}" "${j}" || fail=1
@ -209,6 +227,24 @@ printf .
cmp "${in_lz}" copy.lz || fail=1 cmp "${in_lz}" copy.lz || fail=1
printf . printf .
cat "${bad3_lz}" "${bad4_lz}" "${bad5_lz}" "${in_lz}" > bad345.lz || framework_failure
cat "${bad4_lz}" "${bad5_lz}" "${bad3_lz}" "${in_lz}" > bad453.lz || framework_failure
cat "${bad5_lz}" "${bad3_lz}" "${bad4_lz}" "${in_lz}" > bad534.lz || framework_failure
cat "${in_lz}" "${in_lz}" "${in_lz}" "${in_lz}" > copy4.lz || framework_failure
"${LZIPRECOVER}" -mf -o out4.lz bad345.lz bad453.lz bad534.lz || fail=1
cmp out4.lz copy4.lz || fail=1
"${LZIPRECOVER}" -mf -o out4.lz bad345.lz bad534.lz bad453.lz || fail=1
cmp out4.lz copy4.lz || fail=1
"${LZIPRECOVER}" -mf -o out4.lz bad453.lz bad345.lz bad534.lz || fail=1
cmp out4.lz copy4.lz || fail=1
"${LZIPRECOVER}" -mf -o out4.lz bad453.lz bad534.lz bad345.lz || fail=1
cmp out4.lz copy4.lz || fail=1
"${LZIPRECOVER}" -mf -o out4.lz bad534.lz bad345.lz bad453.lz || fail=1
cmp out4.lz copy4.lz || fail=1
"${LZIPRECOVER}" -mf -o out4.lz bad534.lz bad453.lz bad345.lz || fail=1
cmp out4.lz copy4.lz || fail=1
printf .
rm -f copy.lz rm -f copy.lz
"${LZIPRECOVER}" -R -o copy.lz "${fox5_lz}" || fail=1 "${LZIPRECOVER}" -R -o copy.lz "${fox5_lz}" || fail=1
if [ $? = 0 ] && [ ! -e copy.lz ] ; then printf . ; else printf - ; fail=1 ; fi if [ $? = 0 ] && [ ! -e copy.lz ] ; then printf . ; else printf - ; fail=1 ; fi
@ -220,6 +256,16 @@ cmp "${fox5_lz}" copy.lz || fail=1
cmp "${in_lz}" copy.lz || fail=1 cmp "${in_lz}" copy.lz || fail=1
printf . printf .
cat "${f5b1_lz}" > copy.tar.lz || framework_failure
"${LZIPRECOVER}" -R copy.tar.lz || fail=1
if [ $? = 0 ] && [ -e copy_fixed.tar.lz ] ; then printf . ; else printf - ; fail=1 ; fi
mv copy.tar.lz copy.lz || framework_failure
"${LZIPRECOVER}" -R copy.lz || fail=1
if [ $? = 0 ] && [ -e copy_fixed.lz ] ; then printf . ; else printf - ; fail=1 ; fi
mv copy.lz copy.tlz || framework_failure
"${LZIPRECOVER}" -R copy.tlz || fail=1
if [ $? = 0 ] && [ -e copy_fixed.tlz ] ; then printf . ; else printf - ; fail=1 ; fi
cat "${in_lz}" "${in_lz}" "${in_lz}" > copy || framework_failure cat "${in_lz}" "${in_lz}" "${in_lz}" > copy || framework_failure
printf "garbage" >> copy || fail=1 printf "garbage" >> copy || fail=1
"${LZIPRECOVER}" -s -o copy.lz copy || fail=1 "${LZIPRECOVER}" -s -o copy.lz copy || fail=1

Binary file not shown.

View file

@ -33,7 +33,7 @@
#include <stdint.h> #include <stdint.h>
#include <unistd.h> #include <unistd.h>
#include "../arg_parser.h" #include "arg_parser.h"
#if CHAR_BIT != 8 #if CHAR_BIT != 8
#error "Environments where CHAR_BIT != 8 are not supported." #error "Environments where CHAR_BIT != 8 are not supported."
@ -209,18 +209,18 @@ public:
std::fflush( stderr ); std::fflush( stderr );
int c = 0; int c = 0;
for( int i = 0; i < 8; ++i ) if( data[i] ) ++c; for( int i = 0; i < 8; ++i ) if( data[i] ) ++c;
if( c == 8 ) std::printf( "Testing full byte.\n" ); if( c == 8 ) std::fputs( "Testing full byte.\n", stdout );
else if( c == 0 ) std::printf( "Nothing to test.\n" ); else if( c == 0 ) std::fputs( "Nothing to test.\n", stdout );
else else
{ {
std::printf( "Testing " ); std::fputs( "Testing ", stdout );
for( int i = 0; i < 8; ++i ) for( int i = 0; i < 8; ++i )
if( data[i] ) if( data[i] )
{ {
std::printf( "%d", i + 1 ); std::printf( "%d", i + 1 );
if( --c ) std::printf( "," ); if( --c ) std::fputs( ",", stdout );
} }
std::printf( " bit errors.\n" ); std::fputs( " bit errors.\n", stdout );
} }
std::fflush( stdout ); std::fflush( stdout );
} }
@ -241,7 +241,7 @@ int differing_bits( const uint8_t byte1, const uint8_t byte2 )
int main( const int argc, const char * const argv[] ) int main( const int argc, const char * const argv[] )
{ {
enum { buffer_size = 3 << 20 }; enum { buffer_size = 75 << 20 };
Bitset8 bits; // if Bitset8::parse not called test full byte Bitset8 bits; // if Bitset8::parse not called test full byte
int pos = 0; int pos = 0;
int max_size = buffer_size; int max_size = buffer_size;
@ -330,18 +330,21 @@ int main( const int argc, const char * const argv[] )
if( verbosity >= 0 ) if( verbosity >= 0 )
std::fprintf( stderr, "byte %d\n", i ); std::fprintf( stderr, "byte %d\n", i );
const uint8_t byte = buffer[i]; const uint8_t byte = buffer[i];
for( int j = 0; j < 255; ++j ) for( int j = 1; j < 256; ++j )
{ {
++buffer[i]; ++buffer[i];
if( bits.includes( differing_bits( byte, buffer[i] ) ) ) if( bits.includes( differing_bits( byte, buffer[i] ) ) )
{ {
if( verbosity >= 2 )
std::fprintf( stderr, "0x%02X (0x%02X+0x%02X) ",
buffer[i], byte, j );
f = popen( parser.argument( argind ).c_str(), "w" ); f = popen( parser.argument( argind ).c_str(), "w" );
if( !f ) if( !f )
{ show_error( "Can't open pipe", errno ); return 1; } { show_error( "Can't open pipe", errno ); return 1; }
std::fwrite( buffer, 1, size, f ); std::fwrite( buffer, 1, size, f );
if( pclose( f ) == 0 && verbosity >= 0 ) if( pclose( f ) == 0 && verbosity >= 0 )
std::fprintf( stderr, "0x%02X (0x%02X+0x%02X) passed the test\n", std::fprintf( stderr, "0x%02X (0x%02X+0x%02X) passed the test\n",
buffer[i], byte, j + 1 ); buffer[i], byte, j );
} }
} }
buffer[i] = byte; buffer[i] = byte;