Adding upstream version 1.18.1
Michael Prokop
9 years ago
0 | 2014-06-07 Antonio Diaz Diaz <antonio@gnu.org> | |
1 | ||
2 | * Version 1.18.1 released. | |
3 | * ddrescuelog.cc (do_logic_ops): Fixed 'or' and 'xor'. | |
4 | * Added new option '-H, --test-mode' to simulate read errors. | |
5 | * Added new option '-L, --loose-domain' to ddrescue and ddrescuelog. | |
6 | * Added new option '-N, --no-trim' to disable trimming of | |
7 | damaged areas. | |
8 | * Added new option '-O, --reopen-on-error'. | |
9 | * Added new options '-1, --log-rates', and '-2, --log-reads'. | |
10 | * Extended '-K, --skip-size' with maximum and disable values. | |
11 | * Changed long name of option '-r' to '--retry-passes'. | |
12 | * Changed short name of option '--generate-mode' to '-G'. | |
13 | * Default value of option '-l, --logfile-size' increased to 10000. | |
14 | * If interrupted, ddrescue terminates by raising the signal received. | |
15 | * rescuebook.cc (copy_non_tried): Do not mark skipped blocks as | |
16 | non-trimmed. Try them in additional passes (before trimming). | |
17 | * rescuebook.cc: Limit the copying phase to 3 passes. | |
18 | * rescuebook.cc: Alternate direction of passes during copying phase. | |
19 | * rescuebook.cc: Smallest blocks are trimmed first. | |
20 | * rescuebook.cc (split_errors): Read largest first if logfile full. | |
21 | * Improved speed when using option '-m, --domain-logfile'. | |
22 | * io.cc (show_status): Show the current total run time. | |
23 | * rescuebook.cc: Show pass number and direction during copying. | |
24 | * rescuebook.cc (show_status): Show block pos instead of current_pos. | |
25 | * main.cc: Show "an unknown number of bytes" for unknown isize. | |
26 | * Added option '-B, --binary-prefixes' to ddrescuelog. | |
27 | * Added new option '-C, --complete-logfile' to ddrescuelog. | |
28 | * Added new option '-P, --compare-as-domain' to ddrescuelog. | |
29 | * Improved speed of logic operations in ddrescuelog. | |
30 | * rescuebook.cc (Rescuebook::do_rescue): Show warning when domain | |
31 | is smaller than logfile. | |
32 | * ddrescuelog.cc (do_show_status): Show logfile and domain extents | |
33 | when domain is smaller than logfile. | |
34 | * block.h: Class Block now forces the invariant by itself. | |
35 | * Code reorganization. New class 'Logfile'. | |
36 | * Added status message to rescue logfile. | |
37 | * Many improvements to documentation. | |
38 | * ddrescue.texinfo: Renamed to ddrescue.texi. | |
39 | ||
0 | 40 | 2013-07-09 Antonio Diaz Diaz <antonio@gnu.org> |
1 | 41 | |
2 | 42 | * Version 1.17 released. |
16 | 56 | * rescuebook.cc: Mark failed blocks with 1 sector as bad-sector. |
17 | 57 | * logbook.cc (extend_sblock_vector): Remove last block of |
18 | 58 | logfile if it starts at isize and is not marked as finished. |
19 | * io.cc (show_status,update_rates): Detect a jump back in time | |
59 | * io.cc (show_status, update_rates): Detect a jump back in time | |
20 | 60 | and adjust status. |
21 | 61 | * ddrescue.h (slow_read): Return false for the first 10 seconds. |
22 | 62 | * io.cc (show_status) Leave cursor after message so that ^C does |
153 | 193 | * Version 1.6 released. |
154 | 194 | * Code reorganization. New classes 'Fillbook' and 'Rescuebook'. |
155 | 195 | * logbook.cc (copy_non_tried): Added new variable 'skip_counter'. |
156 | * Added new pass that trims error areas backward before splitting. | |
196 | * Added new pass that trims error areas backwards before splitting. | |
157 | 197 | * Added support for sparse output files. |
158 | 198 | * Blocks longer than hardbs are now split at sector boundaries. |
159 | 199 | * Added new option '-F, --fill'. |
271 | 311 | * Version 0.1 released. |
272 | 312 | |
273 | 313 | |
274 | Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 | |
275 | Antonio Diaz Diaz. | |
314 | Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, | |
315 | 2014 Antonio Diaz Diaz. | |
276 | 316 | |
277 | 317 | This file is a collection of facts, and thus it is not copyrightable, |
278 | 318 | but just in case, you have unlimited permission to copy, distribute and |
9 | 9 | --------- |
10 | 10 | 1. Unpack the archive if you have not done so already: |
11 | 11 | |
12 | tar -xf ddrescue[version].tar.lz | |
13 | or | |
12 | 14 | lzip -cd ddrescue[version].tar.lz | tar -xf - |
13 | or | |
14 | gzip -cd ddrescue[version].tar.gz | tar -xf - | |
15 | 15 | |
16 | 16 | This creates the directory ./ddrescue[version] containing the source from |
17 | 17 | the main archive. |
38 | 38 | |
39 | 39 | Another way |
40 | 40 | ----------- |
41 | You can also compile ddrescue into a separate directory. To do this, you | |
42 | must use a version of 'make' that supports the 'VPATH' variable, such | |
43 | as GNU 'make'. 'cd' to the directory where you want the object files | |
44 | and executables to go and run the 'configure' script. 'configure' | |
45 | automatically checks for the source code in '.', in '..' and in the | |
46 | directory that 'configure' is in. | |
41 | You can also compile ddrescue into a separate directory. | |
42 | To do this, you must use a version of 'make' that supports the 'VPATH' | |
43 | variable, such as GNU 'make'. 'cd' to the directory where you want the | |
44 | object files and executables to go and run the 'configure' script. | |
45 | 'configure' automatically checks for the source code in '.', in '..' and | |
46 | in the directory that 'configure' is in. | |
47 | 47 | |
48 | 48 | 'configure' recognizes the option '--srcdir=DIR' to control where to |
49 | 49 | look for the sources. Usually 'configure' can determine that directory |
53 | 53 | explained above. |
54 | 54 | |
55 | 55 | |
56 | Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 | |
57 | Antonio Diaz Diaz. | |
56 | Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, | |
57 | 2014 Antonio Diaz Diaz. | |
58 | 58 | |
59 | 59 | This file is free documentation: you have unlimited permission to copy, |
60 | 60 | distribute and modify it. |
0 | 0 | |
1 | 1 | DISTNAME = $(pkgname)-$(pkgversion) |
2 | 2 | INSTALL = install |
3 | INSTALL_PROGRAM = $(INSTALL) -p -m 755 | |
4 | INSTALL_DATA = $(INSTALL) -p -m 644 | |
3 | INSTALL_PROGRAM = $(INSTALL) -m 755 | |
4 | INSTALL_DATA = $(INSTALL) -m 644 | |
5 | 5 | INSTALL_DIR = $(INSTALL) -d -m 755 |
6 | 6 | SHELL = /bin/sh |
7 | 7 | |
8 | ddobjs = block.o fillbook.o genbook.o io.o logbook.o rescuebook.o main.o | |
9 | objs = arg_parser.o rational.o $(ddobjs) | |
10 | logobjs = arg_parser.o block.o logbook.o ddrescuelog.o | |
8 | ddobjs = fillbook.o genbook.o io.o logbook.o rescuebook.o main.o | |
9 | objs = arg_parser.o block.o logfile.o loggers.o rational.o $(ddobjs) | |
10 | logobjs = arg_parser.o block.o logbook.o logfile.o ddrescuelog.o | |
11 | 11 | |
12 | 12 | |
13 | 13 | .PHONY : all install install-bin install-info install-man install-strip \ |
17 | 17 | all : $(progname) ddrescuelog |
18 | 18 | |
19 | 19 | $(progname) : $(objs) |
20 | $(CXX) $(LDFLAGS) -o $@ $(objs) | |
20 | $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $(objs) | |
21 | 21 | |
22 | 22 | $(progname)_profiled : $(objs) |
23 | $(CXX) $(LDFLAGS) -pg -o $@ $(objs) | |
23 | $(CXX) $(CXXFLAGS) $(LDFLAGS) -pg -o $@ $(objs) | |
24 | 24 | |
25 | 25 | ddrescuelog : $(logobjs) |
26 | $(CXX) $(LDFLAGS) -o $@ $(logobjs) | |
26 | $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $(logobjs) | |
27 | 27 | |
28 | 28 | static_$(progname) : $(objs) |
29 | $(CXX) $(LDFLAGS) -static -o $@ $(objs) | |
29 | $(CXX) $(CXXFLAGS) $(LDFLAGS) -static -o $@ $(objs) | |
30 | 30 | |
31 | 31 | main.o : main.cc |
32 | $(CXX) $(CPPFLAGS) $(CXXFLAGS) -DPROGVERSION=\"$(pkgversion)\" -c -o $@ $< | |
32 | $(CXX) $(CXXFLAGS) $(CPPFLAGS) -DPROGVERSION=\"$(pkgversion)\" -c -o $@ $< | |
33 | 33 | |
34 | 34 | ddrescuelog.o : ddrescuelog.cc |
35 | $(CXX) $(CPPFLAGS) $(CXXFLAGS) -DPROGVERSION=\"$(pkgversion)\" -c -o $@ $< | |
35 | $(CXX) $(CXXFLAGS) $(CPPFLAGS) -DPROGVERSION=\"$(pkgversion)\" -c -o $@ $< | |
36 | 36 | |
37 | 37 | %.o : %.cc |
38 | $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $< | |
38 | $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $< | |
39 | 39 | |
40 | 40 | $(objs) : Makefile |
41 | 41 | $(ddobjs) : block.h ddrescue.h |
42 | 42 | arg_parser.o : arg_parser.h |
43 | block.o : block.h | |
44 | io.o : loggers.h | |
45 | logfile.o : block.h | |
46 | loggers.o : block.h loggers.h | |
43 | 47 | rational.o : rational.h |
44 | main.o : arg_parser.h rational.h main_common.cc | |
48 | rescuebook.o : loggers.h | |
49 | main.o : arg_parser.h rational.h loggers.h main_common.cc | |
45 | 50 | ddrescuelog.o : Makefile arg_parser.h block.h ddrescue.h main_common.cc |
46 | 51 | |
47 | 52 | |
49 | 54 | |
50 | 55 | info : $(VPATH)/doc/$(pkgname).info |
51 | 56 | |
52 | $(VPATH)/doc/$(pkgname).info : $(VPATH)/doc/$(pkgname).texinfo | |
53 | cd $(VPATH)/doc && makeinfo $(pkgname).texinfo | |
57 | $(VPATH)/doc/$(pkgname).info : $(VPATH)/doc/$(pkgname).texi | |
58 | cd $(VPATH)/doc && makeinfo $(pkgname).texi | |
54 | 59 | |
55 | 60 | man : $(VPATH)/doc/$(progname).1 $(VPATH)/doc/ddrescuelog.1 |
56 | 61 | |
57 | 62 | $(VPATH)/doc/$(progname).1 : $(progname) |
58 | help2man -n 'data recovery tool' \ | |
59 | -o $@ ./$(progname) | |
63 | help2man -n 'data recovery tool' -o $@ ./$(progname) | |
60 | 64 | |
61 | 65 | $(VPATH)/doc/ddrescuelog.1 : ddrescuelog |
62 | help2man -n 'tool for ddrescue logfiles' \ | |
63 | -o $@ --no-info ./ddrescuelog | |
66 | help2man -n 'tool for ddrescue logfiles' -o $@ --no-info ./ddrescuelog | |
64 | 67 | |
65 | 68 | Makefile : $(VPATH)/configure $(VPATH)/Makefile.in |
66 | 69 | ./config.status |
104 | 107 | |
105 | 108 | dist : doc |
106 | 109 | ln -sf $(VPATH) $(DISTNAME) |
107 | tar -cvf $(DISTNAME).tar \ | |
110 | tar -Hustar --owner=root --group=root -cvf $(DISTNAME).tar \ | |
108 | 111 | $(DISTNAME)/AUTHORS \ |
109 | 112 | $(DISTNAME)/COPYING \ |
110 | 113 | $(DISTNAME)/ChangeLog \ |
116 | 119 | $(DISTNAME)/doc/$(progname).1 \ |
117 | 120 | $(DISTNAME)/doc/ddrescuelog.1 \ |
118 | 121 | $(DISTNAME)/doc/$(pkgname).info \ |
119 | $(DISTNAME)/doc/$(pkgname).texinfo \ | |
122 | $(DISTNAME)/doc/$(pkgname).texi \ | |
120 | 123 | $(DISTNAME)/testsuite/check.sh \ |
121 | $(DISTNAME)/testsuite/logfile1 \ | |
122 | $(DISTNAME)/testsuite/logfile2 \ | |
124 | $(DISTNAME)/testsuite/logfile[1-5] \ | |
125 | $(DISTNAME)/testsuite/logfile2i \ | |
126 | $(DISTNAME)/testsuite/logfile_blank \ | |
123 | 127 | $(DISTNAME)/testsuite/test.txt \ |
124 | 128 | $(DISTNAME)/testsuite/test1.txt \ |
125 | 129 | $(DISTNAME)/testsuite/test2.txt \ |
0 | Changes in version 1.17: | |
0 | Changes in version 1.18: | |
1 | 1 | |
2 | The new option "-l, --logfile-size" has been added. | |
2 | A bug has been fixed in the "or" and "xor" operations of ddrescuelog. | |
3 | 3 | |
4 | The new option "-w, --ignore-write-errors", which makes fill mode ignore | |
5 | write errors, has been added. | |
4 | The new option "-H, --test-mode" has been added. | |
6 | 5 | |
7 | The option "--fill" has been renamed to "--fill-mode". | |
6 | The new option "-L, --loose-domain" has been added to ddrescue and | |
7 | ddrescuelog. | |
8 | 8 | |
9 | The option '--generate-logfile' has been renamed to '--generate-mode'. | |
9 | The new option "-N, --no-trim" has been added. | |
10 | 10 | |
11 | The option "--sector-size" has been added as a synonym of "--block-size". | |
11 | The new option "-O, --reopen-on-error" has been added. | |
12 | 12 | |
13 | The option "--retries" has been added as a synonym of "--max-retries". | |
13 | The new options "-1, --log-rates" and "-2, --log-reads" have been added. | |
14 | They produce data files usable by plotting utilities like gnuplot. | |
14 | 15 | |
15 | The option "--size" has been added as a synonym of "--max-size". | |
16 | The option "-K, --skip-size" has been extended to accept a maximum skip | |
17 | size and to disable skipping. | |
16 | 18 | |
17 | Trimming is now done from both edges of each non-trimmed block. Largest | |
18 | blocks are trimmed first. | |
19 | The long name of option "-r" has been changed to "--retry-passes" to | |
20 | make it clear that ddrescue tries each sector only once per retry pass. | |
19 | 21 | |
20 | Largest blocks are now split first until logfile reaches | |
21 | "--logfile-size" entries. | |
22 | The short name of option "--generate-mode" has been changed to "-G". | |
22 | 23 | |
23 | Ddrescue now terminates with an error if an unexpected EOF would discard | |
24 | any successfully read data, just as it does when the input file | |
25 | disappears from /dev. | |
24 | The default value of option "-l, --logfile-size" has been increased to | |
25 | 10000 (from 1000). | |
26 | 26 | |
27 | During the copying phase, failed blocks containing one sector are marked | |
28 | as bad-sector instead of as non-trimmed. This avoids reading a sector | |
29 | twice when a cluster size of 1 is used. | |
27 | If ddrescue is interrupted by a signal, it now terminates by raising the | |
28 | signal received. | |
30 | 29 | |
31 | Ddrescue now removes the last block from the logfile if it starts at the | |
32 | end of the input file and is not marked as finished. This automatically | |
33 | adjusts the logfile to shrinking input devices, like CD-ROMs written in | |
34 | Track-At-Once mode. | |
30 | The algorithm of the copying phase has been changed. It now marks as | |
31 | non-trimmed just the failed blocks, skips beyond them, and tries the | |
32 | skipped areas later in two additional passes (before trimming), | |
33 | reversing the direction after each pass. The third pass is a sweeping | |
34 | pass, with skipping disabled. The new method is faster around large | |
35 | errors, keeps the logfile smaller, and produces better starting points | |
36 | for trimming, which now tries the smallest blocks first. | |
35 | 37 | |
36 | A bug has been fixed that prevented status to update after the system | |
37 | clock had been put back. | |
38 | Speed has been improved when using complex domains with option '-m, | |
39 | --domain-logfile'. | |
38 | 40 | |
39 | Slow reads are now ignored during the first 10 seconds. | |
41 | Current total run time is now shown on the screen. | |
40 | 42 | |
41 | Control-C no more overwrites status message. | |
43 | The pass number and direction are now shown during the copying phase. | |
42 | 44 | |
43 | Generate mode no more requires the "--force" option when outfile is not | |
44 | a regular file. | |
45 | The message "About to copy an unknown number of bytes" is now shown when | |
46 | the size of the input file can't be determined. | |
45 | 47 | |
46 | Ddrescuelog no more says that the logfile does not exist when it exists | |
47 | but is empty. | |
48 | The option "-B, --binary-prefixes" has been added to ddrescuelog. | |
48 | 49 | |
49 | The new chapter "Using ddrescue safely" has been added to the manual. | |
50 | The new option "-C, --complete-logfile" has been added to ddrescuelog. | |
50 | 51 | |
51 | The manual now explains that only whole sectors can be read when "direct | |
52 | disc access" is used. | |
52 | The new option "-P, --compare-as-domain" has been added to ddrescuelog. | |
53 | 53 | |
54 | "configure" now accepts options with a separate argument. | |
54 | Many improvements have been made to the documentation. | |
55 | 55 | |
56 | The target "install-bin" has been added to the Makefile. | |
56 | "ddrescue.texinfo" has been renamed to "ddrescue.texi". |
0 | 0 | Description |
1 | 1 | |
2 | 2 | GNU ddrescue is a data recovery tool. It copies data from one file or |
3 | block device (hard disc, cdrom, etc) to another, trying hard to rescue | |
4 | data in case of read errors. | |
3 | block device (hard disc, cdrom, etc) to another, trying to rescue the | |
4 | good parts first in case of read errors. | |
5 | 5 | |
6 | 6 | Ddrescuelog is a tool that manipulates ddrescue logfiles, shows logfile |
7 | 7 | contents, converts logfiles to/from other formats, compares logfiles, |
23 | 23 | gaps without wiping out the data already rescued. |
24 | 24 | |
25 | 25 | Automatic merging of backups: If you have two or more damaged copies of |
26 | a file, cdrom, etc, and run ddrescue on all of them, one at a time, | |
27 | with the same output file, you will probably obtain a complete and | |
28 | error-free file. This is so because the probability of having damaged | |
29 | areas at the same places on different input files is very low. Using | |
30 | the logfile, only the needed blocks are read from the second and | |
31 | successive copies. | |
26 | a file, cdrom, etc, and run ddrescue on all of them, one at a time, with | |
27 | the same output file, you will probably obtain a complete and error-free | |
28 | file. This is so because the probability of having the same area damaged | |
29 | in all copies is low. Using the logfile, only the needed blocks are read | |
30 | from the second and successive copies. | |
32 | 31 | |
33 | Ddrescue recommends lzip for compression of backups because of its | |
34 | reliability and data recovery capabilities, including error-checked | |
35 | merging of backup copies. Lziprecover makes lzip files resistant to | |
36 | bit-flip, one of the most common forms of data corruption, and its | |
37 | recovery capabilities contribute to make of the lzip format one of the | |
38 | best formats for long-term data archiving. The combination ddrescue + | |
39 | lziprecover is the best option for recovering data from multiple damaged | |
40 | copies. | |
32 | Ddrescue recommends lzip for compression of backups because the lzip | |
33 | format is designed for long-term data archiving and provides data | |
34 | recovery capabilities which nicely complement those of ddrescue. | |
35 | (Ddrescue fills unreadable sectors with data from other copies, while | |
36 | lziprecover corrects corrupt sectors with data from other copies). If | |
37 | the cause of file corruption is damaged media, the combination ddrescue | |
38 | + lziprecover is the best option for recovering data from multiple | |
39 | damaged copies. | |
41 | 40 | |
42 | 41 | Recordable CD and DVD media keep their data only for a finite time |
43 | 42 | (typically for many years). After that time, data loss develops slowly |
69 | 68 | data, marking bad areas or even, in some cases, "repair" damaged |
70 | 69 | sectors. |
71 | 70 | |
71 | One of the great strengths of ddrescue is that it is interface-agnostic, | |
72 | and so can be used for any kind of device supported by your kernel (ATA, | |
73 | SATA, SCSI, old MFM drives, floppy discs, or even flash media cards like | |
74 | SD). | |
72 | 75 | |
73 | Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 | |
74 | Antonio Diaz Diaz. | |
76 | ||
77 | Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, | |
78 | 2014 Antonio Diaz Diaz. | |
75 | 79 | |
76 | 80 | This file is free documentation: you have unlimited permission to copy, |
77 | 81 | distribute and modify it. |
0 | 0 | /* Arg_parser - POSIX/GNU command line argument parser. (C++ version) |
1 | Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 | |
1 | Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 | |
2 | 2 | Antonio Diaz Diaz. |
3 | 3 | |
4 | 4 | This library is free software: you can redistribute it and/or modify |
5 | 5 | it under the terms of the GNU General Public License as published by |
6 | the Free Software Foundation, either version 3 of the License, or | |
6 | the Free Software Foundation, either version 2 of the License, or | |
7 | 7 | (at your option) any later version. |
8 | 8 | |
9 | 9 | This library is distributed in the hope that it will be useful, |
119 | 119 | |
120 | 120 | if( index < 0 ) |
121 | 121 | { |
122 | error_ = "invalid option -- "; error_ += c; | |
122 | error_ = "invalid option -- '"; error_ += c; error_ += '\''; | |
123 | 123 | return false; |
124 | 124 | } |
125 | 125 | |
134 | 134 | { |
135 | 135 | if( !arg || !arg[0] ) |
136 | 136 | { |
137 | error_ = "option requires an argument -- "; error_ += c; | |
137 | error_ = "option requires an argument -- '"; error_ += c; | |
138 | error_ += '\''; | |
138 | 139 | return false; |
139 | 140 | } |
140 | 141 | data.back().argument = arg; ++argind; cind = 0; |
155 | 156 | while( argind < argc ) |
156 | 157 | { |
157 | 158 | const unsigned char ch1 = argv[argind][0]; |
158 | const unsigned char ch2 = ( ch1 ? argv[argind][1] : 0 ); | |
159 | const unsigned char ch2 = ch1 ? argv[argind][1] : 0; | |
159 | 160 | |
160 | 161 | if( ch1 == '-' && ch2 ) // we found an option |
161 | 162 | { |
162 | 163 | const char * const opt = argv[argind]; |
163 | const char * const arg = (argind + 1 < argc) ? argv[argind+1] : 0; | |
164 | const char * const arg = ( argind + 1 < argc ) ? argv[argind+1] : 0; | |
164 | 165 | if( ch2 == '-' ) |
165 | 166 | { |
166 | 167 | if( !argv[argind][2] ) { ++argind; break; } // we found "--" |
0 | 0 | /* Arg_parser - POSIX/GNU command line argument parser. (C++ version) |
1 | Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 | |
1 | Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 | |
2 | 2 | Antonio Diaz Diaz. |
3 | 3 | |
4 | 4 | This library is free software: you can redistribute it and/or modify |
5 | 5 | it under the terms of the GNU General Public License as published by |
6 | the Free Software Foundation, either version 3 of the License, or | |
6 | the Free Software Foundation, either version 2 of the License, or | |
7 | 7 | (at your option) any later version. |
8 | 8 | |
9 | 9 | This library is distributed in the hope that it will be useful, |
0 | 0 | /* GNU ddrescue - Data recovery tool |
1 | 1 | Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, |
2 | 2013 Antonio Diaz Diaz. | |
2 | 2013, 2014 Antonio Diaz Diaz. | |
3 | 3 | |
4 | 4 | This program is free software: you can redistribute it and/or modify |
5 | 5 | it under the terms of the GNU General Public License as published by |
20 | 20 | #include <algorithm> |
21 | 21 | #include <climits> |
22 | 22 | #include <cstdio> |
23 | #include <cstdlib> | |
23 | 24 | #include <string> |
24 | 25 | #include <vector> |
25 | 26 | #include <stdint.h> |
26 | 27 | |
27 | 28 | #include "block.h" |
28 | #include "ddrescue.h" | |
29 | 29 | |
30 | 30 | |
31 | 31 | // Align pos to next boundary if size is big enough |
65 | 65 | { |
66 | 66 | if( this->follows( b ) ) pos_ = b.pos_; |
67 | 67 | else if( !b.follows( *this ) ) return false; |
68 | if( b.size_ > LLONG_MAX - end() ) | |
69 | internal_error( "size overflow joining two Blocks." ); | |
68 | 70 | size_ += b.size_; |
69 | if( size_ < 0 || size_ > LLONG_MAX - pos_ ) | |
70 | internal_error( "size overflow joining two Blocks" ); | |
71 | 71 | return true; |
72 | } | |
73 | ||
74 | ||
75 | // shift the border of two consecutive Blocks | |
76 | void Block::shift( Block & b, const long long pos ) | |
77 | { | |
78 | if( end() != b.pos_ || pos <= pos_ || pos >= b.end() ) | |
79 | internal_error( "bad argument shifting the border of two Blocks." ); | |
80 | b.size_ = b.end() - pos; b.pos_ = pos; size_ = pos - pos_; | |
72 | 81 | } |
73 | 82 | |
74 | 83 | |
85 | 94 | } |
86 | 95 | |
87 | 96 | |
88 | void Domain::crop( const Block & b ) | |
97 | Domain::Domain( const long long p, const long long s, | |
98 | const char * const logname, const bool loose ) | |
89 | 99 | { |
90 | for( unsigned i = block_vector.size(); i > 0; ) | |
100 | const Block b( p, s ); | |
101 | if( !logname || !logname[0] ) { block_vector.push_back( b ); return; } | |
102 | Logfile logfile( logname ); | |
103 | if( !logfile.read_logfile( loose ? '?' : 0 ) ) | |
91 | 104 | { |
92 | block_vector[--i].crop( b ); | |
93 | if( block_vector[i].size() <= 0 ) | |
94 | block_vector.erase( block_vector.begin() + i ); | |
105 | char buf[80]; | |
106 | snprintf( buf, sizeof buf, | |
107 | "Logfile '%s' does not exist or is not readable.", logname ); | |
108 | show_error( buf ); | |
109 | std::exit( 1 ); | |
95 | 110 | } |
96 | if( block_vector.size() == 0 ) block_vector.push_back( Block( 0, 0 ) ); | |
111 | logfile.compact_sblock_vector(); | |
112 | for( int i = 0; i < logfile.sblocks(); ++i ) | |
113 | { | |
114 | const Sblock & sb = logfile.sblock( i ); | |
115 | if( sb.status() == Sblock::finished ) block_vector.push_back( sb ); | |
116 | } | |
117 | if( block_vector.empty() ) block_vector.push_back( Block( 0, 0 ) ); | |
118 | else this->crop( b ); | |
97 | 119 | } |
98 | 120 | |
99 | 121 | |
100 | void Domain::crop_by_file_size( const long long end ) | |
122 | void Domain::crop( const Block & b ) | |
101 | 123 | { |
102 | unsigned i = block_vector.size(); | |
103 | while( i > 0 && block_vector[i-1].pos() >= end ) --i; | |
104 | if( i == 0 ) | |
105 | block_vector[0].assign( 0, 0 ); | |
106 | else | |
107 | { | |
108 | Block & b = block_vector[--i]; | |
109 | if( b.includes( end ) ) b.size( end - b.pos() ); | |
110 | } | |
111 | block_vector.erase( block_vector.begin() + i + 1, block_vector.end() ); | |
124 | unsigned r = block_vector.size(); | |
125 | while( r > 0 && b < block_vector[r-1] ) --r; | |
126 | if( r > 0 ) block_vector[r-1].crop( b ); | |
127 | if( r <= 0 || block_vector[r-1].size() <= 0 ) // no block overlaps b | |
128 | { block_vector.clear(); block_vector.push_back( Block( 0, 0 ) ); return; } | |
129 | if( r < block_vector.size() ) // remove blocks beyond b | |
130 | block_vector.erase( block_vector.begin() + r, block_vector.end() ); | |
131 | if( b.pos() <= 0 ) return; | |
132 | --r; // block_vector[r] is now the last non-cropped-out block | |
133 | unsigned l = 0; | |
134 | while( l < r && block_vector[l] < b ) ++l; | |
135 | if( l < r ) block_vector[l].crop( b ); // crop block overlapping b | |
136 | if( l > 0 ) // remove blocks before b | |
137 | block_vector.erase( block_vector.begin(), block_vector.begin() + l ); | |
112 | 138 | } |
0 | 0 | /* GNU ddrescue - Data recovery tool |
1 | 1 | Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, |
2 | 2013 Antonio Diaz Diaz. | |
2 | 2013, 2014 Antonio Diaz Diaz. | |
3 | 3 | |
4 | 4 | This program is free software: you can redistribute it and/or modify |
5 | 5 | it under the terms of the GNU General Public License as published by |
28 | 28 | |
29 | 29 | class Block |
30 | 30 | { |
31 | long long pos_, size_; // pos + size <= LLONG_MAX | |
32 | ||
33 | public: | |
34 | Block( const long long p, const long long s ) | |
35 | : pos_( p ), size_( s ) {} | |
31 | long long pos_, size_; // pos >= 0 && size >= 0 && pos + size <= LLONG_MAX | |
32 | ||
33 | void fix_size() // limit size_ to largest possible value | |
34 | { if( size_ < 0 || size_ > LLONG_MAX - pos_ ) size_ = LLONG_MAX - pos_; } | |
35 | ||
36 | public: | |
37 | Block( const long long p, const long long s ) : pos_( p ), size_( s ) | |
38 | { if( p < 0 ) { pos_ = 0; if( s > 0 ) size_ -= std::min( s, -p ); } | |
39 | fix_size(); } | |
36 | 40 | |
37 | 41 | long long pos() const { return pos_; } |
38 | 42 | long long size() const { return size_; } |
39 | 43 | long long end() const { return pos_ + size_; } |
40 | 44 | |
41 | void pos( const long long p ) { pos_ = p; } | |
42 | void size( const long long s ) { size_ = s; } | |
43 | void end( const long long e ) | |
44 | { pos_ = e - size_; if( pos_ < 0 ) { size_ += pos_; pos_ = 0; } } | |
45 | void pos( const long long p ) | |
46 | { pos_ = std::max( p, 0LL ); | |
47 | if( size_ > LLONG_MAX - pos_ ) size_ = LLONG_MAX - pos_; } | |
48 | void size( const long long s ) { size_ = s; fix_size(); } | |
49 | void end( long long e ) | |
50 | { if( e < 0 ) e = LLONG_MAX; | |
51 | if( size_ <= e ) pos_ = e - size_; else { pos_ = 0; size_ = e; } } | |
45 | 52 | Block & assign( const long long p, const long long s ) |
46 | { pos_ = p; size_ = s; return *this; } | |
47 | ||
48 | void fix_size() // limit size_ to largest possible value | |
49 | { if( size_ < 0 || size_ > LLONG_MAX - pos_ ) size_ = LLONG_MAX - pos_; } | |
53 | { | |
54 | pos_ = p; size_ = s; | |
55 | if( p < 0 ) { pos_ = 0; if( s > 0 ) size_ -= std::min( s, -p ); } | |
56 | fix_size(); return *this; | |
57 | } | |
58 | ||
50 | 59 | void align_pos( const int alignment ); |
51 | 60 | void align_end( const int alignment ); |
52 | void inc_size( const long long delta ) { size_ += delta; } | |
53 | 61 | |
54 | 62 | bool operator==( const Block & b ) const |
55 | 63 | { return pos_ == b.pos_ && size_ == b.size_; } |
56 | 64 | bool operator!=( const Block & b ) const |
57 | 65 | { return pos_ != b.pos_ || size_ != b.size_; } |
66 | ||
67 | bool operator<( const Block & b ) const { return ( end() <= b.pos() ); } | |
58 | 68 | |
59 | 69 | bool follows( const Block & b ) const |
60 | 70 | { return ( pos_ == b.end() ); } |
62 | 72 | { return ( pos_ <= b.pos_ && end() >= b.end() ); } |
63 | 73 | bool includes( const long long pos ) const |
64 | 74 | { return ( pos_ <= pos && end() > pos ); } |
75 | bool strictly_includes( const long long pos ) const | |
76 | { return ( pos_ < pos && end() > pos ); } | |
65 | 77 | |
66 | 78 | void crop( const Block & b ); |
67 | 79 | bool join( const Block & b ); |
80 | void shift( Block & b, const long long pos ); | |
68 | 81 | Block split( long long pos, const int hardbs = 1 ); |
69 | 82 | }; |
70 | 83 | |
107 | 120 | std::vector< Block > block_vector; // blocks are ordered and don't overlap |
108 | 121 | |
109 | 122 | public: |
110 | Domain( const long long p, const long long s, const char * const logname = 0 ); | |
111 | ||
112 | long long pos() const { return block_vector[0].pos(); } | |
113 | long long size() const | |
114 | { return block_vector.back().end() - block_vector[0].pos(); } | |
123 | Domain( const long long p, const long long s, | |
124 | const char * const logname = 0, const bool loose = false ); | |
125 | ||
126 | long long pos() const { return block_vector.front().pos(); } | |
115 | 127 | long long end() const { return block_vector.back().end(); } |
128 | long long size() const { return end() - pos(); } | |
129 | const Block & block( const int i ) const { return block_vector[i]; } | |
116 | 130 | int blocks() const { return (int)block_vector.size(); } |
131 | bool empty() const { return ( end() <= pos() ); } | |
132 | bool full() const { return ( !empty() && end() >= LLONG_MAX ); } | |
117 | 133 | |
118 | 134 | long long in_size() const |
119 | 135 | { |
131 | 147 | return false; |
132 | 148 | } |
133 | 149 | |
134 | bool operator<( const Block & b ) const | |
135 | { return ( block_vector.back().end() <= b.pos() ); } | |
136 | ||
137 | long long breaks_block_by( const Block & b ) const | |
138 | { | |
139 | for( unsigned i = 0; i < block_vector.size(); ++i ) | |
150 | bool operator<( const Block & b ) const { return ( end() <= b.pos() ); } | |
151 | ||
152 | bool includes( const Block & b ) const | |
153 | { | |
154 | unsigned l = 0, r = block_vector.size(); | |
155 | while( l < r ) | |
140 | 156 | { |
141 | const Block & db = block_vector[i]; | |
142 | if( b.includes( db.pos() ) && b.pos() < db.pos() ) return db.pos(); | |
143 | const long long end = db.end(); | |
144 | if( b.includes( end ) && b.pos() < end ) return end; | |
157 | const int m = ( l + r ) / 2; | |
158 | const Block & db = block_vector[m]; | |
159 | if( db.includes( b ) ) return true; | |
160 | if( db < b ) l = m + 1; else if( b < db ) r = m; else break; | |
145 | 161 | } |
146 | return 0; | |
147 | } | |
148 | ||
149 | bool includes( const Block & b ) const | |
150 | { | |
151 | for( unsigned i = 0; i < block_vector.size(); ++i ) | |
152 | if( block_vector[i].includes( b ) ) return true; | |
153 | 162 | return false; |
154 | 163 | } |
155 | 164 | |
163 | 172 | void clear() |
164 | 173 | { block_vector.clear(); block_vector.push_back( Block( 0, 0 ) ); } |
165 | 174 | void crop( const Block & b ); |
166 | void crop_by_file_size( const long long end ); | |
167 | }; | |
175 | void crop_by_file_size( const long long size ) { crop( Block( 0, size ) ); } | |
176 | }; | |
177 | ||
178 | ||
179 | class Logfile | |
180 | { | |
181 | public: | |
182 | enum Status | |
183 | { copying = '?', trimming = '*', splitting = '/', retrying = '-', | |
184 | filling = 'F', generating = 'G', finished = '+' }; | |
185 | ||
186 | private: | |
187 | long long current_pos_; | |
188 | const char * const filename_; | |
189 | std::string current_msg; | |
190 | Status current_status_; | |
191 | mutable int index_; // cached index of last find or change | |
192 | bool read_only_; | |
193 | std::vector< Sblock > sblock_vector; // note: blocks are consecutive | |
194 | ||
195 | void erase_sblock( const int i ) | |
196 | { sblock_vector.erase( sblock_vector.begin() + i ); } | |
197 | void insert_sblock( const int i, const Sblock & sb ) | |
198 | { sblock_vector.insert( sblock_vector.begin() + i, sb ); } | |
199 | ||
200 | public: | |
201 | explicit Logfile( const char * const logname ) | |
202 | : current_pos_( 0 ), filename_( logname ), current_status_( copying ), | |
203 | index_( 0 ), read_only_( false ) {} | |
204 | ||
205 | void compact_sblock_vector(); | |
206 | void extend_sblock_vector( const long long isize ); | |
207 | bool truncate_vector( const long long end, const bool force = false ); | |
208 | void make_blank() | |
209 | { sblock_vector.clear(); | |
210 | sblock_vector.push_back( Sblock( 0, -1, Sblock::non_tried ) ); } | |
211 | bool read_logfile( const int default_sblock_status = 0 ); | |
212 | int write_logfile( FILE * f = 0, const bool timestamp = false ) const; | |
213 | ||
214 | bool blank() const; | |
215 | long long current_pos() const { return current_pos_; } | |
216 | Status current_status() const { return current_status_; } | |
217 | const char * filename() const { return filename_; } | |
218 | bool read_only() const { return read_only_; } | |
219 | ||
220 | void current_pos( const long long pos ) { current_pos_ = pos; } | |
221 | void current_status( const Status st, const char * const msg = "" ) | |
222 | { current_status_ = st; | |
223 | current_msg = ( st == finished ) ? "Finished" : msg; } | |
224 | ||
225 | Block extent() const | |
226 | { if( sblock_vector.empty() ) return Block( 0, 0 ); | |
227 | return Block( sblock_vector.front().pos(), | |
228 | sblock_vector.back().end() - sblock_vector.front().pos() ); } | |
229 | const Sblock & sblock( const int i ) const { return sblock_vector[i]; } | |
230 | int sblocks() const { return (int)sblock_vector.size(); } | |
231 | void change_sblock_status( const int i, const Sblock::Status st ) | |
232 | { sblock_vector[i].status( st ); } | |
233 | ||
234 | void split_by_domain_borders( const Domain & domain ); | |
235 | void split_by_logfile_borders( const Logfile & logfile ); | |
236 | bool try_split_sblock_by( const long long pos, const int i ) | |
237 | { | |
238 | if( sblock_vector[i].strictly_includes( pos ) ) | |
239 | { insert_sblock( i, sblock_vector[i].split( pos ) ); return true; } | |
240 | return false; | |
241 | } | |
242 | ||
243 | int find_index( const long long pos ) const; | |
244 | int find_largest_sblock( const Sblock::Status st, | |
245 | const Domain & domain ) const; | |
246 | int find_smallest_sblock( const Sblock::Status st, | |
247 | const Domain & domain, const int min_size ) const; | |
248 | void find_chunk( Block & b, const Sblock::Status st, | |
249 | const Domain & domain, const int alignment = 1 ) const; | |
250 | void rfind_chunk( Block & b, const Sblock::Status st, | |
251 | const Domain & domain, const int alignment = 1 ) const; | |
252 | int change_chunk_status( const Block & b, const Sblock::Status st, | |
253 | const Domain & domain ); | |
254 | ||
255 | static bool isstatus( const int st ) | |
256 | { return ( st == copying || st == trimming || st == splitting || | |
257 | st == retrying || st == filling || st == generating || | |
258 | st == finished ); } | |
259 | static const char * status_name( const Status st ); | |
260 | }; | |
261 | ||
262 | ||
263 | // Defined in main_common.cc | |
264 | // | |
265 | extern int verbosity; | |
266 | void show_error( const char * const msg, | |
267 | const int errcode = 0, const bool help = false ); | |
268 | void internal_error( const char * const msg ); | |
269 | int empty_domain(); | |
270 | int not_readable( const char * const logname ); | |
271 | int not_writable( const char * const logname ); | |
272 | long initial_time(); | |
273 | bool write_logfile_header( FILE * const f, const char * const logtype ); | |
274 | bool write_timestamp( FILE * const f ); | |
275 | bool write_final_timestamp( FILE * const f ); | |
276 | const char * format_num( long long num, long long limit = 999999, | |
277 | const int set_prefix = 0 ); |
0 | 0 | #! /bin/sh |
1 | 1 | # configure script for GNU ddrescue - Data recovery tool |
2 | 2 | # Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, |
3 | # 2013 Antonio Diaz Diaz. | |
3 | # 2013, 2014 Antonio Diaz Diaz. | |
4 | 4 | # |
5 | 5 | # This configure script is free software: you have unlimited permission |
6 | 6 | # to copy, distribute and modify it. |
7 | 7 | |
8 | 8 | pkgname=ddrescue |
9 | pkgversion=1.17 | |
9 | pkgversion=1.18.1 | |
10 | 10 | progname=ddrescue |
11 | srctrigger=ddrescue.h | |
11 | srctrigger=doc/${pkgname}.texi | |
12 | 12 | |
13 | 13 | # clear some things potentially inherited from environment. |
14 | 14 | LC_ALL=C |
64 | 64 | echo " --datarootdir=DIR base directory for doc and data [${datarootdir}]" |
65 | 65 | echo " --infodir=DIR info files directory [${infodir}]" |
66 | 66 | echo " --mandir=DIR man pages directory [${mandir}]" |
67 | echo " CXX=COMPILER C++ compiler to use [g++]" | |
67 | echo " CXX=COMPILER C++ compiler to use [${CXX}]" | |
68 | 68 | echo " CPPFLAGS=OPTIONS command line options for the preprocessor [${CPPFLAGS}]" |
69 | 69 | echo " CXXFLAGS=OPTIONS command line options for the C++ compiler [${CXXFLAGS}]" |
70 | 70 | echo " LDFLAGS=OPTIONS command line options for the linker [${LDFLAGS}]" |
166 | 166 | cat > Makefile << EOF |
167 | 167 | # Makefile for GNU ddrescue - Data recovery tool |
168 | 168 | # Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, |
169 | # 2013 Antonio Diaz Diaz. | |
169 | # 2013, 2014 Antonio Diaz Diaz. | |
170 | 170 | # This file was generated automatically by configure. Do not edit. |
171 | 171 | # |
172 | 172 | # This Makefile is free software: you have unlimited permission |
0 | 0 | /* GNU ddrescue - Data recovery tool |
1 | 1 | Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, |
2 | 2013 Antonio Diaz Diaz. | |
2 | 2013, 2014 Antonio Diaz Diaz. | |
3 | 3 | |
4 | 4 | This program is free software: you can redistribute it and/or modify |
5 | 5 | it under the terms of the GNU General Public License as published by |
15 | 15 | along with this program. If not, see <http://www.gnu.org/licenses/>. |
16 | 16 | */ |
17 | 17 | |
18 | class Logbook | |
19 | { | |
20 | public: | |
21 | enum Status | |
22 | { copying = '?', trimming = '*', splitting = '/', retrying = '-', | |
23 | filling = 'F', generating = 'G', finished = '+' }; | |
24 | ||
25 | private: | |
18 | class Logbook : public Logfile | |
19 | { | |
26 | 20 | const long long offset_; // outfile offset (opos - ipos); |
27 | long long current_pos_; | |
28 | 21 | long long logfile_isize_; |
29 | Status current_status_; | |
30 | 22 | Domain & domain_; // rescue domain |
31 | 23 | uint8_t *iobuf_base, *iobuf_; // iobuf is aligned to page and hardbs |
32 | const char * const filename_; | |
33 | 24 | const int hardbs_, softbs_; |
34 | 25 | const char * final_msg_; |
35 | 26 | int final_errno_; |
36 | mutable int index_; // cached index of last find or change | |
37 | std::vector< Sblock > sblock_vector; // note: blocks are consecutive | |
38 | 27 | long ul_t1; // variable for update_logfile |
39 | 28 | bool logfile_exists_; |
40 | 29 | |
41 | 30 | Logbook( const Logbook & ); // declared as private |
42 | 31 | void operator=( const Logbook & ); // declared as private |
43 | 32 | |
44 | void erase_sblock( const int i ) | |
45 | { sblock_vector.erase( sblock_vector.begin() + i ); } | |
46 | void insert_sblock( const int i, const Sblock & sb ) | |
47 | { sblock_vector.insert( sblock_vector.begin() + i, sb ); } | |
48 | void split_domain_border_sblocks(); | |
49 | ||
50 | public: | |
51 | Logbook( const long long offset, const long long isize, | |
52 | Domain & dom, const char * const logname, | |
53 | const int cluster, const int hardbs, | |
54 | const bool complete_only, const bool do_not_read = false ); | |
33 | public: | |
34 | Logbook( const long long offset, const long long isize, Domain & dom, | |
35 | const char * const logname, const int cluster, | |
36 | const int hardbs, const bool complete_only ); | |
55 | 37 | ~Logbook() { delete[] iobuf_base; } |
56 | 38 | |
57 | bool blank() const; | |
58 | void compact_sblock_vector(); | |
59 | bool update_logfile( const int odes = -1, const bool force = false, | |
60 | const bool retry = true ); | |
61 | void write_logfile( FILE * const f ) const; | |
62 | ||
63 | long long current_pos() const { return current_pos_; } | |
64 | Status current_status() const { return current_status_; } | |
39 | bool update_logfile( const int odes = -1, const bool force = false ); | |
40 | ||
65 | 41 | const Domain & domain() const { return domain_; } |
66 | const char * filename() const { return filename_; } | |
67 | 42 | uint8_t * iobuf() const { return iobuf_; } |
68 | 43 | int hardbs() const { return hardbs_; } |
69 | 44 | int softbs() const { return softbs_; } |
73 | 48 | bool logfile_exists() const { return logfile_exists_; } |
74 | 49 | long long logfile_isize() const { return logfile_isize_; } |
75 | 50 | |
76 | void current_pos( const long long pos ) { current_pos_ = pos; } | |
77 | void current_status( const Status st ) { current_status_ = st; } | |
78 | void final_msg( const char * const msg ) { final_msg_ = msg; } | |
79 | void final_errno( const int e ) { final_errno_ = e; } | |
80 | ||
81 | const Sblock & sblock( const int i ) const | |
82 | { return sblock_vector[i]; } | |
83 | int sblocks() const { return (int)sblock_vector.size(); } | |
84 | void change_sblock_status( const int i, const Sblock::Status st ) | |
85 | { sblock_vector[i].status( st ); } | |
86 | void split_sblock_by( const long long pos, const int i ) | |
87 | { | |
88 | if( sblock_vector[i].includes( pos ) ) | |
89 | insert_sblock( i, sblock_vector[i].split( pos ) ); | |
90 | } | |
91 | bool truncate_vector( const long long end, const bool force = false ); | |
51 | void final_msg( const char * const msg, const int e = 0 ) | |
52 | { final_msg_ = msg; final_errno_ = e; } | |
53 | ||
92 | 54 | void truncate_domain( const long long end ) |
93 | 55 | { domain_.crop_by_file_size( end ); } |
94 | ||
95 | int find_index( const long long pos ) const; | |
96 | int find_largest_sblock( const Sblock::Status st ) const; | |
97 | int find_smallest_sblock( const Sblock::Status st ) const; | |
98 | void find_chunk( Block & b, const Sblock::Status st, | |
99 | const int alignment = 0 ) const; | |
100 | void rfind_chunk( Block & b, const Sblock::Status st, | |
101 | const int alignment = 0 ) const; | |
102 | int change_chunk_status( const Block & b, const Sblock::Status st ); | |
103 | ||
104 | static bool isstatus( const int st ) | |
105 | { return ( st == copying || st == trimming || st == splitting || | |
106 | st == retrying || st == filling || st == generating || | |
107 | st == finished ); } | |
108 | static const char * status_name( const Status st ); | |
109 | 56 | }; |
110 | 57 | |
111 | 58 | |
121 | 68 | // variables for show_status |
122 | 69 | long long a_rate, c_rate, first_size, last_size; |
123 | 70 | long long last_ipos; |
124 | long t0, t1; | |
71 | long t0, t1; // start, current times | |
72 | int oldlen; | |
125 | 73 | |
126 | 74 | int fill_areas( const std::string & filltypes ); |
127 | 75 | int fill_block( const Block & b ); |
128 | void show_status( const long long ipos, bool force = false ); | |
76 | void show_status( const long long ipos, const char * const msg = 0, | |
77 | bool force = false ); | |
129 | 78 | |
130 | 79 | public: |
131 | 80 | Fillbook( const long long offset, Domain & dom, |
135 | 84 | ignore_write_errors_( ignore_write_errors ), |
136 | 85 | synchronous_( synchronous ), |
137 | 86 | a_rate( 0 ), c_rate( 0 ), first_size( 0 ), last_size( 0 ), |
138 | last_ipos( 0 ), t0( 0 ), t1( 0 ) | |
87 | last_ipos( 0 ), t0( 0 ), t1( 0 ), oldlen( 0 ) | |
139 | 88 | {} |
140 | 89 | |
141 | 90 | int do_fill( const int odes, const std::string & filltypes ); |
150 | 99 | // variables for show_status |
151 | 100 | long long a_rate, c_rate, first_size, last_size; |
152 | 101 | long long last_ipos; |
153 | long t0, t1; | |
102 | long t0, t1; // start, current times | |
154 | 103 | int oldlen; |
155 | 104 | |
156 | 105 | void check_block( const Block & b, int & copied_size, int & error_size ); |
172 | 121 | |
173 | 122 | struct Rb_options |
174 | 123 | { |
175 | enum { default_skipbs = 65536, max_skipbs = 1 << 30 }; | |
124 | enum { default_skipbs = 65536, max_max_skipbs = 1 << 30 }; | |
176 | 125 | |
177 | 126 | long long max_error_rate; |
178 | 127 | long long min_outfile_size; |
181 | 130 | int max_errors; |
182 | 131 | int max_logfile_size; |
183 | 132 | int max_retries; |
133 | int o_direct; // O_DIRECT or 0 | |
184 | 134 | int skipbs; // initial size to skip on read error |
135 | int max_skipbs; // maximum size to skip on read error | |
185 | 136 | bool complete_only; |
186 | 137 | bool new_errors_only; |
187 | 138 | bool nosplit; |
139 | bool notrim; | |
140 | bool reopen_on_error; | |
188 | 141 | bool retrim; |
142 | bool reverse; | |
189 | 143 | bool sparse; |
190 | 144 | bool try_again; |
191 | 145 | |
192 | 146 | Rb_options() |
193 | 147 | : max_error_rate( -1 ), min_outfile_size( -1 ), min_read_rate( -1 ), |
194 | timeout( -1 ), max_errors( -1 ), max_logfile_size( 1000 ), | |
195 | max_retries( 0 ), skipbs( default_skipbs ), complete_only( false ), | |
196 | new_errors_only( false ), nosplit( false ), retrim( false ), | |
148 | timeout( -1 ), max_errors( -1 ), max_logfile_size( 10000 ), | |
149 | max_retries( 0 ), o_direct( 0 ), skipbs( default_skipbs ), | |
150 | max_skipbs( max_max_skipbs ), complete_only( false ), | |
151 | new_errors_only( false ), nosplit( false ), notrim( false ), | |
152 | reopen_on_error( false ), retrim( false ), reverse( false ), | |
197 | 153 | sparse( false ), try_again( false ) |
198 | 154 | {} |
199 | 155 | |
203 | 159 | min_read_rate == o.min_read_rate && timeout == o.timeout && |
204 | 160 | max_errors == o.max_errors && |
205 | 161 | max_logfile_size == o.max_logfile_size && |
206 | max_retries == o.max_retries && skipbs == o.skipbs && | |
162 | max_retries == o.max_retries && o_direct == o.o_direct && | |
163 | skipbs == o.skipbs && max_skipbs == o.max_skipbs && | |
207 | 164 | complete_only == o.complete_only && |
208 | 165 | new_errors_only == o.new_errors_only && |
209 | nosplit == o.nosplit && retrim == o.retrim && | |
166 | nosplit == o.nosplit && notrim == o.notrim && | |
167 | reopen_on_error == o.reopen_on_error && | |
168 | retrim == o.retrim && reverse == o.reverse && | |
210 | 169 | sparse == o.sparse && try_again == o.try_again ); } |
211 | 170 | bool operator!=( const Rb_options & o ) const |
212 | 171 | { return !( *this == o ); } |
218 | 177 | long long error_rate; |
219 | 178 | long long sparse_size; // end position of pending writes |
220 | 179 | long long recsize, errsize; // total recovered and error sizes |
180 | const Domain * const test_domain; // good/bad map for test mode | |
221 | 181 | const char * const iname_; |
222 | const int max_skip_size; // maximum size to skip on read error | |
223 | 182 | int e_code; // error code for errors_or_timeout |
224 | 183 | // 1 rate, 2 errors, 4 timeout |
225 | 184 | int errors; // error areas found so far |
226 | 185 | int ides_, odes_; // input and output file descriptors |
227 | const bool synchronous_; | |
186 | const bool access_works, synchronous_; | |
228 | 187 | // variables for update_rates |
229 | 188 | long long a_rate, c_rate, first_size, last_size; |
230 | 189 | long long last_ipos; |
231 | 190 | long t0, t1, ts; // start, current, last successful |
232 | 191 | int oldlen; |
233 | 192 | bool rates_updated; |
193 | bool first_post; // variable for show_status | |
234 | 194 | |
235 | 195 | bool extend_outfile_size(); |
236 | 196 | int copy_block( const Block & b, int & copied_size, int & error_size ); |
241 | 201 | void reduce_min_read_rate() |
242 | 202 | { if( min_read_rate > 0 ) min_read_rate /= 10; } |
243 | 203 | bool slow_read() const |
244 | { return ( t1 - t0 >= 10 && // no slow reads for first 10s | |
204 | { return ( t1 - t0 >= 30 && // no slow reads for first 30s | |
245 | 205 | ( ( min_read_rate > 0 && c_rate < min_read_rate && |
246 | 206 | c_rate < a_rate / 2 ) || |
247 | 207 | ( min_read_rate == 0 && c_rate < a_rate / 10 ) ) ); } |
248 | int copy_and_update( const Block & b, const Sblock::Status st, | |
249 | int & copied_size, int & error_size, | |
250 | const char * const msg, bool & first_post, | |
251 | const bool forward ); | |
208 | int update( const Block & b, const Sblock::Status st, | |
209 | const int copied_size, const int error_size ); | |
210 | int copy_and_update( const Block & b, int & error_size, | |
211 | const char * const msg, const bool forward ); | |
212 | int copy_and_update2( const Block & b, int & copied_size, | |
213 | int & error_size, const char * const msg, | |
214 | const bool forward, const bool small_try ); | |
215 | bool reopen_infile(); | |
252 | 216 | int copy_non_tried(); |
253 | int rcopy_non_tried(); | |
217 | int fcopy_non_tried( const char * const msg, const int pass ); | |
218 | int rcopy_non_tried( const char * const msg, const int pass ); | |
254 | 219 | int trim_errors(); |
255 | int split_errors( const bool reverse ); | |
220 | int split_errors(); | |
256 | 221 | int copy_errors(); |
257 | 222 | int rcopy_errors(); |
258 | 223 | void update_rates( const bool force = false ); |
260 | 225 | const bool force = false ); |
261 | 226 | public: |
262 | 227 | Rescuebook( const long long offset, const long long isize, |
263 | Domain & dom, const Rb_options & rb_opts, | |
264 | const char * const iname, const char * const logname, | |
265 | const int cluster, const int hardbs, | |
266 | const bool synchronous ); | |
267 | ||
268 | int do_rescue( const int ides, const int odes, const bool reverse ); | |
228 | Domain & dom, const Domain * const test_dom, | |
229 | const Rb_options & rb_opts, const char * const iname, | |
230 | const char * const logname, const int cluster, | |
231 | const int hardbs, const bool synchronous ); | |
232 | ||
233 | int do_rescue( const int ides, const int odes ); | |
269 | 234 | }; |
270 | 235 | |
271 | 236 | |
287 | 252 | const char * format_time( long t ); |
288 | 253 | bool interrupted(); |
289 | 254 | void set_signals(); |
290 | ||
291 | ||
292 | // Defined in main_common.cc | |
293 | // | |
294 | extern int verbosity; | |
295 | void internal_error( const char * const msg ); | |
296 | void show_error( const char * const msg, | |
297 | const int errcode = 0, const bool help = false ); | |
298 | void write_logfile_header( FILE * const f ); | |
299 | const char * format_num( long long num, long long limit = 999999, | |
300 | const int set_prefix = 0 ); | |
255 | int signaled_exit(); |
0 | 0 | /* GNU ddrescuelog - Tool for ddrescue logfiles |
1 | Copyright (C) 2011, 2012, 2013 Antonio Diaz Diaz. | |
1 | Copyright (C) 2011, 2012, 2013, 2014 Antonio Diaz Diaz. | |
2 | 2 | |
3 | 3 | This program is free software: you can redistribute it and/or modify |
4 | 4 | it under the terms of the GNU General Public License as published by |
20 | 20 | (eg, bug) which caused ddrescuelog to panic. |
21 | 21 | */ |
22 | 22 | |
23 | #include <algorithm> | |
23 | 24 | #include <cerrno> |
24 | 25 | #include <climits> |
25 | 26 | #include <cstdio> |
26 | 27 | #include <cstdlib> |
27 | 28 | #include <cstring> |
29 | #include <ctime> | |
28 | 30 | #include <string> |
29 | 31 | #include <vector> |
30 | 32 | #include <stdint.h> |
40 | 42 | const char * const program_name = "ddrescuelog"; |
41 | 43 | const char * invocation_name = 0; |
42 | 44 | |
43 | enum Mode { m_none, m_and, m_change, m_compare, m_create, m_delete, | |
44 | m_done_st, m_invert, m_list, m_or, m_status, m_xor }; | |
45 | enum Mode { m_none, m_and, m_change, m_compare, m_complete, m_create, | |
46 | m_delete, m_done_st, m_invert, m_list, m_or, m_status, m_xor }; | |
45 | 47 | |
46 | 48 | |
47 | 49 | void show_help( const int hardbs ) |
51 | 53 | "other formats, compares them, and tests rescue status.\n" |
52 | 54 | "\nUsage: %s [options] logfile\n", invocation_name ); |
53 | 55 | std::printf( "\nOptions:\n" |
54 | " -h, --help display this help and exit\n" | |
55 | " -V, --version output version information and exit\n" | |
56 | " -a, --change-types=<ot>,<nt> change the block types of a logfile\n" | |
57 | " -b, --block-size=<bytes> block size in bytes [default %d]\n", hardbs ); | |
58 | std::printf( " -c, --create-logfile[=<tt>] create logfile from list of blocks [+-]\n" | |
59 | " -d, --delete-if-done delete the logfile if rescue is finished\n" | |
60 | " -D, --done-status return 0 if rescue is finished\n" | |
61 | " -f, --force overwrite existing output files\n" | |
62 | " -i, --input-position=<bytes> starting position of rescue domain [0]\n" | |
63 | " -l, --list-blocks=<types> print block numbers of given types (?*/-+)\n" | |
64 | " -m, --domain-logfile=<file> restrict domain to finished blocks in file\n" | |
65 | " -n, --invert-logfile invert block types (finished <-> others)\n" | |
66 | " -o, --output-position=<bytes> starting position in output file [ipos]\n" | |
67 | " -p, --compare-logfile=<file> compare block types in domain of both files\n" | |
68 | " -q, --quiet suppress all messages\n" | |
69 | " -s, --size=<bytes> maximum size of rescue domain to be processed\n" | |
70 | " -t, --show-status show a summary of logfile contents\n" | |
71 | " -v, --verbose be verbose (a 2nd -v gives more)\n" | |
72 | " -x, --xor-logfile=<file> XOR the finished blocks in file with logfile\n" | |
73 | " -y, --and-logfile=<file> AND the finished blocks in file with logfile\n" | |
74 | " -z, --or-logfile=<file> OR the finished blocks in file with logfile\n" | |
75 | "Numbers may be followed by a multiplier: s = sectors, k = kB = 10^3 = 1000,\n" | |
76 | "Ki = KiB = 2^10 = 1024, M = 10^6, Mi = 2^20, G = 10^9, Gi = 2^30, etc...\n" | |
56 | " -h, --help display this help and exit\n" | |
57 | " -V, --version output version information and exit\n" | |
58 | " -a, --change-types=<ot>,<nt> change the block types of logfile\n" | |
59 | " -b, --block-size=<bytes> block size in bytes [default %d]\n", hardbs ); | |
60 | std::printf( " -B, --binary-prefixes show binary multipliers in numbers [SI]\n" | |
61 | " -c, --create-logfile[=<tt>] create logfile from list of blocks [+-]\n" | |
62 | " -C, --complete-logfile[=<t>] complete logfile adding blocks of type t [?]\n" | |
63 | " -d, --delete-if-done delete the logfile if rescue is finished\n" | |
64 | " -D, --done-status return 0 if rescue is finished\n" | |
65 | " -f, --force overwrite existing output files\n" | |
66 | " -i, --input-position=<bytes> starting position of rescue domain [0]\n" | |
67 | " -l, --list-blocks=<types> print block numbers of given types (?*/-+)\n" | |
68 | " -L, --loose-domain accept an incomplete domain logfile\n" | |
69 | " -m, --domain-logfile=<file> restrict domain to finished blocks in file\n" | |
70 | " -n, --invert-logfile invert block types (finished <--> others)\n" | |
71 | " -o, --output-position=<bytes> starting position in output file [ipos]\n" | |
72 | " -p, --compare-logfile=<file> compare block types in domain of both files\n" | |
73 | " -P, --compare-as-domain=<file> like -p but compare finished blocks only\n" | |
74 | " -q, --quiet suppress all messages\n" | |
75 | " -s, --size=<bytes> maximum size of rescue domain to be processed\n" | |
76 | " -t, --show-status show a summary of logfile contents\n" | |
77 | " -v, --verbose be verbose (a 2nd -v gives more)\n" | |
78 | " -x, --xor-logfile=<file> XOR the finished blocks in file with logfile\n" | |
79 | " -y, --and-logfile=<file> AND the finished blocks in file with logfile\n" | |
80 | " -z, --or-logfile=<file> OR the finished blocks in file with logfile\n" | |
81 | "Numbers may be in decimal, hexadecimal or octal, and may be followed by a\n" | |
82 | "multiplier: s = sectors, k = 1000, Ki = 1024, M = 10^6, Mi = 2^20, etc...\n" | |
77 | 83 | "\nExit status: 0 for a normal exit, 1 for environmental problems (file\n" |
78 | 84 | "not found, invalid flags, I/O errors, etc), 2 to indicate a corrupt or\n" |
79 | 85 | "invalid input file, 3 for an internal consistency error (eg, bug) which\n" |
84 | 90 | } |
85 | 91 | |
86 | 92 | |
87 | void set_types( const std::string & arg, | |
88 | std::string & types1, std::string & types2 ) | |
93 | void parse_types( const std::string & arg, | |
94 | std::string & types1, std::string & types2 ) | |
89 | 95 | { |
90 | 96 | std::string * p = &types1; |
91 | 97 | bool error = false, comma_found = false; |
103 | 109 | if( !Sblock::isstatus( ch ) ) { error = true; break; } |
104 | 110 | *p += ch; |
105 | 111 | } |
106 | if( types1.size() == 0 || types2.size() == 0 ) error = true; | |
112 | if( types1.empty() || types2.empty() ) error = true; | |
107 | 113 | if( error ) |
108 | 114 | { |
109 | 115 | show_error( "Invalid type for 'change-types' option.", 0, true ); |
114 | 120 | } |
115 | 121 | |
116 | 122 | |
117 | void set_types( const std::string & arg, | |
118 | Sblock::Status & type1, Sblock::Status & type2 ) | |
119 | { | |
120 | if( arg.size() == 0 ) return; | |
123 | void parse_2types( const std::string & arg, | |
124 | Sblock::Status & type1, Sblock::Status & type2 ) | |
125 | { | |
126 | if( arg.empty() ) return; | |
121 | 127 | if( arg.size() != 2 || arg[0] == arg[1] || |
122 | 128 | !Sblock::isstatus( arg[0] ) || !Sblock::isstatus( arg[1] ) ) |
123 | 129 | { |
129 | 135 | } |
130 | 136 | |
131 | 137 | |
132 | void verify_logname_and_domain( const Logbook & logbook ) | |
133 | { | |
134 | if( !logbook.logfile_exists() ) | |
135 | { | |
136 | char buf[80]; | |
137 | snprintf( buf, sizeof buf, "Logfile '%s' does not exist.", | |
138 | logbook.filename() ); | |
139 | show_error( buf ); | |
138 | void parse_type( const std::string & arg, Sblock::Status & complete_type ) | |
139 | { | |
140 | if( arg.empty() ) return; | |
141 | if( arg.size() != 1 || !Sblock::isstatus( arg[0] ) ) | |
142 | { | |
143 | show_error( "Invalid type for 'complete-logfile' option.", 0, true ); | |
140 | 144 | std::exit( 1 ); |
141 | 145 | } |
142 | if( logbook.domain().size() == 0 ) | |
143 | { show_error( "Empty domain." ); std::exit( 0 ); } | |
146 | complete_type = Sblock::Status( arg[0] ); | |
144 | 147 | } |
145 | 148 | |
146 | 149 | |
147 | 150 | int do_logic_ops( Domain & domain, const char * const logname, |
148 | 151 | const char * const second_logname, const Mode program_mode ) |
149 | 152 | { |
150 | Domain domain2( domain ); | |
151 | Logbook logbook( 0, 0, domain, logname, 1, 1, true ); | |
152 | verify_logname_and_domain( logbook ); | |
153 | const Logbook logbook2( 0, 0, domain2, second_logname, 1, 1, true ); | |
154 | verify_logname_and_domain( logbook2 ); | |
155 | ||
156 | for( int i = 0; i < logbook.sblocks(); ++i ) | |
157 | { | |
158 | const Sblock & sb = logbook.sblock( i ); | |
159 | if( !logbook.domain().includes( sb ) ) | |
160 | { if( logbook.domain() < sb ) break; else continue; } | |
153 | Logfile logfile( logname ); | |
154 | if( !logfile.read_logfile() ) return not_readable( logname ); | |
155 | logfile.compact_sblock_vector(); | |
156 | ||
157 | Logfile logfile2( second_logname ); | |
158 | if( !logfile2.read_logfile() ) return not_readable( second_logname ); | |
159 | logfile2.compact_sblock_vector(); | |
160 | ||
161 | domain.crop( logfile.extent() ); | |
162 | domain.crop( logfile2.extent() ); | |
163 | if( domain.empty() ) return empty_domain(); | |
164 | logfile.split_by_domain_borders( domain ); | |
165 | logfile2.split_by_domain_borders( domain ); | |
166 | logfile.split_by_logfile_borders( logfile2 ); | |
167 | logfile2.split_by_logfile_borders( logfile ); | |
168 | ||
169 | for( int i = 0, j = 0; ; ++i, ++j ) | |
170 | { | |
171 | while( i < logfile.sblocks() && !domain.includes( logfile.sblock( i ) ) ) | |
172 | ++i; | |
173 | while( j < logfile2.sblocks() && !domain.includes( logfile2.sblock( j ) ) ) | |
174 | ++j; | |
175 | if( i >= logfile.sblocks() || j >= logfile2.sblocks() ) break; | |
176 | const Sblock & sb1 = logfile.sblock( i ); | |
177 | const Sblock & sb2 = logfile2.sblock( j ); | |
178 | if( sb1.pos() != sb2.pos() || sb1.size() != sb2.size() ) | |
179 | internal_error( "blocks got out of sync." ); | |
180 | const bool f1 = ( sb1.status() == Sblock::finished ); | |
181 | const bool f2 = ( sb2.status() == Sblock::finished ); | |
161 | 182 | switch( program_mode ) |
162 | 183 | { |
163 | 184 | case m_and: |
164 | { | |
165 | if( sb.status() != Sblock::finished ) continue; | |
166 | Block b( sb ); | |
167 | logbook2.find_chunk( b, Sblock::finished ); | |
168 | if( b.size() <= 0 || b.pos() >= sb.end() ) | |
169 | logbook.change_sblock_status( i, Sblock::bad_sector ); | |
170 | else if( b == sb ) continue; | |
171 | else if( b.pos() == sb.pos() ) logbook.split_sblock_by( b.end(), i ); | |
172 | else | |
173 | { logbook.change_chunk_status( Block( sb.pos(), b.pos() - sb.pos() ), | |
174 | Sblock::bad_sector ); --i; } | |
175 | } break; | |
185 | if( f1 && !f2 ) logfile.change_sblock_status( i, Sblock::bad_sector ); | |
186 | break; | |
176 | 187 | case m_or: |
177 | { | |
178 | if( sb.status() == Sblock::finished ) continue; | |
179 | Block b( sb ); | |
180 | logbook2.find_chunk( b, Sblock::finished ); | |
181 | if( b.size() <= 0 || b.pos() >= sb.end() ) continue; | |
182 | else if( b == sb ) | |
183 | logbook.change_sblock_status( i, Sblock::finished ); | |
184 | else if( b.pos() == sb.pos() ) | |
185 | { logbook.change_chunk_status( b, Sblock::finished ); --i; } | |
186 | else logbook.split_sblock_by( b.end(), i ); | |
187 | } break; | |
188 | if( !f1 && f2 ) logfile.change_sblock_status( i, Sblock::finished ); | |
189 | break; | |
188 | 190 | case m_xor: |
189 | { | |
190 | const Sblock::Status st = ( ( sb.status() == Sblock::finished ) ? | |
191 | Sblock::bad_sector : Sblock::finished ); | |
192 | Block b( sb ); | |
193 | logbook2.find_chunk( b, Sblock::finished ); | |
194 | if( b.size() <= 0 || b.pos() >= sb.end() ) continue; | |
195 | else if( b == sb ) logbook.change_sblock_status( i, st ); | |
196 | else if( b.pos() == sb.pos() ) | |
197 | { logbook.change_chunk_status( b, st ); --i; } | |
198 | else logbook.split_sblock_by( b.end(), i ); | |
199 | } break; | |
200 | default: internal_error( "invalid program_mode" ); | |
201 | } | |
202 | } | |
203 | logbook.compact_sblock_vector(); | |
204 | logbook.write_logfile( stdout ); | |
191 | if( f1 != ( ( f1 || f2 ) && !( f1 && f2 ) ) ) | |
192 | logfile.change_sblock_status( i, f1 ? Sblock::bad_sector : Sblock::finished ); | |
193 | break; | |
194 | default: internal_error( "invalid program_mode." ); | |
195 | } | |
196 | } | |
197 | logfile.compact_sblock_vector(); | |
198 | logfile.write_logfile( stdout ); | |
205 | 199 | if( std::fclose( stdout ) != 0 ) |
206 | 200 | { show_error( "Can't close stdout", errno ); return 1; } |
207 | 201 | return 0; |
211 | 205 | int change_types( Domain & domain, const char * const logname, |
212 | 206 | const std::string & types1, const std::string & types2 ) |
213 | 207 | { |
214 | Logbook logbook( 0, 0, domain, logname, 1, 1, true ); | |
215 | verify_logname_and_domain( logbook ); | |
216 | ||
217 | for( int i = 0; i < logbook.sblocks(); ++i ) | |
218 | { | |
219 | const Sblock & sb = logbook.sblock( i ); | |
220 | if( !logbook.domain().includes( sb ) ) | |
221 | { if( logbook.domain() < sb ) break; else continue; } | |
208 | Logfile logfile( logname ); | |
209 | if( !logfile.read_logfile() ) return not_readable( logname ); | |
210 | domain.crop( logfile.extent() ); | |
211 | if( domain.empty() ) return empty_domain(); | |
212 | logfile.split_by_domain_borders( domain ); | |
213 | ||
214 | for( int i = 0; i < logfile.sblocks(); ++i ) | |
215 | { | |
216 | const Sblock & sb = logfile.sblock( i ); | |
217 | if( !domain.includes( sb ) ) | |
218 | { if( domain < sb ) break; else continue; } | |
222 | 219 | const unsigned j = types1.find( sb.status() ); |
223 | 220 | if( j < types1.size() ) |
224 | logbook.change_sblock_status( i, Sblock::Status( types2[j] ) ); | |
225 | } | |
226 | logbook.compact_sblock_vector(); | |
227 | logbook.write_logfile( stdout ); | |
221 | logfile.change_sblock_status( i, Sblock::Status( types2[j] ) ); | |
222 | } | |
223 | logfile.compact_sblock_vector(); | |
224 | logfile.write_logfile( stdout ); | |
228 | 225 | if( std::fclose( stdout ) != 0 ) |
229 | 226 | { show_error( "Can't close stdout", errno ); return 1; } |
230 | 227 | return 0; |
231 | 228 | } |
232 | 229 | |
233 | 230 | |
231 | int set_for_compare( Domain & domain, Logfile & logfile, | |
232 | const bool as_domain, const bool loose ) | |
233 | { | |
234 | if( !logfile.read_logfile( ( as_domain && loose ) ? '?' : 0 ) ) | |
235 | return not_readable( logfile.filename() ); | |
236 | logfile.compact_sblock_vector(); | |
237 | domain.crop( logfile.extent() ); | |
238 | if( domain.empty() ) return empty_domain(); | |
239 | logfile.split_by_domain_borders( domain ); | |
240 | return -1; | |
241 | } | |
242 | ||
234 | 243 | int compare_logfiles( Domain & domain, const char * const logname, |
235 | const char * const second_logname ) | |
244 | const char * const second_logname, | |
245 | const bool as_domain, const bool loose ) | |
236 | 246 | { |
237 | 247 | Domain domain2( domain ); |
238 | const Logbook logbook( 0, 0, domain, logname, 1, 1, true ); | |
239 | verify_logname_and_domain( logbook ); | |
240 | const Logbook logbook2( 0, 0, domain2, second_logname, 1, 1, true ); | |
241 | verify_logname_and_domain( logbook2 ); | |
242 | ||
243 | int retval = 0; | |
244 | if( logbook.domain() != logbook2.domain() ) retval = 1; | |
245 | else for( int i = 0; i < logbook.sblocks(); ++i ) | |
246 | { | |
247 | const Sblock & sb = logbook.sblock( i ); | |
248 | if( !logbook.domain().includes( sb ) ) | |
249 | { if( logbook.domain() < sb ) break; else continue; } | |
250 | const int j = logbook2.find_index( sb.pos() ); | |
251 | if( j < 0 || logbook2.sblock( j ) != sb ) { retval = 1; break; } | |
248 | Logfile logfile( logname ); | |
249 | int retval = set_for_compare( domain, logfile, as_domain, loose ); | |
250 | if( retval >= 0 ) return retval; | |
251 | ||
252 | Logfile logfile2( second_logname ); | |
253 | retval = set_for_compare( domain2, logfile2, as_domain, loose ); | |
254 | if( retval >= 0 ) return retval; | |
255 | ||
256 | retval = 0; | |
257 | if( !as_domain && domain != domain2 ) retval = 1; | |
258 | else | |
259 | { | |
260 | int i = 0, j = 0; | |
261 | while( true ) | |
262 | { | |
263 | while( i < logfile.sblocks() && | |
264 | ( !domain.includes( logfile.sblock( i ) ) || | |
265 | ( as_domain && logfile.sblock( i ).status() != Sblock::finished ) ) ) | |
266 | ++i; | |
267 | while( j < logfile2.sblocks() && | |
268 | ( !domain2.includes( logfile2.sblock( j ) ) || | |
269 | ( as_domain && logfile2.sblock( j ).status() != Sblock::finished ) ) ) | |
270 | ++j; | |
271 | if( ( i < logfile.sblocks() ) != ( j < logfile2.sblocks() ) ) | |
272 | { retval = 1; break; } // one file has more blocks | |
273 | if( i >= logfile.sblocks() ) break; // successful compare | |
274 | if( logfile.sblock( i++ ) != logfile2.sblock( j++ ) ) | |
275 | { retval = 1; break; } | |
276 | } | |
252 | 277 | } |
253 | 278 | if( retval ) |
254 | 279 | { |
255 | 280 | char buf[80]; |
256 | 281 | snprintf( buf, sizeof buf, "Logfiles '%s' and '%s' differ.", |
257 | logbook.filename(), logbook2.filename() ); | |
282 | logfile.filename(), logfile2.filename() ); | |
258 | 283 | show_error( buf ); |
259 | 284 | } |
260 | 285 | return retval; |
286 | } | |
287 | ||
288 | ||
289 | int complete_logfile( const char * const logname, | |
290 | const Sblock::Status complete_type ) | |
291 | { | |
292 | Logfile logfile( logname ); | |
293 | if( !logfile.read_logfile( complete_type ) ) return not_readable( logname ); | |
294 | logfile.compact_sblock_vector(); | |
295 | logfile.write_logfile( stdout ); | |
296 | if( std::fclose( stdout ) != 0 ) | |
297 | { show_error( "Can't close stdout", errno ); return 1; } | |
298 | return 0; | |
261 | 299 | } |
262 | 300 | |
263 | 301 | |
266 | 304 | const Sblock::Status type2, const bool force ) |
267 | 305 | { |
268 | 306 | char buf[80]; |
269 | Logbook logbook( 0, 0, domain, logname, 1, hardbs, false, force ); | |
270 | if( logbook.logfile_exists() ) | |
307 | Logfile logfile( logname ); | |
308 | if( !force && logfile.read_logfile() ) | |
271 | 309 | { |
272 | 310 | snprintf( buf, sizeof buf, |
273 | 311 | "Logfile '%s' exists. Use '--force' to overwrite it.", logname ); |
274 | 312 | show_error( buf ); |
275 | 313 | return 1; |
276 | 314 | } |
277 | if( logbook.domain().size() == 0 ) | |
278 | { show_error( "Empty domain." ); return 0; } | |
279 | ||
280 | for( int i = 0; i < logbook.sblocks(); ++i ) // mark all logfile as type2 | |
281 | logbook.change_sblock_status( i, type2 ); | |
315 | if( domain.empty() ) return empty_domain(); | |
316 | logfile.make_blank(); | |
317 | logfile.split_by_domain_borders( domain ); | |
318 | ||
319 | for( int i = 0; i < logfile.sblocks(); ++i ) // mark all logfile as type2 | |
320 | logfile.change_sblock_status( i, type2 ); | |
282 | 321 | |
283 | 322 | // mark every block read from stdin and in domain as type1 |
284 | 323 | for( int linenum = 1; ; ++linenum ) |
286 | 325 | long long block; |
287 | 326 | const int n = std::scanf( "%lli\n", &block ); |
288 | 327 | if( n < 0 ) break; // EOF |
289 | if( n != 1 || block > LLONG_MAX / hardbs ) | |
290 | { | |
291 | char buf[80]; | |
328 | if( n != 1 || block < 0 || block > LLONG_MAX / hardbs ) | |
329 | { | |
292 | 330 | snprintf( buf, sizeof buf, |
293 | 331 | "error reading block number from stdin, line %d", linenum ); |
294 | 332 | show_error( buf ); |
295 | 333 | return 2; |
296 | 334 | } |
297 | 335 | const Block b( block * hardbs, hardbs ); |
298 | if( logbook.domain().includes( b ) ) | |
299 | logbook.change_chunk_status( b, type1 ); | |
300 | } | |
301 | logbook.truncate_vector( logbook.domain().end(), true ); | |
302 | if( !logbook.update_logfile( -1, true, false ) ) return 1; | |
336 | if( domain.includes( b ) ) | |
337 | logfile.change_chunk_status( b, type1, domain ); | |
338 | } | |
339 | logfile.truncate_vector( domain.end(), true ); | |
340 | if( !logfile.write_logfile() ) return 1; | |
303 | 341 | return 0; |
304 | 342 | } |
305 | 343 | |
307 | 345 | int test_if_done( Domain & domain, const char * const logname, const bool del ) |
308 | 346 | { |
309 | 347 | char buf[80]; |
310 | const Logbook logbook( 0, 0, domain, logname, 1, 1, true ); | |
311 | verify_logname_and_domain( logbook ); | |
312 | ||
313 | for( int i = 0; i < logbook.sblocks(); ++i ) | |
314 | { | |
315 | const Sblock & sb = logbook.sblock( i ); | |
316 | if( !logbook.domain().includes( sb ) ) | |
317 | { if( logbook.domain() < sb ) break; else continue; } | |
348 | Logfile logfile( logname ); | |
349 | if( !logfile.read_logfile() ) return not_readable( logname ); | |
350 | domain.crop( logfile.extent() ); | |
351 | if( domain.empty() ) return empty_domain(); | |
352 | logfile.split_by_domain_borders( domain ); | |
353 | ||
354 | for( int i = 0; i < logfile.sblocks(); ++i ) | |
355 | { | |
356 | const Sblock & sb = logfile.sblock( i ); | |
357 | if( !domain.includes( sb ) ) | |
358 | { if( domain < sb ) break; else continue; } | |
318 | 359 | if( sb.status() != Sblock::finished ) |
319 | 360 | { |
320 | 361 | if( verbosity >= 1 ) |
346 | 387 | const std::string & blocktypes ) |
347 | 388 | { |
348 | 389 | long long last_block = -1; |
349 | const Logbook logbook( offset, 0, domain, logname, 1, hardbs, true ); | |
350 | verify_logname_and_domain( logbook ); | |
351 | ||
352 | for( int i = 0; i < logbook.sblocks(); ++i ) | |
353 | { | |
354 | const Sblock & sb = logbook.sblock( i ); | |
355 | if( !logbook.domain().includes( sb ) ) | |
356 | { if( logbook.domain() < sb ) break; else continue; } | |
390 | Logfile logfile( logname ); | |
391 | if( !logfile.read_logfile() ) return not_readable( logname ); | |
392 | domain.crop( logfile.extent() ); | |
393 | if( domain.empty() ) return empty_domain(); | |
394 | logfile.split_by_domain_borders( domain ); | |
395 | ||
396 | for( int i = 0; i < logfile.sblocks(); ++i ) | |
397 | { | |
398 | const Sblock & sb = logfile.sblock( i ); | |
399 | if( !domain.includes( sb ) ) | |
400 | { if( domain < sb ) break; else continue; } | |
357 | 401 | if( blocktypes.find( sb.status() ) >= blocktypes.size() ) continue; |
358 | for( long long block = ( sb.pos() + logbook.offset() ) / hardbs; | |
359 | block * hardbs < sb.end() + logbook.offset(); ++block ) | |
402 | for( long long block = ( sb.pos() + offset ) / hardbs; | |
403 | block * hardbs < sb.end() + offset; ++block ) | |
360 | 404 | { |
361 | 405 | if( block > last_block ) |
362 | 406 | { |
363 | 407 | last_block = block; |
364 | 408 | std::printf( "%lld\n", block ); |
365 | 409 | } |
366 | else if( block < last_block ) internal_error( "block out of order" ); | |
410 | else if( block < last_block ) internal_error( "block out of order." ); | |
367 | 411 | } |
368 | 412 | } |
369 | 413 | return 0; |
422 | 466 | int errors = 0; |
423 | 467 | Sblock::Status old_status = Sblock::non_tried; |
424 | 468 | bool first_block = true, good = true; |
425 | const Logbook logbook( 0, 0, domain, logname, 1, 1, true ); | |
426 | verify_logname_and_domain( logbook ); | |
427 | ||
428 | for( int i = 0; i < logbook.sblocks(); ++i ) | |
429 | { | |
430 | const Sblock & sb = logbook.sblock( i ); | |
431 | if( !logbook.domain().includes( sb ) ) | |
432 | { | |
433 | if( logbook.domain() < sb ) break; | |
469 | Logfile logfile( logname ); | |
470 | if( !logfile.read_logfile() ) return not_readable( logname ); | |
471 | const Block extent = logfile.extent(); | |
472 | domain.crop( extent ); | |
473 | if( domain.empty() ) return empty_domain(); | |
474 | const int true_sblocks = logfile.sblocks(); | |
475 | logfile.split_by_domain_borders( domain ); | |
476 | ||
477 | for( int i = 0; i < logfile.sblocks(); ++i ) | |
478 | { | |
479 | const Sblock & sb = logfile.sblock( i ); | |
480 | if( !domain.includes( sb ) ) | |
481 | { | |
482 | if( domain < sb ) break; | |
434 | 483 | else { first_block = true; good = true; continue; } |
435 | 484 | } |
436 | 485 | const bool sc = ( first_block || sb.status() != old_status ); |
454 | 503 | old_status = sb.status(); |
455 | 504 | } |
456 | 505 | |
457 | const long long domain_size = logbook.domain().in_size(); | |
506 | const long long domain_size = domain.in_size(); | |
458 | 507 | const long long errsize = size_non_trimmed + size_non_split + size_bad_sector; |
459 | std::printf( "\ncurrent pos: %10sB, current status: %s\n", | |
460 | format_num( logbook.current_pos() ), | |
461 | logbook.status_name( logbook.current_status() ) ); | |
462 | std::printf( "domain size: %10sB, in %4d area(s)\n", | |
463 | format_num( domain_size ), | |
464 | logbook.domain().blocks() ); | |
465 | std::printf( " rescued: %10sB, in %4d area(s) (%s)\n", | |
508 | std::printf( "\n current pos: %10sB, current status: %s\n", | |
509 | format_num( logfile.current_pos() ), | |
510 | logfile.status_name( logfile.current_status() ) ); | |
511 | std::printf( "logfile extent: %10sB, in %5d area(s)\n", | |
512 | format_num( extent.size() ), true_sblocks ); | |
513 | if( domain.pos() > 0 || domain.end() < extent.end() ) | |
514 | { | |
515 | std::printf( " domain begin: %10sB, domain end: %10sB\n", | |
516 | format_num( domain.pos() ), format_num( domain.end() ) ); | |
517 | std::printf( " domain size: %10sB, in %5d area(s)\n", | |
518 | format_num( domain_size ), domain.blocks() ); | |
519 | } | |
520 | std::printf( " rescued: %10sB, in %5d area(s) (%s)\n", | |
466 | 521 | format_num( size_finished ), areas_finished, |
467 | 522 | format_percentage( size_finished, domain_size ) ); |
468 | std::printf( " non-tried: %10sB, in %4d area(s) (%s)\n", | |
523 | std::printf( " non-tried: %10sB, in %5d area(s) (%s)\n", | |
469 | 524 | format_num( size_non_tried ), areas_non_tried, |
470 | 525 | format_percentage( size_non_tried, domain_size ) ); |
471 | std::printf( "\n errsize: %10sB, errors: %7u (%s)\n", | |
526 | std::printf( "\n errsize: %10sB, errors: %8u (%s)\n", | |
472 | 527 | format_num( errsize ), errors, |
473 | 528 | format_percentage( errsize, domain_size ) ); |
474 | std::printf( "non-trimmed: %10sB, in %4d area(s) (%s)\n", | |
529 | std::printf( " non-trimmed: %10sB, in %5d area(s) (%s)\n", | |
475 | 530 | format_num( size_non_trimmed ), areas_non_trimmed, |
476 | 531 | format_percentage( size_non_trimmed, domain_size ) ); |
477 | std::printf( " non-split: %10sB, in %4d area(s) (%s)\n", | |
532 | std::printf( " non-split: %10sB, in %5d area(s) (%s)\n", | |
478 | 533 | format_num( size_non_split ), areas_non_split, |
479 | 534 | format_percentage( size_non_split, domain_size ) ); |
480 | std::printf( " bad-sector: %10sB, in %4d area(s) (%s)\n", | |
535 | std::printf( " bad-sector: %10sB, in %5d area(s) (%s)\n", | |
481 | 536 | format_num( size_bad_sector ), areas_bad_sector, |
482 | 537 | format_percentage( size_bad_sector, domain_size ) ); |
483 | 538 | return 0; |
499 | 554 | const int default_hardbs = 512; |
500 | 555 | int hardbs = default_hardbs; |
501 | 556 | Mode program_mode = m_none; |
557 | bool as_domain = false; | |
502 | 558 | bool force = false; |
559 | bool loose = false; | |
503 | 560 | std::string types1, types2; |
504 | 561 | Sblock::Status type1 = Sblock::finished, type2 = Sblock::bad_sector; |
562 | Sblock::Status complete_type = Sblock::non_tried; | |
505 | 563 | invocation_name = argv[0]; |
506 | 564 | command_line = argv[0]; |
507 | 565 | for( int i = 1; i < argc; ++i ) |
508 | 566 | { command_line += ' '; command_line += argv[i]; } |
567 | initial_time_ = std::time( 0 ); | |
509 | 568 | |
510 | 569 | const Arg_parser::Option options[] = |
511 | 570 | { |
512 | { 'a', "change-types", Arg_parser::yes }, | |
513 | { 'b', "block-size", Arg_parser::yes }, | |
514 | { 'b', "sector-size", Arg_parser::yes }, | |
515 | { 'c', "create-logfile", Arg_parser::maybe }, | |
516 | { 'd', "delete-if-done", Arg_parser::no }, | |
517 | { 'D', "done-status", Arg_parser::no }, | |
518 | { 'f', "force", Arg_parser::no }, | |
519 | { 'h', "help", Arg_parser::no }, | |
520 | { 'i', "input-position", Arg_parser::yes }, | |
521 | { 'l', "list-blocks", Arg_parser::yes }, | |
522 | { 'm', "domain-logfile", Arg_parser::yes }, | |
523 | { 'n', "invert-logfile", Arg_parser::no }, | |
524 | { 'o', "output-position", Arg_parser::yes }, | |
525 | { 'p', "compare-logfile", Arg_parser::yes }, | |
526 | { 'q', "quiet", Arg_parser::no }, | |
527 | { 's', "size", Arg_parser::yes }, | |
528 | { 's', "max-size", Arg_parser::yes }, | |
529 | { 't', "show-status", Arg_parser::no }, | |
530 | { 'v', "verbose", Arg_parser::no }, | |
531 | { 'V', "version", Arg_parser::no }, | |
532 | { 'x', "xor-logfile", Arg_parser::yes }, | |
533 | { 'y', "and-logfile", Arg_parser::yes }, | |
534 | { 'z', "or-logfile", Arg_parser::yes }, | |
535 | { 0 , 0, Arg_parser::no } }; | |
571 | { 'a', "change-types", Arg_parser::yes }, | |
572 | { 'b', "block-size", Arg_parser::yes }, | |
573 | { 'b', "sector-size", Arg_parser::yes }, | |
574 | { 'B', "binary-prefixes", Arg_parser::no }, | |
575 | { 'c', "create-logfile", Arg_parser::maybe }, | |
576 | { 'C', "complete-logfile", Arg_parser::maybe }, | |
577 | { 'd', "delete-if-done", Arg_parser::no }, | |
578 | { 'D', "done-status", Arg_parser::no }, | |
579 | { 'f', "force", Arg_parser::no }, | |
580 | { 'h', "help", Arg_parser::no }, | |
581 | { 'i', "input-position", Arg_parser::yes }, | |
582 | { 'l', "list-blocks", Arg_parser::yes }, | |
583 | { 'L', "loose-domain", Arg_parser::no }, | |
584 | { 'm', "domain-logfile", Arg_parser::yes }, | |
585 | { 'n', "invert-logfile", Arg_parser::no }, | |
586 | { 'o', "output-position", Arg_parser::yes }, | |
587 | { 'p', "compare-logfile", Arg_parser::yes }, | |
588 | { 'P', "compare-as-domain", Arg_parser::yes }, | |
589 | { 'q', "quiet", Arg_parser::no }, | |
590 | { 's', "size", Arg_parser::yes }, | |
591 | { 's', "max-size", Arg_parser::yes }, | |
592 | { 't', "show-status", Arg_parser::no }, | |
593 | { 'v', "verbose", Arg_parser::no }, | |
594 | { 'V', "version", Arg_parser::no }, | |
595 | { 'x', "xor-logfile", Arg_parser::yes }, | |
596 | { 'y', "and-logfile", Arg_parser::yes }, | |
597 | { 'z', "or-logfile", Arg_parser::yes }, | |
598 | { 0 , 0, Arg_parser::no } }; | |
536 | 599 | |
537 | 600 | const Arg_parser parser( argc, argv, options ); |
538 | 601 | if( parser.error().size() ) // bad option |
547 | 610 | switch( code ) |
548 | 611 | { |
549 | 612 | case 'a': set_mode( program_mode, m_change ); |
550 | set_types( arg, types1, types2 ); break; | |
613 | parse_types( arg, types1, types2 ); break; | |
551 | 614 | case 'b': hardbs = getnum( arg, 0, 1, INT_MAX ); break; |
615 | case 'B': format_num( 0, 0, -1 ); break; // set binary prefixes | |
552 | 616 | case 'c': set_mode( program_mode, m_create ); |
553 | set_types( arg, type1, type2 ); break; | |
617 | parse_2types( arg, type1, type2 ); break; | |
618 | case 'C': set_mode( program_mode, m_complete ); | |
619 | parse_type( arg, complete_type ); break; | |
554 | 620 | case 'd': set_mode( program_mode, m_delete ); break; |
555 | 621 | case 'D': set_mode( program_mode, m_done_st ); break; |
556 | 622 | case 'f': force = true; break; |
558 | 624 | case 'i': ipos = getnum( arg, hardbs, 0 ); break; |
559 | 625 | case 'l': set_mode( program_mode, m_list ); types1 = arg; |
560 | 626 | check_types( types1, "list-blocks" ); break; |
561 | case 'm': set_name( &domain_logfile_name, arg ); break; | |
627 | case 'L': loose = true; break; | |
628 | case 'm': set_name( &domain_logfile_name, arg, code ); break; | |
562 | 629 | case 'n': set_mode( program_mode, m_invert ); break; |
563 | 630 | case 'o': opos = getnum( arg, hardbs, 0 ); break; |
564 | case 'p': set_mode( program_mode, m_compare ); | |
565 | second_logname = arg; break; | |
631 | case 'p': | |
632 | case 'P': set_mode( program_mode, m_compare ); | |
633 | second_logname = arg; as_domain = ( code == 'P' ); break; | |
566 | 634 | case 'q': verbosity = -1; break; |
567 | 635 | case 's': max_size = getnum( arg, hardbs, -1 ); break; |
568 | 636 | case 't': set_mode( program_mode, m_status ); break; |
574 | 642 | second_logname = arg; break; |
575 | 643 | case 'z': set_mode( program_mode, m_or ); |
576 | 644 | second_logname = arg; break; |
577 | default : internal_error( "uncaught option" ); | |
645 | default : internal_error( "uncaught option." ); | |
578 | 646 | } |
579 | 647 | } // end process options |
580 | 648 | |
599 | 667 | |
600 | 668 | // end scan arguments |
601 | 669 | |
602 | Domain domain( ipos, max_size, domain_logfile_name ); | |
670 | Domain domain( ipos, max_size, domain_logfile_name, loose ); | |
603 | 671 | |
604 | 672 | switch( program_mode ) |
605 | 673 | { |
606 | case m_none: internal_error( "invalid operation" ); break; | |
674 | case m_none: internal_error( "invalid operation." ); break; | |
607 | 675 | case m_and: |
608 | 676 | case m_or: |
609 | 677 | case m_xor: |
610 | 678 | return do_logic_ops( domain, logname, second_logname, program_mode ); |
611 | 679 | case m_change: return change_types( domain, logname, types1, types2 ); |
612 | case m_compare: return compare_logfiles( domain, logname, second_logname ); | |
680 | case m_compare: | |
681 | return compare_logfiles( domain, logname, second_logname, as_domain, loose ); | |
682 | case m_complete: return complete_logfile( logname, complete_type ); | |
613 | 683 | case m_create: return create_logfile( domain, logname, hardbs, |
614 | 684 | type1, type2, force ); |
615 | 685 | case m_delete: return test_if_done( domain, logname, true ); |
0 | 0 | .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.37.1. |
1 | .TH DDRESCUE "1" "July 2013" "ddrescue 1.17" "User Commands" | |
1 | .TH DDRESCUE "1" "June 2014" "ddrescue 1.18.1" "User Commands" | |
2 | 2 | .SH NAME |
3 | 3 | ddrescue \- data recovery tool |
4 | 4 | .SH SYNOPSIS |
7 | 7 | .SH DESCRIPTION |
8 | 8 | GNU ddrescue \- Data recovery tool. |
9 | 9 | Copies data from one file or block device to another, |
10 | trying hard to rescue data in case of read errors. | |
10 | trying to rescue the good parts first in case of read errors. | |
11 | 11 | .PP |
12 | 12 | You should use a logfile unless you know what you are doing. |
13 | 13 | If you reboot, check the device names before restarting ddrescue. |
14 | Do not use options '\-F' or '\-g' without reading the manual first. | |
14 | Do not use options '\-F' or '\-G' without reading the manual first. | |
15 | 15 | .SH OPTIONS |
16 | 16 | .TP |
17 | 17 | \fB\-h\fR, \fB\-\-help\fR |
56 | 56 | \fB\-F\fR, \fB\-\-fill\-mode=\fR<types> |
57 | 57 | fill given type blocks with infile data (?*/\-+) |
58 | 58 | .TP |
59 | \fB\-g\fR, \fB\-\-generate\-mode\fR | |
59 | \fB\-G\fR, \fB\-\-generate\-mode\fR | |
60 | 60 | generate approximate logfile from partial copy |
61 | .TP | |
62 | \fB\-H\fR, \fB\-\-test\-mode=\fR<file> | |
63 | set map of good/bad blocks from given logile | |
61 | 64 | .TP |
62 | 65 | \fB\-i\fR, \fB\-\-input\-position=\fR<bytes> |
63 | 66 | starting position in input file [0] |
65 | 68 | \fB\-I\fR, \fB\-\-verify\-input\-size\fR |
66 | 69 | verify input file size with size in logfile |
67 | 70 | .TP |
68 | \fB\-K\fR, \fB\-\-skip\-size=\fR<bytes> | |
71 | \fB\-K\fR, \fB\-\-skip\-size=\fR<min>[,<max>] | |
69 | 72 | initial size to skip on read error [64 KiB] |
70 | 73 | .TP |
71 | 74 | \fB\-l\fR, \fB\-\-logfile\-size=\fR<entries> |
72 | do not grow logfile beyond this size [1000] | |
75 | do not grow logfile beyond this size [10000] | |
76 | .TP | |
77 | \fB\-L\fR, \fB\-\-loose\-domain\fR | |
78 | accept an incomplete domain logfile | |
73 | 79 | .TP |
74 | 80 | \fB\-m\fR, \fB\-\-domain\-logfile=\fR<file> |
75 | 81 | restrict domain to finished blocks in file |
78 | 84 | mark all failed blocks as non\-trimmed |
79 | 85 | .TP |
80 | 86 | \fB\-n\fR, \fB\-\-no\-split\fR |
81 | do not try to split or retry failed blocks | |
87 | skip the splitting phase | |
88 | .TP | |
89 | \fB\-N\fR, \fB\-\-no\-trim\fR | |
90 | skip the trimming phase | |
82 | 91 | .TP |
83 | 92 | \fB\-o\fR, \fB\-\-output\-position=\fR<bytes> |
84 | 93 | starting position in output file [ipos] |
94 | .TP | |
95 | \fB\-O\fR, \fB\-\-reopen\-on\-error\fR | |
96 | reopen input file after every read error | |
85 | 97 | .TP |
86 | 98 | \fB\-p\fR, \fB\-\-preallocate\fR |
87 | 99 | preallocate space on disc for output file |
89 | 101 | \fB\-q\fR, \fB\-\-quiet\fR |
90 | 102 | suppress all messages |
91 | 103 | .TP |
92 | \fB\-r\fR, \fB\-\-retries=\fR<n> | |
93 | exit after given retries (\fB\-1\fR=\fIinfinity\fR) [0] | |
104 | \fB\-r\fR, \fB\-\-retry\-passes=\fR<n> | |
105 | exit after <n> retry passes (\fB\-1\fR=\fIinfinity\fR) [0] | |
94 | 106 | .TP |
95 | 107 | \fB\-R\fR, \fB\-\-reverse\fR |
96 | reverse direction of copy operations | |
108 | reverse direction of some copy operations | |
97 | 109 | .TP |
98 | 110 | \fB\-s\fR, \fB\-\-size=\fR<bytes> |
99 | 111 | maximum size of input data to be copied |
115 | 127 | .TP |
116 | 128 | \fB\-x\fR, \fB\-\-extend\-outfile=\fR<bytes> |
117 | 129 | extend outfile size to be at least this long |
130 | .TP | |
131 | \fB\-1\fR, \fB\-\-log\-rates=\fR<file> | |
132 | log rates and error sizes in file | |
133 | .TP | |
134 | \fB\-2\fR, \fB\-\-log\-reads=\fR<file> | |
135 | log all read operations in file | |
118 | 136 | .PP |
119 | Numbers may be followed by a multiplier: s = sectors, k = kB = 10^3 = 1000, | |
120 | Ki = KiB = 2^10 = 1024, M = 10^6, Mi = 2^20, G = 10^9, Gi = 2^30, etc... | |
137 | Numbers may be in decimal, hexadecimal or octal, and may be followed by a | |
138 | multiplier: s = sectors, k = 1000, Ki = 1024, M = 10^6, Mi = 2^20, etc... | |
121 | 139 | Time intervals have the format 1[.5][smhd] or 1/2[smhd]. |
122 | 140 | .PP |
123 | 141 | Exit status: 0 for a normal exit, 1 for environmental problems (file |
131 | 149 | .br |
132 | 150 | General help using GNU software: http://www.gnu.org/gethelp |
133 | 151 | .SH COPYRIGHT |
134 | Copyright \(co 2013 Antonio Diaz Diaz. | |
152 | Copyright \(co 2014 Antonio Diaz Diaz. | |
135 | 153 | License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> |
136 | 154 | .br |
137 | 155 | This is free software: you are free to change and redistribute it. |
Binary diff not shown
0 | \input texinfo @c -*-texinfo-*- | |
1 | @c %**start of header | |
2 | @setfilename ddrescue.info | |
3 | @documentencoding ISO-8859-15 | |
4 | @settitle GNU ddrescue Manual | |
5 | @finalout | |
6 | @c %**end of header | |
7 | ||
8 | @set UPDATED 7 June 2014 | |
9 | @set VERSION 1.18 | |
10 | ||
11 | @dircategory GNU Packages | |
12 | @direntry | |
13 | * Ddrescue: (ddrescue). Data recovery tool | |
14 | @end direntry | |
15 | ||
16 | ||
17 | @ifnothtml | |
18 | @titlepage | |
19 | @title GNU ddrescue | |
20 | @subtitle Data recovery tool | |
21 | @subtitle for Ddrescue version @value{VERSION}, @value{UPDATED} | |
22 | @author by Antonio Diaz Diaz | |
23 | ||
24 | @page | |
25 | @vskip 0pt plus 1filll | |
26 | @end titlepage | |
27 | ||
28 | @contents | |
29 | @end ifnothtml | |
30 | ||
31 | @node Top | |
32 | @top | |
33 | ||
34 | This manual is for GNU ddrescue (version @value{VERSION}, @value{UPDATED}). | |
35 | ||
36 | @menu | |
37 | * Introduction:: Purpose and features of GNU ddrescue | |
38 | * Basic concepts:: Blocks, clusters, devices, files, sectors, etc | |
39 | * Important advice:: Read this or risk losing your data | |
40 | * Algorithm:: How ddrescue recovers the data | |
41 | * Invoking ddrescue:: Command line interface | |
42 | * Logfile structure:: Detailed format of the logfile | |
43 | * Examples:: A small tutorial with examples | |
44 | * Direct disc access:: Bypassing the kernel cache | |
45 | * Fill mode:: Selectively overwriting the output file | |
46 | * Generate mode:: Generating an approximate logfile | |
47 | * Ddrescuelog:: Tool for ddrescue logfiles | |
48 | * Invoking ddrescuelog:: Command line interface | |
49 | * Problems:: Reporting bugs | |
50 | * Concept index:: Index of concepts | |
51 | @end menu | |
52 | ||
53 | @sp 1 | |
54 | Copyright @copyright{} 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, | |
55 | 2012, 2013, 2014 Antonio Diaz Diaz. | |
56 | ||
57 | This manual is free documentation: you have unlimited permission | |
58 | to copy, distribute and modify it. | |
59 | ||
60 | ||
61 | @node Introduction | |
62 | @chapter Introduction | |
63 | @cindex introduction | |
64 | ||
65 | GNU ddrescue is a data recovery tool. It copies data from one file or | |
66 | block device (hard disc, cdrom, etc) to another, trying to rescue the | |
67 | good parts first in case of read errors. | |
68 | ||
69 | The basic operation of ddrescue is fully automatic. That is, you don't | |
70 | have to wait for an error, stop the program, read the log, restart it | |
71 | from a new position, etc. | |
72 | ||
73 | If you use the logfile feature of ddrescue, the data is rescued very | |
74 | efficiently, (only the needed blocks are read). Also you can interrupt | |
75 | the rescue at any time and resume it later at the same point. | |
76 | ||
77 | Ddrescue does not write zeros to the output when it finds bad sectors in | |
78 | the input, and does not truncate the output file if not asked to. So, | |
79 | every time you run it on the same output file, it tries to fill in the | |
80 | gaps without wiping out the data already rescued. | |
81 | ||
82 | Automatic merging of backups: If you have two or more damaged copies of | |
83 | a file, cdrom, etc, and run ddrescue on all of them, one at a time, with | |
84 | the same output file, you will probably obtain a complete and error-free | |
85 | file. This is so because the probability of having the same area damaged | |
86 | in all copies is low. Using the logfile, only the needed blocks are read | |
87 | from the second and successive copies. | |
88 | ||
89 | Ddrescue recommends lzip for compression of backups because the lzip | |
90 | format is designed for long-term data archiving and provides data | |
91 | recovery capabilities which nicely complement those of ddrescue. | |
92 | (Ddrescue fills unreadable sectors with data from other copies, while | |
93 | lziprecover corrects corrupt sectors with data from other copies). If | |
94 | the cause of file corruption is damaged media, the combination ddrescue | |
95 | + lziprecover is the best option for recovering data from multiple | |
96 | damaged copies. @xref{lziprecover-example}, for an example. | |
97 | ||
98 | Recordable CD and DVD media keep their data only for a finite time | |
99 | (typically for many years). After that time, data loss develops slowly | |
100 | with read errors growing from the outer media region towards the inside. | |
101 | Just make two (or more) copies of every important CD/DVD you burn so | |
102 | that you can later recover them with ddrescue. | |
103 | ||
104 | Because ddrescue needs to read and write at random places, it only works | |
105 | on seekable (random access) input and output files. | |
106 | ||
107 | If your system supports it, ddrescue can use direct disc access to read | |
108 | the input file, bypassing the kernel cache. | |
109 | ||
110 | Ddrescue also features a "fill mode" able to selectively overwrite parts | |
111 | of the output file, which has a number of interesting uses like wiping | |
112 | data, marking bad areas or even, in some cases, "repair" damaged | |
113 | sectors. | |
114 | ||
115 | One of the great strengths of ddrescue is that it is interface-agnostic, | |
116 | and so can be used for any kind of device supported by your kernel (ATA, | |
117 | SATA, SCSI, old MFM drives, floppy discs, or even flash media cards like | |
118 | SD). | |
119 | ||
120 | ||
121 | @node Basic concepts | |
122 | @chapter Basic concepts | |
123 | @cindex basic concepts | |
124 | ||
125 | @table @asis | |
126 | @item Block | |
127 | Any amount of data. A block is described by its starting position and | |
128 | its size. The starting position (or beginning position) is the lowest | |
129 | position in the block. The end of the block is its starting position | |
130 | plus its size. | |
131 | ||
132 | @item Cluster | |
133 | Group of consecutive sectors read or written in one go. | |
134 | ||
135 | @item Device | |
136 | Piece of hardware containing data. Hard disc drives, cdrom drives, USB | |
137 | pendrives, are devices. /dev/hda, /dev/sdb, are device names. | |
138 | ||
139 | @item File | |
140 | Files are named units of data which are stored by the operating system | |
141 | for you to retrieve later by name. Devices and partitions are accessed | |
142 | by means of their associated file names. | |
143 | ||
144 | @item Partition | |
145 | Every part in which a device is divided. A partition normally contains a | |
146 | file system. /dev/hda1, /dev/sdb3, are partition names. | |
147 | ||
148 | @item Recoverable formats | |
149 | As ddrescue uses standard library functions to read data from the device | |
150 | being rescued, only mountable device formats can be rescued with | |
151 | ddrescue. DVDs can be mounted and they can be rescued, "compact disc | |
152 | digital audio" CDs can't, "video CDs"[1] maybe.@* | |
153 | [1] http://en.wikipedia.org/wiki/Video_CD | |
154 | ||
155 | @item Rescue domain | |
156 | Block or set of blocks to be acted upon (rescued, listed, etc). You can | |
157 | define it with the options @samp{--input-position}, @samp{--size} and | |
158 | @samp{--domain-logfile}. The rescue domain defaults to the whole input | |
159 | file or logfile. | |
160 | ||
161 | The amount of data rescued, number of errors, etc, shown by ddrescue may | |
162 | vary or even become zero if you limit the rescue domain. Don't worry, | |
163 | they have not disappeared; they are simply out of the specified rescue | |
164 | domain. | |
165 | ||
166 | @item Sector | |
167 | Hardware block. Smallest accessible amount of data on a device. | |
168 | ||
169 | @end table | |
170 | ||
171 | ||
172 | @node Important advice | |
173 | @chapter Using ddrescue safely | |
174 | @cindex using ddrescue safely | |
175 | ||
176 | Ddrescue is like any other power tool. You need to understand what it | |
177 | does, and you need to understand some things about the machines it does | |
178 | those things to, in order to use it safely. | |
179 | ||
180 | Always use a logfile unless you know you won't need it. Without a | |
181 | logfile, ddrescue can't resume a rescue, only reinitiate it. | |
182 | ||
183 | Never try to rescue a r/w mounted partition. The resulting copy may be | |
184 | useless. | |
185 | ||
186 | Never try to repair a file system on a drive with I/O errors; you will | |
187 | probably lose even more data. | |
188 | ||
189 | If you use a device or a partition as destination, any data stored there | |
190 | will be overwritten. | |
191 | ||
192 | Some systems may change device names on reboot (eg. udev enabled | |
193 | systems). If you reboot, check the device names before restarting | |
194 | ddrescue. | |
195 | ||
196 | If you interrupt the rescue and then reboot, any partially copied | |
197 | partitions should be hidden before allowing them to be touched by any | |
198 | operating system that tries to mount and "fix" the partitions it sees. | |
199 | ||
200 | ||
201 | @node Algorithm | |
202 | @chapter Algorithm | |
203 | @cindex algorithm | |
204 | ||
205 | GNU ddrescue is not a derivative of dd, nor is related to dd in any way | |
206 | except in that both can be used for copying data from one device to | |
207 | another. The key difference is that ddrescue uses a sophisticated | |
208 | algorithm to copy data from failing drives causing them as little | |
209 | additional damage as possible. | |
210 | ||
211 | Ddrescue manages efficiently the status of the rescue in progress and | |
212 | tries to rescue the good parts first, scheduling reads inside bad (or | |
213 | slow) areas for later. This maximizes the amount of data that can be | |
214 | finally recovered from a failing drive. | |
215 | ||
216 | The standard dd utility can be used to save data from a failing drive, | |
217 | but it reads the data sequentially, which may wear out the drive without | |
218 | rescuing anything if the errors are at the beginning of the drive. | |
219 | ||
220 | Other programs read the data sequentially but switch to small size reads | |
221 | when they find errors. This is a bad idea because it means spending more | |
222 | time at error areas, damaging the surface, the heads and the drive | |
223 | mechanics, instead of getting out of them as fast as possible. This | |
224 | behavior reduces the chances of rescuing the remaining good data. | |
225 | ||
226 | The algorithm of ddrescue is as follows (the user may interrupt the | |
227 | process at any point, but be aware that a bad drive can block ddrescue | |
228 | for a long time until the kernel gives up): | |
229 | ||
230 | 1) Optionally read a logfile describing the status of a multi-part or | |
231 | previously interrupted rescue. If no logfile is specified or is empty or | |
232 | does not exist, mark all the rescue domain as non-tried. | |
233 | ||
234 | 2) (First phase; Copying) Read the non-tried parts of the input file, | |
235 | marking the failed blocks as non-trimmed and skipping beyond them. Skip | |
236 | also beyond slow areas. The skipped areas are tried later in two | |
237 | additional passes (before trimming), reversing the direction after each | |
238 | pass until all the rescue domain is tried. The third pass is a sweeping | |
239 | pass, with skipping disabled. (The purpose is to delimit large errors | |
240 | fast, keep the logfile small, and produce good starting points for | |
241 | trimming). Only non-tried areas are read in large blocks. Trimming, | |
242 | splitting and retrying are done sector by sector. Each sector is tried | |
243 | at most two times; the first in this step (usually as part of a large | |
244 | block read, but sometimes as a single sector read), the second in one of | |
245 | the steps below as a single sector read. | |
246 | ||
247 | 3) (Second phase; Trimming) Read forwards one sector at a time from the | |
248 | leading edge of the smallest non-trimmed block, until a bad sector is | |
249 | found. Then read backwards one sector at a time from the trailing edge | |
250 | of the same block, until a bad sector is found. For each non-trimmed | |
251 | block, mark the bad sectors found as bad-sector and mark the rest of | |
252 | that block as non-split without trying to read it. Repeat until there | |
253 | are no more non-trimmed blocks. (Large non-trimmed blocks are produced | |
254 | by concatenation of smaller ones, and its fraction of good data at the | |
255 | edges is therefore smaller). | |
256 | ||
257 | 4) (Third phase; Splitting) Read forwards one sector at a time from the | |
258 | center of the largest non-split block, until a bad sector is found. | |
259 | Then, if the bad sector found is not the first one tried, read backwards | |
260 | one sector at a time from the center of the same block, until a bad | |
261 | sector is found. If the logfile is larger than @samp{--logfile-size}, | |
262 | read sequentially the largest non-split blocks until the number of | |
263 | entries in the logfile drops below @samp{--logfile-size}. Repeat until | |
264 | all remaining non-split blocks have less than 7 sectors. Then read the | |
265 | remaining non-split blocks sequentially. | |
266 | ||
267 | 5) (Fourth phase; Retrying) Optionally try to read again the bad sectors | |
268 | until the specified number of retry passes is reached. Every bad sector | |
269 | is tried only once in each pass. Ddrescue can't know if a bad sector is | |
270 | unrecoverable or if it will be eventually read after some retries. | |
271 | ||
272 | 6) Optionally write a logfile for later use. | |
273 | ||
274 | @sp 1 | |
275 | The total error size (@samp{errsize}) is sum of the sizes of all the | |
276 | non-trimmed, non-split and bad-sector blocks. It increases during the | |
277 | copying phase and may decrease during trimming, splitting and retrying. | |
278 | Note that as ddrescue splits the failed blocks, making them smaller, the | |
279 | total error size may decrease while the number of errors increases. | |
280 | ||
281 | The logfile is periodically saved to disc, as well as when ddrescue | |
282 | finishes or is interrupted. So in case of a crash you can resume the | |
283 | rescue with little recopying. The interval between saves varies from 30 | |
284 | seconds to 5 minutes depending on logfile size (larger logfiles are | |
285 | saved at longer intervals). | |
286 | ||
287 | Also, the same logfile can be used for multiple commands that copy | |
288 | different areas of the input file, and for multiple recovery attempts | |
289 | over different subsets. See this example: | |
290 | ||
291 | @noindent | |
292 | Rescue the most important part of the disc first. | |
293 | @example | |
294 | ddrescue -i0 -s50MiB /dev/hdc hdimage logfile | |
295 | ddrescue -i0 -s1MiB -d -r3 /dev/hdc hdimage logfile | |
296 | @end example | |
297 | ||
298 | @noindent | |
299 | Then rescue some key disc areas. | |
300 | @example | |
301 | ddrescue -i30GiB -s10GiB /dev/hdc hdimage logfile | |
302 | ddrescue -i230GiB -s5GiB /dev/hdc hdimage logfile | |
303 | @end example | |
304 | ||
305 | @noindent | |
306 | Now rescue the rest (does not recopy what is already done). | |
307 | @example | |
308 | ddrescue /dev/hdc hdimage logfile | |
309 | ddrescue -d -r3 /dev/hdc hdimage logfile | |
310 | @end example | |
311 | ||
312 | ||
313 | @node Invoking ddrescue | |
314 | @chapter Invoking ddrescue | |
315 | @cindex invoking ddrescue | |
316 | @cindex options | |
317 | @cindex usage | |
318 | @cindex version | |
319 | ||
320 | The format for running ddrescue is: | |
321 | ||
322 | @example | |
323 | ddrescue [@var{options}] @var{infile} @var{outfile} [@var{logfile}] | |
324 | @end example | |
325 | ||
326 | ddrescue supports the following options: | |
327 | ||
328 | @table @samp | |
329 | @item -h | |
330 | @itemx --help | |
331 | Print an informative help message describing the options and exit. | |
332 | ||
333 | @item -V | |
334 | @itemx --version | |
335 | Print the version number of ddrescue on the standard output and exit. | |
336 | ||
337 | @item -a @var{bytes} | |
338 | @itemx --min-read-rate=@var{bytes} | |
339 | Minimum read rate of good non-tried areas, in bytes per second. If the | |
340 | read rate falls below this value, ddrescue will skip ahead a variable | |
341 | amount depending on rate and error histories. The skipped blocks are | |
342 | tried in additional passes (before trimming) where the minimum read rate | |
343 | is divided by ten before each pass, until there are no more non-tried | |
344 | blocks left. | |
345 | ||
346 | If @var{bytes} is 0 (auto), the minimum read rate is recalculated for | |
347 | each block as @w{(average_rate / 10)}. Values above device capabilities | |
348 | are ignored. | |
349 | ||
350 | @item -A | |
351 | @itemx --try-again | |
352 | Mark all non-split and non-trimmed blocks inside the rescue domain as | |
353 | non-tried before beginning the rescue. Try this if the drive stops | |
354 | responding and ddrescue immediately starts splitting failed blocks when | |
355 | restarted. If @samp{--retrim} is also specified, mark all failed blocks | |
356 | inside the rescue domain as non-tried. | |
357 | ||
358 | @item -b @var{bytes} | |
359 | @itemx --sector-size=@var{bytes} | |
360 | Sector (hardware block) size of input device in bytes (usually 512 for | |
361 | hard discs and 3.5" floppies, 1024 for 5.25" floppies, and 2048 for | |
362 | cdroms). Defaults to 512. | |
363 | ||
364 | @item -B | |
365 | @itemx --binary-prefixes | |
366 | Show units with binary prefixes (powers of 1024).@* | |
367 | SI prefixes (powers of 1000) are used by default. (See table below). | |
368 | ||
369 | @item -c @var{sectors} | |
370 | @itemx --cluster-size=@var{sectors} | |
371 | Number of sectors to copy at a time. Defaults to @w{64 KiB / sector_size}. | |
372 | Try smaller values for slow drives. The number of sectors per track (18 | |
373 | or 9) is a good value for floppies. | |
374 | ||
375 | @item -C | |
376 | @itemx --complete-only | |
377 | Limit rescue domain to the blocks listed in the @var{logfile}. Do not | |
378 | read new data beyond @var{logfile} limits. This is useful when reading | |
379 | from devices of undefined size (like raw devices), when the drive | |
380 | returns an incorrect size, or when reading from a partial copy. It can | |
381 | only be used after a first rescue attempt, possibly limited with the | |
382 | @samp{--size} option, has produced a complete @var{logfile}. | |
383 | ||
384 | @item -d | |
385 | @itemx --direct | |
386 | Use direct disc access to read from @var{infile}, bypassing the kernel | |
387 | cache. (Open the file with the O_DIRECT flag). Use it only on devices or | |
388 | partitions, not on regular files. Sector size must be correctly set for | |
389 | this to work. Not all systems support this. | |
390 | ||
391 | If your system does not support direct disc access, ddrescue will warn | |
392 | you. If the sector size is not correctly set, all reads will result in | |
393 | errors, and no data will be rescued. | |
394 | ||
395 | @item -D | |
396 | @itemx --synchronous | |
397 | Use synchronous writes for @var{outfile}. (Issue a fsync call after | |
398 | every write). May be useful when forcing the drive to remap its bad | |
399 | sectors. | |
400 | ||
401 | @item -e [+]@var{n} | |
402 | @itemx --max-errors=[+]@var{n} | |
403 | Maximum number of error areas allowed before giving up. Defaults to | |
404 | infinity. If @var{n} is preceded by @samp{+} the number refers to new | |
405 | error areas found in this run, not counting those already annotated in | |
406 | the @var{logfile}. | |
407 | ||
408 | @item -E @var{bytes} | |
409 | @itemx --max-error-rate=@var{bytes} | |
410 | Maximum rate of errors allowed before giving up, in bytes per second. | |
411 | Defaults to infinity. The rate being measured is that of actually failed | |
412 | reads, so the rescue may finish because of this rate being exceeded even | |
413 | if the total error size (errsize) does not change because the areas | |
414 | being tried are already marked as errors. | |
415 | ||
416 | @item -f | |
417 | @itemx --force | |
418 | Force overwrite of @var{outfile}. Needed when @var{outfile} is not a | |
419 | regular file, but a device or partition. | |
420 | ||
421 | @item -F @var{types} | |
422 | @itemx --fill-mode=@var{types} | |
423 | Fill the blocks in @var{outfile} specified as any of @var{types} in | |
424 | @var{logfile}, with data read from @var{infile}. @var{types} contains | |
425 | one or more of the status characters defined in the chapter Logfile | |
426 | structure (@pxref{Logfile structure}). See the chapter Fill mode | |
427 | (@pxref{Fill mode}) for a complete description of the fill mode. | |
428 | ||
429 | @item -G | |
430 | @itemx --generate-mode | |
431 | Generate an approximate @var{logfile} from the @var{infile} and | |
432 | @var{outfile} of the original rescue run. Note that you must keep the | |
433 | original offset between @samp{--input-position} and | |
434 | @samp{--output-position} of the original rescue run. See the chapter | |
435 | Generate mode (@pxref{Generate mode}) for a complete description of the | |
436 | generate mode. | |
437 | ||
438 | @item -H @var{file} | |
439 | @itemx --test-mode=@var{file} | |
440 | Builds a map of good/bad blocks using the logfile @var{file} and uses it | |
441 | to simulate read errors in @var{infile}. The blocks marked as finished | |
442 | in @var{file} will be read normally. All other block types will be | |
443 | considered read errors without even trying to read them from | |
444 | @var{infile}. This mode is an aid in improving the algorithm of ddrescue | |
445 | and is also useful to verify that ddrescue produces accurate results in | |
446 | presence of read errors. | |
447 | ||
448 | @item -i @var{bytes} | |
449 | @itemx --input-position=@var{bytes} | |
450 | Starting position of the rescue domain in @var{infile}, in bytes. | |
451 | Defaults to 0. This is not the point from which ddrescue starts copying. | |
452 | (For example, if you pass the @samp{--reverse} option to ddrescue, it | |
453 | starts reading from the end of the rescue domain). In fill mode it | |
454 | refers to a position in the @var{infile} of the original rescue run. See | |
455 | the chapter Fill mode (@pxref{Fill mode}) for details. | |
456 | ||
457 | @item -I | |
458 | @itemx --verify-input-size | |
459 | Compare the size of @var{infile} with the size calculated from the list | |
460 | of blocks contained in the @var{logfile}, and exit with status 1 if they | |
461 | differ. This is not enabled by default because the size of some devices | |
462 | can't be known in advance and because the size derived from the | |
463 | @var{logfile} may be incomplete, for example after doing a partial | |
464 | rescue. | |
465 | ||
466 | @item -K @var{initial}[,@var{max}] | |
467 | @itemx -K ,@var{max} | |
468 | @itemx --skip-size=@var{initial}[,@var{max}] | |
469 | @itemx --skip-size=,@var{max} | |
470 | Set limits to skip size during the copying phase. @var{initial} is the | |
471 | size to skip on the first read error or slow read, in bytes. @var{max} | |
472 | is the maximum size to skip. The values given will be rounded to the | |
473 | next multiple of sector size. The skip size will be doubled for each | |
474 | read error or slow read until it reaches @var{max} or, if @var{max} is | |
475 | omitted, 1% of the size of @var{infile} or 1 GiB (whichever is smaller), | |
476 | and will be reset to @var{initial} when good data is found. Valid values | |
477 | range from 64 KiB to 1 GiB. @var{initial} defaults to 64 KiB. An | |
478 | @var{initial} value of 0 disables skipping entirely. | |
479 | ||
480 | If ddrescue is having difficulties skipping away from a large area with | |
481 | scattered errors, or if the device has large error areas at regular | |
482 | intervals, you can increase the initial skip size with this option. | |
483 | Inversely, if ddrescue is skipping too much, leaving large non-tried | |
484 | areas behind each error (which will be read later in the usually slower | |
485 | backwards direction), you can reduce the maximum skip size, or disable | |
486 | skipping. | |
487 | ||
488 | @samp{--skip-size} is independent from @samp{--cluster-size}. The size | |
489 | to skip is calculated from the end of the block that just failed. | |
490 | ||
491 | @item -l @var{entries} | |
492 | @itemx --logfile-size=@var{entries} | |
493 | During the splitting phase, do not grow logfile beyond this number of | |
494 | entries. Logfile may be larger if it was larger at startup or if it | |
495 | became larger during the copying or trimming phases. Defaults to 10000. | |
496 | (Each entry is about 26-30 bytes in size). | |
497 | ||
498 | @item -L | |
499 | @itemx --loose-domain | |
500 | Accept an incomplete synthetic (user fabricated) domain logfile or | |
501 | test-mode logfile and fill the gaps with non-tried blocks. The blocks in | |
502 | the logfile must be strictly ascending and non-overlapping, but they do | |
503 | not need to be contiguous. This option allows making quick edits to a | |
504 | logfile without all the size calculations involved in making all data | |
505 | blocks contiguous again. | |
506 | ||
507 | @item -m @var{file} | |
508 | @itemx --domain-logfile=@var{file} | |
509 | Restrict the rescue domain to the blocks marked as finished in the | |
510 | logfile @var{file}. This is useful for merging partially recovered | |
511 | images of backups, or if the destination drive fails during the rescue. | |
512 | ||
513 | @item -M | |
514 | @itemx --retrim | |
515 | Mark all failed blocks inside the rescue domain as non-trimmed before | |
516 | beginning the rescue. The effect is similar to @samp{--retry-passes=1}, | |
517 | but the bad sectors are tried in a different order, making perhaps | |
518 | possible to rescue some of them. | |
519 | ||
520 | @item -n | |
521 | @itemx --no-split | |
522 | Skip the splitting phase. Avoids spending a lot of time trying to rescue | |
523 | the most difficult parts of the file. | |
524 | ||
525 | @item -N | |
526 | @itemx --no-trim | |
527 | Skip the trimming phase. Specially useful in the first parts of a | |
528 | multi-part rescue. | |
529 | ||
530 | @item -o @var{bytes} | |
531 | @itemx --output-position=@var{bytes} | |
532 | Starting position of the image of the rescue domain in @var{outfile}, in | |
533 | bytes. Defaults to @samp{--input-position}. The bytes below @var{bytes} | |
534 | aren't touched if they exist and truncation is not requested. Else they | |
535 | are set to 0. | |
536 | ||
537 | @item -O | |
538 | @itemx --reopen-on-error | |
539 | Close @var{infile} and then reopen it after every read error and, if | |
540 | @samp{--min-read-rate} is set, after every slow read encountered both | |
541 | during the copying phase. Use this option if you notice a permanent drop | |
542 | in transfer rate after finding read errors or slow areas. But be warned | |
543 | that most probably the slowing-down is intentionally caused by the | |
544 | kernel in an attempt to increase the probability of reading data from | |
545 | the device. | |
546 | ||
547 | @item -p | |
548 | @itemx --preallocate | |
549 | Preallocate space on disc for @var{outfile}. Only space for regular | |
550 | files can be preallocated. If preallocation succeeds, rescue will not | |
551 | fail due to lack of free space on disc. If ddrescue can't determine the | |
552 | size to preallocate, you may need to specify it with some combination of | |
553 | the @samp{--input-position}, @samp{--output-position}, @samp{--size}, | |
554 | and @samp{--domain-logfile} options. | |
555 | ||
556 | @item -q | |
557 | @itemx --quiet | |
558 | Quiet operation. Suppress all messages. | |
559 | ||
560 | @item -r @var{n} | |
561 | @itemx --retry-passes=@var{n} | |
562 | Exit after given number of retry passes. Defaults to 0. -1 means | |
563 | infinity. Every bad sector is tried only once in each pass. To retry bad | |
564 | sectors detected on a previous run, you must specify a non-zero number | |
565 | of retry passes. | |
566 | ||
567 | @item -R | |
568 | @itemx --reverse | |
569 | Reverse the direction of the first pass of copying, all retrying passes, | |
570 | and the sequential part of splitting, running them backwards from the | |
571 | end of the rescue domain (or from the end of @var{infile} if no domain | |
572 | options are specified). @samp{--reverse} does not modify the size of the | |
573 | blocks copied during each phase, just the order in which they are tried. | |
574 | ||
575 | @item -s @var{bytes} | |
576 | @itemx --size=@var{bytes} | |
577 | Maximum size of the rescue domain, in bytes. It limits the amount of | |
578 | input data to be copied. If ddrescue can't determine the size of the | |
579 | input device, you may need to specify it with this option. Note that | |
580 | this option does not specify the size of the resulting @var{outfile}. | |
581 | For example, the following command creates an @var{outfile} 300 bytes | |
582 | long, but only writes data on the last 200 bytes: | |
583 | ||
584 | @example | |
585 | ddrescue -i 100 -s 200 infile outfile logfile | |
586 | @end example | |
587 | ||
588 | @item -S | |
589 | @itemx --sparse | |
590 | Use sparse writes for @var{outfile}. (The blocks of zeros are not | |
591 | actually allocated on disc). May save a lot of disc space in some cases. | |
592 | Not all systems support this. Only regular files can be sparse. | |
593 | ||
594 | @item -t | |
595 | @itemx --truncate | |
596 | Truncate @var{outfile} to zero size before writing to it. Only works for | |
597 | regular files, not for drives or partitions. | |
598 | ||
599 | @item -T @var{interval} | |
600 | @itemx --timeout=@var{interval} | |
601 | Maximum time since last successful read allowed before giving up. | |
602 | Defaults to infinity. @var{interval} is a rational number (like 1.5 or | |
603 | 1/2) optionally followed by one of @samp{s}, @samp{m}, @samp{h} or | |
604 | @samp{d}, meaning seconds, minutes, hours and days respectively. If no | |
605 | unit is specified, it defaults to seconds. | |
606 | ||
607 | @item -v | |
608 | @itemx --verbose | |
609 | Verbose mode. Further -v's (up to 4) increase the verbosity level. | |
610 | ||
611 | @item -w | |
612 | @itemx --ignore-write-errors | |
613 | Make fill mode ignore write errors. This is useful to avoid ddrescue | |
614 | exiting because of new errors developing while wiping the good sectors | |
615 | of a failing drive. Fill mode normally writes to @var{outfile} one | |
616 | cluster at a time. With this option, after the first write error is | |
617 | found in an area, the rest of that area is filled sector by sector. | |
618 | ||
619 | @item -x @var{bytes} | |
620 | @itemx --extend-outfile=@var{bytes} | |
621 | Extend the size of @var{outfile} to make it at least @var{bytes} long. | |
622 | If the size of @var{outfile} is already equal or longer than @var{bytes} | |
623 | then this option does nothing. Use this option to guarantee a minimum | |
624 | size for @var{outfile}. Only regular files can be extended. | |
625 | ||
626 | @item -1 @var{file} | |
627 | @itemx --log-rates=@var{file} | |
628 | Log rates and error sizes every second in @var{file}. If @var{file} | |
629 | already exists, it will be overwritten. Every time the screen is updated | |
630 | with new details, some of those details (time, input position, current | |
631 | and average rates, number of errors and error size) are written to | |
632 | @var{file} in a format usable by plotting utilities like gnuplot. This | |
633 | allows a posterior analysis of the drive to see if it has any weak zones | |
634 | (areas where the transfer rate drops well below the sustained average). | |
635 | ||
636 | @item -2 @var{file} | |
637 | @itemx --log-reads=@var{file} | |
638 | Log all read operations in @var{file}. If @var{file} already exists, it | |
639 | will be overwritten. Every read attempt and its result (position, size, | |
640 | copied size and error size) is written to @var{file}. (The position | |
641 | written is always the beginning of the block tried, even if reading | |
642 | backwards). A line is also written at the beginning of each phase | |
643 | (copying, trimming, splitting and retrying). Finally, a line with a time | |
644 | mark is written every second (unless the read takes more time). Use this | |
645 | option with caution because @var{file} may become very large very | |
646 | quickly. Use lzip to compress @var{file} if you need to store or | |
647 | transmit it. | |
648 | ||
649 | @end table | |
650 | ||
651 | Numbers given as arguments to options (positions, sizes, rates, etc) may | |
652 | be expressed as decimal, hexadecimal or octal values (using the same | |
653 | syntax as integer constants in C++), and may be followed by a multiplier | |
654 | and an optional @samp{B} for "byte". | |
655 | ||
656 | Table of SI and binary prefixes (unit multipliers): | |
657 | ||
658 | @multitable {Prefix} {kilobyte (10^3 = 1000)} {|} {Prefix} {kibibyte (2^10 = 1024)} | |
659 | @item Prefix @tab Value @tab | @tab Prefix @tab Value | |
660 | @item @tab @tab | @tab s @tab sectors | |
661 | @item k @tab kilobyte (10^3 = 1000) @tab | @tab Ki @tab kibibyte (2^10 = 1024) | |
662 | @item M @tab megabyte (10^6) @tab | @tab Mi @tab mebibyte (2^20) | |
663 | @item G @tab gigabyte (10^9) @tab | @tab Gi @tab gibibyte (2^30) | |
664 | @item T @tab terabyte (10^12) @tab | @tab Ti @tab tebibyte (2^40) | |
665 | @item P @tab petabyte (10^15) @tab | @tab Pi @tab pebibyte (2^50) | |
666 | @item E @tab exabyte (10^18) @tab | @tab Ei @tab exbibyte (2^60) | |
667 | @item Z @tab zettabyte (10^21) @tab | @tab Zi @tab zebibyte (2^70) | |
668 | @item Y @tab yottabyte (10^24) @tab | @tab Yi @tab yobibyte (2^80) | |
669 | @end multitable | |
670 | ||
671 | @sp 1 | |
672 | Exit status: 0 for a normal exit, 1 for environmental problems (file not | |
673 | found, invalid flags, I/O errors, etc), 2 to indicate a corrupt or | |
674 | invalid input file, 3 for an internal consistency error (eg, bug) which | |
675 | caused ddrescue to panic. | |
676 | ||
677 | If ddrescue is interrupted by a signal, it updates the logfile and then | |
678 | terminates by raising the signal received. | |
679 | ||
680 | ||
681 | @node Logfile structure | |
682 | @chapter Logfile structure | |
683 | @cindex logfile structure | |
684 | ||
685 | The logfile is a text file easy to read and edit. It is formed by three | |
686 | parts, the heading comments, the status line, and the list of data | |
687 | blocks. Any line beginning with @samp{#} is a comment line. | |
688 | ||
689 | The heading comments contain the version of ddrescue or ddrescuelog that | |
690 | created the logfile, the command line used, and the time when the | |
691 | program started. If the logfile was created by ddrescue it will also | |
692 | contain the current time when the logfile was saved and a copy of the | |
693 | status message from the screen describing the operation being performed | |
694 | (copying, trimming, finished, etc). They are intended as information for | |
695 | the user. | |
696 | ||
697 | The first non-comment line is the status line. It contains a | |
698 | non-negative integer and a status character. The integer is the position | |
699 | being tried in the input file. (The beginning of the block being tried | |
700 | in a forward pass or the end of the block in a backward pass). The | |
701 | status character is one of these: | |
702 | ||
703 | @multitable {Character} {generating approximate logfile} | |
704 | @item Character @tab Meaning | |
705 | @item '?' @tab copying non-tried blocks | |
706 | @item '*' @tab trimming non-trimmed blocks | |
707 | @item '/' @tab splitting non-split blocks | |
708 | @item '-' @tab retrying bad sectors | |
709 | @item 'F' @tab filling specified blocks | |
710 | @item 'G' @tab generating approximate logfile | |
711 | @item '+' @tab finished | |
712 | @end multitable | |
713 | ||
714 | The blocks in the list of data blocks must be contiguous and | |
715 | non-overlapping. | |
716 | ||
717 | Every line in the list of data blocks describes a block of data. It | |
718 | contains 2 non-negative integers and a status character. The first | |
719 | integer is the starting position of the block in the input file, the | |
720 | second integer is the size (in bytes) of the block. The status character | |
721 | is one of these: | |
722 | ||
723 | @multitable {Character} {failed block bad-sector(s)} | |
724 | @item Character @tab Meaning | |
725 | @item '?' @tab non-tried block | |
726 | @item '*' @tab failed block non-trimmed | |
727 | @item '/' @tab failed block non-split | |
728 | @item '-' @tab failed block bad-sector(s) | |
729 | @item '+' @tab finished block | |
730 | @end multitable | |
731 | ||
732 | @noindent | |
733 | And here is an example logfile: | |
734 | ||
735 | @example | |
736 | # Rescue Logfile. Created by GNU ddrescue version @value{VERSION} | |
737 | # Command line: ddrescue -d -c18 /dev/fd0 fdimage logfile | |
738 | # Start time: 2014-05-03 09:37:44 | |
739 | # Current time: 2014-05-03 09:38:19 | |
740 | # Copying non-tried blocks... Pass 1 (forwards) | |
741 | # current_pos current_status | |
742 | 0x00120000 ? | |
743 | # pos size status | |
744 | 0x00000000 0x00117000 + | |
745 | 0x00117000 0x00000200 - | |
746 | 0x00117200 0x00001000 / | |
747 | 0x00118200 0x00007E00 * | |
748 | 0x00120000 0x00048000 ? | |
749 | @end example | |
750 | ||
751 | If you edit the file, you may use decimal, hexadecimal or octal values, | |
752 | using the same syntax as integer constants in C++. | |
753 | ||
754 | NOTE: Logfiles generated by a version of ddrescue prior to 1.6 lack the | |
755 | status line. If you want to use an old logfile with ddrescue 1.6 or | |
756 | later, you will have to insert a line like @samp{0 +} at the beginning | |
757 | of the logfile. | |
758 | ||
759 | ||
760 | @node Examples | |
761 | @chapter A small tutorial with examples | |
762 | @cindex examples | |
763 | ||
764 | This tutorial is for those already able to use the dd command. If you | |
765 | don't know what dd is, better search the net for some introductory | |
766 | material about dd and GNU ddrescue first. | |
767 | ||
768 | A failing drive tends to develop more and more errors as time passes. | |
769 | Because of this, you should rescue the data from a drive as soon as you | |
770 | notice the first error. Be diligent because every time a physically | |
771 | damaged drive powers up and is able to output some data, it may be the | |
772 | very last time that it ever will. | |
773 | ||
774 | You should make a copy of the failing drive with ddrescue, and then try | |
775 | to repair the copy. If your data is really important, use the first copy | |
776 | as a master for a second copy, and try to repair the second copy. If | |
777 | something goes wrong, you have the master intact to try again. | |
778 | ||
779 | If you are trying to rescue a whole partition, first repair the copy | |
780 | with e2fsck or some other tool appropriate for the type of partition you | |
781 | are trying to rescue, then mount the repaired copy somewhere and try to | |
782 | recover the files in it. | |
783 | ||
784 | If the drive is so damaged that the file system in the rescued partition | |
785 | can't be repaired or mounted, you will have to browse the rescued data | |
786 | with an hex editor and extract the desired parts by hand or use a file | |
787 | recovery tool like photorec. | |
788 | ||
789 | If the partition table is damaged, you may try to rescue the whole disc, | |
790 | then try to repair the partition table and the partitions on the copy. | |
791 | ||
792 | If the damaged drive is not listed in /dev, then you cannot rescue it. | |
793 | At least not with ddrescue. | |
794 | ||
795 | @sp 1 | |
796 | @noindent | |
797 | Example 1: Rescue a whole disc with two ext2 partitions in /dev/hda to | |
798 | /dev/hdb.@* | |
799 | Note: you do not need to partition /dev/hdb beforehand, but if the | |
800 | partition table on /dev/hda is damaged, you'll need to recreate it | |
801 | somehow on /dev/hdb. | |
802 | ||
803 | @example | |
804 | ddrescue -f -n /dev/hda /dev/hdb logfile | |
805 | ddrescue -d -f -r3 /dev/hda /dev/hdb logfile | |
806 | fdisk /dev/hdb | |
807 | e2fsck -v -f /dev/hdb1 | |
808 | e2fsck -v -f /dev/hdb2 | |
809 | @end example | |
810 | ||
811 | @sp 1 | |
812 | @noindent | |
813 | Example 2: Rescue an ext2 partition in /dev/hda2 to /dev/hdb2.@* | |
814 | Note: you need to create the hdb2 partition with fdisk first. hdb2 | |
815 | should be of appropriate type and size. | |
816 | ||
817 | @example | |
818 | ddrescue -f -n /dev/hda2 /dev/hdb2 logfile | |
819 | ddrescue -d -f -r3 /dev/hda2 /dev/hdb2 logfile | |
820 | e2fsck -v -f /dev/hdb2 | |
821 | mount -t ext2 -o ro /dev/hdb2 /mnt | |
822 | (read rescued files from /mnt) | |
823 | @end example | |
824 | ||
825 | @sp 1 | |
826 | @noindent | |
827 | Example 3: Rescue a CD-ROM in /dev/cdrom. | |
828 | ||
829 | @example | |
830 | ddrescue -n -b2048 /dev/cdrom cdimage logfile | |
831 | ddrescue -d -b2048 /dev/cdrom cdimage logfile | |
832 | (if errsize is zero, cdimage now contains a complete image of the | |
833 | CD-ROM and you can write it to a blank CD-ROM) | |
834 | @end example | |
835 | ||
836 | @sp 1 | |
837 | @noindent | |
838 | Example 4: Rescue a CD-ROM in /dev/cdrom from two copies. | |
839 | ||
840 | @example | |
841 | ddrescue -n -b2048 /dev/cdrom cdimage logfile | |
842 | ddrescue -d -b2048 /dev/cdrom cdimage logfile | |
843 | (insert second copy in the CD drive) | |
844 | ddrescue -d -r1 -b2048 /dev/cdrom cdimage logfile | |
845 | (if errsize is zero, cdimage now contains a complete image of the | |
846 | CD-ROM and you can write it to a blank CD-ROM) | |
847 | @end example | |
848 | ||
849 | @sp 1 | |
850 | @noindent | |
851 | Example 5: Merge the partially recovered images of 3 identical DVDs | |
852 | using their logfiles as domain logfiles. | |
853 | ||
854 | @example | |
855 | ddrescue -m logfile1 dvdimage1 dvdimage logfile | |
856 | ddrescue -m logfile2 dvdimage2 dvdimage logfile | |
857 | ddrescue -m logfile3 dvdimage3 dvdimage logfile | |
858 | (if errsize is zero, dvdimage now contains a complete image of the DVD | |
859 | and you can write it to a blank DVD) | |
860 | @end example | |
861 | ||
862 | @sp 1 | |
863 | @anchor{lziprecover-example} | |
864 | @noindent | |
865 | Example 6: Rescue a lzip compressed backup from two copies on CD-ROM | |
866 | with error-checked merging of copies | |
867 | @ifnothtml | |
868 | (@xref{Top,lziprecover manual,,lziprecover}, | |
869 | @end ifnothtml | |
870 | @ifhtml | |
871 | (See the | |
872 | @uref{http://www.nongnu.org/lzip/manual/lziprecover_manual.html,,lziprecover manual} | |
873 | @end ifhtml | |
874 | for details about lziprecover). | |
875 | ||
876 | @example | |
877 | ddrescue -b2048 /dev/cdrom cdimage1 logfile1 | |
878 | mount -t iso9660 -o loop,ro cdimage1 /mnt/cdimage | |
879 | cp /mnt/cdimage/backup.tar.lz rescued1.tar.lz | |
880 | umount /mnt/cdimage | |
881 | (insert second copy in the CD drive) | |
882 | ddrescue -b2048 /dev/cdrom cdimage2 logfile2 | |
883 | mount -t iso9660 -o loop,ro cdimage2 /mnt/cdimage | |
884 | cp /mnt/cdimage/backup.tar.lz rescued2.tar.lz | |
885 | umount /mnt/cdimage | |
886 | lziprecover -m -v -o backup.tar.lz rescued1.tar.lz rescued2.tar.lz | |
887 | @end example | |
888 | ||
889 | @sp 1 | |
890 | @noindent | |
891 | Example 7: While rescuing the whole drive /dev/hda to /dev/hdb, /dev/hda | |
892 | freezes up at position 12345678. | |
893 | ||
894 | @example | |
895 | ddrescue -f /dev/hda /dev/hdb logfile <-- /dev/hda freezes here | |
896 | (restart /dev/hda or reboot computer) | |
897 | (restart copy at a safe distance from the troubled sector) | |
898 | ddrescue -f -i 12350000 /dev/hda /dev/hdb logfile | |
899 | (copy backwards down to the troubled sector) | |
900 | ddrescue -f -R /dev/hda /dev/hdb logfile | |
901 | @end example | |
902 | ||
903 | @sp 1 | |
904 | @noindent | |
905 | Example 8: While rescuing the whole drive /dev/hda to /dev/hdb, /dev/hdb | |
906 | fails and you have to rescue data to a third drive, /dev/hdc. | |
907 | ||
908 | @example | |
909 | ddrescue -f -n /dev/hda /dev/hdb logfile1 <-- /dev/hdb fails here | |
910 | ddrescue -f -m logfile1 /dev/hdb /dev/hdc logfile2 | |
911 | ddrescue -f -n /dev/hda /dev/hdc logfile2 | |
912 | ddrescue -d -f -r3 /dev/hda /dev/hdc logfile2 | |
913 | @end example | |
914 | ||
915 | @sp 1 | |
916 | @noindent | |
917 | Example 9: While rescuing the whole drive /dev/hda to /dev/hdb, /dev/hda | |
918 | stops responding and disappears from /dev. | |
919 | ||
920 | @example | |
921 | ddrescue -f -n /dev/hda /dev/hdb logfile <-- /dev/hda fails here | |
922 | (restart /dev/hda or reboot computer as many times as needed) | |
923 | ddrescue -f -n -A /dev/hda /dev/hdb logfile | |
924 | ddrescue -d -f -r3 /dev/hda /dev/hdb logfile | |
925 | @end example | |
926 | ||
927 | ||
928 | @node Direct disc access | |
929 | @chapter Direct disc access | |
930 | @cindex direct disc access | |
931 | @cindex raw devices | |
932 | ||
933 | If you notice that the positions and sizes in the logfile are ALWAYS | |
934 | multiples of the sector size, maybe your kernel is caching the disc | |
935 | accesses and grouping them. In this case you may want to use direct disc | |
936 | access or a raw device to bypass the kernel cache and rescue more of | |
937 | your data. | |
938 | ||
939 | NOTE! Sector size must be correctly set with the @samp{--sector-size} | |
940 | option for this to work. Only whole sectors can be read; both | |
941 | @samp{--input-position} and @samp{--size} must be a multiple of sector | |
942 | size. | |
943 | ||
944 | Try the @samp{--direct} option first. If direct disc access is not | |
945 | available in your system, try raw devices. Read your system | |
946 | documentation to find how to bind a raw device to a regular block | |
947 | device. Some OSs provide raw access through special device names, like | |
948 | /dev/rdisk. | |
949 | ||
950 | Ddrescue aligns its I/O buffer to the sector size so that it can be used | |
951 | for direct disc access or to read from raw devices. For efficiency | |
952 | reasons, also aligns it to the memory page size if page size is a | |
953 | multiple of sector size. On some systems, ddrescue can't determine the | |
954 | size of a raw device, so an explicit @samp{--size} or | |
955 | @samp{--complete-only} option may be needed. | |
956 | ||
957 | Using direct disc access, or reading from a raw device, may be slower or | |
958 | faster than normal cached reading depending on your OS and hardware. In | |
959 | case it is slower you may want to make a first pass using normal cached | |
960 | reads and use direct disc access, or a raw device, only to recover the | |
961 | good sectors inside the failed blocks. | |
962 | ||
963 | @sp 1 | |
964 | @noindent | |
965 | Example 1: using direct disc access. | |
966 | ||
967 | @example | |
968 | ddrescue -f -n /dev/hdb1 /dev/hdc1 logfile | |
969 | ddrescue -d -f -r3 /dev/hdb1 /dev/hdc1 logfile | |
970 | e2fsck -v -f /dev/hdc1 | |
971 | mount -t ext2 -o ro /dev/hdc1 /mnt | |
972 | @end example | |
973 | ||
974 | @sp 1 | |
975 | @noindent | |
976 | Example 2: using a raw device. | |
977 | ||
978 | @example | |
979 | raw /dev/raw/raw1 /dev/hdb1 | |
980 | ddrescue -f -n /dev/hdb1 /dev/hdc1 logfile | |
981 | ddrescue -C -f -r3 /dev/raw/raw1 /dev/hdc1 logfile | |
982 | raw /dev/raw/raw1 0 0 | |
983 | e2fsck -v -f /dev/hdc1 | |
984 | mount -t ext2 -o ro /dev/hdc1 /mnt | |
985 | @end example | |
986 | ||
987 | ||
988 | @node Fill mode | |
989 | @chapter Fill mode | |
990 | @cindex fill Mode | |
991 | ||
992 | When ddrescue is invoked with the @samp{--fill-mode} option it operates | |
993 | in "fill mode", which is different from the default "rescue mode". That | |
994 | is, if you use the @samp{--fill-mode} option, ddrescue does not rescue | |
995 | anything. It only fills with data read from the input file the blocks of | |
996 | the output file whose status character from the logfile coincides with | |
997 | one of the type characters specified as argument to the | |
998 | @samp{--fill-mode} option. | |
999 | ||
1000 | In fill mode the input file may have any size. If it is too small, the | |
1001 | data will be duplicated as many times as necessary to fill the input | |
1002 | buffer. If it is too big, only the data needed to fill the input buffer | |
1003 | will be read. Then the same data will be written to every cluster or | |
1004 | sector to be filled. | |
1005 | ||
1006 | Note that in fill mode the input file is always read from position 0. If | |
1007 | you specify a @samp{--input-position}, it refers to the original input | |
1008 | file from which the logfile was built, and is only used to calculate the | |
1009 | offset between input and output positions. | |
1010 | ||
1011 | Note also that when filling the input file of the original rescue run | |
1012 | you should set @samp{--input-position} and @samp{--output-position} to | |
1013 | identical values, whereas when filling the output file of the original | |
1014 | rescue run you should keep the original offset between | |
1015 | @samp{--input-position} and @samp{--output-position}. | |
1016 | ||
1017 | The @samp{--fill-mode} option implies the @samp{--complete-only} option. | |
1018 | ||
1019 | In fill mode the logfile is updated to allow resumability when | |
1020 | interrupted or in case of a crash, but as nothing is being rescued the | |
1021 | logfile is not destroyed. The status line is the only part of the | |
1022 | logfile that is modified. | |
1023 | ||
1024 | @sp 1 | |
1025 | @noindent | |
1026 | The fill mode has a number of uses. See the following examples: | |
1027 | ||
1028 | @noindent | |
1029 | Example 1: Mark parts of the rescued copy to allow finding them when | |
1030 | examined in an hex editor. For example, the following command line fills | |
1031 | all blocks marked as @samp{-} (bad-sector) with copies of the string | |
1032 | @w{@samp{BAD SECTOR }}: | |
1033 | ||
1034 | @example | |
1035 | printf "BAD SECTOR " > tmpfile | |
1036 | ddrescue --fill-mode=- tmpfile outfile logfile | |
1037 | @end example | |
1038 | ||
1039 | @noindent | |
1040 | Example 2: Wipe only the good sectors, leaving the bad sectors alone. | |
1041 | This way, the drive will still test bad (i.e., with unreadable sectors). | |
1042 | This is the fastest way of wiping a failing drive, and is specially | |
1043 | useful when sending the drive back to the manufacturer for warranty | |
1044 | replacement. | |
1045 | ||
1046 | @example | |
1047 | ddrescue --fill-mode=+ --force /dev/zero bad_drive logfile | |
1048 | @end example | |
1049 | ||
1050 | @noindent | |
1051 | Example 3: Force the drive to remap the bad sectors, making it usable | |
1052 | again. If the drive has only a few bad sectors, and they are not caused | |
1053 | by drive age, you can probably just rewrite those sectors, and the drive | |
1054 | will reallocate them automatically to new "spare" sectors that it keeps | |
1055 | for just this purpose. WARNING! This may not work on your drive. | |
1056 | ||
1057 | @example | |
1058 | ddrescue --fill-mode=- --force --synchronous /dev/zero bad_drive logfile | |
1059 | @end example | |
1060 | ||
1061 | @sp 1 | |
1062 | @noindent | |
1063 | Fill mode can also help you to figure out, independently of the file | |
1064 | system used, what files are partially or entirely in the bad areas of | |
1065 | the disc. Just follow these steps: | |
1066 | ||
1067 | 1) Copy the damaged drive with ddrescue until finished. Do not use | |
1068 | sparse writes. This yields a logfile with only finished (@samp{+}) and | |
1069 | bad-sector (@samp{-}) blocks. | |
1070 | ||
1071 | 2) Fill the bad-sector blocks of the copied drive or image file with a | |
1072 | string not present in any file, for example "DEADBEEF". | |
1073 | ||
1074 | 3) Mount the copied drive (or the image file, via loopback device). | |
1075 | ||
1076 | 4) Grep for the fill string in all the files. Those files containing the | |
1077 | string reside (at least partially) in damaged disc areas. Note that if | |
1078 | all the damaged areas are in unused space, grep will not find the string | |
1079 | in any file, which means that no files are damaged. | |
1080 | ||
1081 | 5) Unmount the copied drive or image file. | |
1082 | ||
1083 | 6) Optionally fill the bad-sector blocks of the copied drive or image | |
1084 | file with zeros to restore the disc image. | |
1085 | ||
1086 | @noindent | |
1087 | Example 4: Figure out what files are in the bad areas of the disc. | |
1088 | ||
1089 | @example | |
1090 | ddrescue -b2048 /dev/cdrom cdimage logfile | |
1091 | printf "DEADBEEF" > tmpfile | |
1092 | ddrescue --fill-mode=- tmpfile cdimage logfile | |
1093 | rm tmpfile | |
1094 | mount -t iso9660 -o loop,ro cdimage /mnt/cdimage | |
1095 | find /mnt/cdimage -type f -exec grep -l "DEADBEEF" '@{@}' ';' | |
1096 | umount /mnt/cdimage | |
1097 | ddrescue --fill-mode=- /dev/zero cdimage logfile | |
1098 | @end example | |
1099 | ||
1100 | ||
1101 | @node Generate mode | |
1102 | @chapter Generate mode | |
1103 | @cindex generate Mode | |
1104 | ||
1105 | NOTE: When ddrescue is invoked with the @samp{--generate-mode} option it | |
1106 | operates in "generate mode", which is different from the default "rescue | |
1107 | mode". That is, if you use the @samp{--generate-mode} option, ddrescue | |
1108 | does not rescue anything. It only tries to generate a logfile for later | |
1109 | use. | |
1110 | ||
1111 | So you didn't read the tutorial and started ddrescue without a logfile. | |
1112 | Now, two days later, your computer crashed and you can't know how much | |
1113 | data ddrescue managed to save. And even worse, you can't resume the | |
1114 | rescue; you have to restart it from the very beginning. | |
1115 | ||
1116 | Or maybe you started copying a drive with @w{@code{dd | |
1117 | conv=noerror,sync}} and are now in the same situation described above. | |
1118 | In this case, note that you can't use a copy made by dd unless it was | |
1119 | invoked with the @samp{sync} conversion argument. | |
1120 | ||
1121 | Don't despair (yet). Ddrescue can in some cases generate an approximate | |
1122 | logfile, from the input file and the (partial) copy, that is almost as | |
1123 | good as an exact logfile. It makes this by simply assuming that sectors | |
1124 | containing all zeros were not rescued. | |
1125 | ||
1126 | However, if the destination of the copy was a drive or a partition, (or | |
1127 | an existing regular file and truncation was not requested), most | |
1128 | probably you will need to restart ddrescue from the very beginning. | |
1129 | (This time with a logfile, of course). The reason is that old data may | |
1130 | be present in the drive that have not been overwritten yet, and may be | |
1131 | thus non-tried but non-zero. | |
1132 | ||
1133 | For example, if you first tried one of these commands: | |
1134 | @example | |
1135 | ddrescue infile outfile | |
1136 | or | |
1137 | dd if=infile of=outfile conv=noerror,sync | |
1138 | @end example | |
1139 | ||
1140 | then you can generate an approximate logfile with this command: | |
1141 | @example | |
1142 | ddrescue --generate-mode infile outfile logfile | |
1143 | @end example | |
1144 | ||
1145 | @noindent | |
1146 | Note that you must keep the original offset between | |
1147 | @samp{--input-position} and @samp{--output-position} of the original | |
1148 | rescue run. | |
1149 | ||
1150 | ||
1151 | @node Ddrescuelog | |
1152 | @chapter Ddrescuelog | |
1153 | @cindex ddrescuelog | |
1154 | ||
1155 | Ddrescuelog is a tool that manipulates ddrescue logfiles, shows logfile | |
1156 | contents, converts logfiles to/from other formats, compares logfiles, | |
1157 | tests rescue status, and can delete a logfile if the rescue is done. | |
1158 | Ddrescuelog operations can be restricted to one or several parts of the | |
1159 | logfile if the domain setting options are used. | |
1160 | ||
1161 | When performing logic operations (AND, OR, XOR) on logfiles of different | |
1162 | extension, only the blocks present in both files are processed. | |
1163 | ||
1164 | Here are some examples of how to use ddrescuelog, alone or in | |
1165 | combination with other tools. | |
1166 | ||
1167 | @sp 1 | |
1168 | @noindent | |
1169 | Example 1: Delete the logfile if the rescue is finished (all data is | |
1170 | recovered without errors left). | |
1171 | ||
1172 | @example | |
1173 | ddrescue -f /dev/hda /dev/hdb logfile | |
1174 | ddrescuelog -d logfile | |
1175 | @end example | |
1176 | ||
1177 | @sp 1 | |
1178 | @noindent | |
1179 | Example 2: Rescue two ext2 partitions in /dev/hda to | |
1180 | /dev/hdb and repair the file systems using badblock lists generated with | |
1181 | ddrescuelog. File system block size is 4096.@* | |
1182 | Note: you do need to partition /dev/hdb beforehand. | |
1183 | ||
1184 | @example | |
1185 | fdisk /dev/hdb <-- partition /deb/hdb | |
1186 | ddrescue -f /dev/hda1 /dev/hdb1 logfile1 | |
1187 | ddrescue -f /dev/hda2 /dev/hdb2 logfile2 | |
1188 | ddrescuelog -l- -b4096 logfile1 > badblocks1 | |
1189 | ddrescuelog -l- -b4096 logfile2 > badblocks2 | |
1190 | e2fsck -v -f -L badblocks1 /dev/hdb1 | |
1191 | e2fsck -v -f -L badblocks2 /dev/hdb2 | |
1192 | @end example | |
1193 | ||
1194 | @sp 1 | |
1195 | @noindent | |
1196 | Example 3: Rescue a whole disc with two ext2 partitions in /dev/hda to | |
1197 | /dev/hdb and repair the file systems using badblock lists generated with | |
1198 | ddrescuelog. Disc sector size is 512, file system block size is 4096. | |
1199 | Arguments to options @samp{-i} and @samp{-s} are the starting positions | |
1200 | and sizes of the partitions being rescued.@* | |
1201 | Note: you do not need to partition /dev/hdb beforehand, but if the | |
1202 | partition table on /dev/hda is damaged, you'll need to recreate it | |
1203 | somehow on /dev/hdb. | |
1204 | ||
1205 | @example | |
1206 | ddrescue -f /dev/hda /dev/hdb logfile | |
1207 | fdisk /dev/hdb <-- get partition sizes | |
1208 | ddrescuelog -l- -b512 -i63b -o0 -s9767457b -b4096 logfile > badblocks1 | |
1209 | ddrescuelog -l- -b512 -i9767520b -o0 -s128520b -b4096 logfile > badblocks2 | |
1210 | e2fsck -v -f -L badblocks1 /dev/hdb1 | |
1211 | e2fsck -v -f -L badblocks2 /dev/hdb2 | |
1212 | @end example | |
1213 | ||
1214 | ||
1215 | @node Invoking ddrescuelog | |
1216 | @chapter Invoking ddrescuelog | |
1217 | @cindex invoking ddrescuelog | |
1218 | ||
1219 | The format for running ddrescuelog is: | |
1220 | ||
1221 | @example | |
1222 | ddrescuelog [@var{options}] @var{logfile} | |
1223 | @end example | |
1224 | ||
1225 | Ddrescuelog supports the following options: | |
1226 | ||
1227 | @table @samp | |
1228 | @item -h | |
1229 | @itemx --help | |
1230 | Print an informative help message describing the options and exit. | |
1231 | ||
1232 | @item -V | |
1233 | @itemx --version | |
1234 | Print the version number of ddrescuelog on the standard output and exit. | |
1235 | ||
1236 | @item -a @var{old_types},@var{new_types} | |
1237 | @itemx --change-types=@var{old_types},@var{new_types} | |
1238 | Change the status of every block in the rescue domain from one type in | |
1239 | @var{old_types} to the corresponding type in @var{new_types}, much like | |
1240 | the @samp{tr} command does, and write the resulting logfile to standard | |
1241 | output. @var{old_types} and @var{new_types} are strings of block status | |
1242 | characters as defined in the chapter Logfile structure (@pxref{Logfile | |
1243 | structure}). Blocks whose status is not in @var{old_types} are left | |
1244 | unchanged. If @var{new_types} is shorter than @var{old_types} the last | |
1245 | type of @var{new_types} is repeated as many times as necessary. | |
1246 | ||
1247 | @item -b @var{bytes} | |
1248 | @itemx --block-size=@var{bytes} | |
1249 | Block size used by ddrescuelog. Depending on the requested operation it | |
1250 | may be the sector size of the input device, the block size of the | |
1251 | rescued file system, etc. Defaults to 512. | |
1252 | ||
1253 | @item -B | |
1254 | @itemx --binary-prefixes | |
1255 | Show units with binary prefixes (powers of 1024).@* | |
1256 | SI prefixes (powers of 1000) are used by default. (See table above, | |
1257 | @ref{Invoking ddrescue}). | |
1258 | ||
1259 | @item -c[@var{type1}@var{type2}] | |
1260 | @itemx --create-logfile[=@var{type1}@var{type2}] | |
1261 | Create a logfile from a list of block numbers read from standard input. | |
1262 | Only blocks included in the rescue domain will be added to @var{logfile}. | |
1263 | ||
1264 | @var{type1} and @var{type2} are block status characters as defined in | |
1265 | the chapter Logfile structure (@pxref{Logfile structure}). @var{type1} | |
1266 | sets the type for blocks included in the list, while @var{type2} sets | |
1267 | the type for the rest of the logfile. If not specified, @var{type1} | |
1268 | defaults to @samp{+} and @var{type2} defaults to @samp{-}. | |
1269 | ||
1270 | @item -C[@var{type}] | |
1271 | @itemx --complete-logfile[=@var{type}] | |
1272 | Complete a synthetic (user fabricated) @var{logfile} by filling the gaps | |
1273 | with blocks of type @var{type}, and write the completed logfile to | |
1274 | standard output. @var{type} is one of the block status characters | |
1275 | defined in the chapter Logfile structure (@pxref{Logfile structure}). If | |
1276 | @var{type} is not specified, the gaps are filled with non-tried blocks. | |
1277 | All gaps in the logfile are filled. Domain options are ignored. | |
1278 | ||
1279 | @item -d | |
1280 | @itemx --delete-if-done | |
1281 | Delete the given @var{logfile} if all the blocks in the rescue domain | |
1282 | have been successfuly recovered. The exit status is 0 if @var{logfile} | |
1283 | could be deleted, 1 otherwise. | |
1284 | ||
1285 | @item -D | |
1286 | @itemx --done-status | |
1287 | Test if all the blocks in the rescue domain have been successfuly | |
1288 | recovered. The exit status is 0 if all tested blocks are finished, 1 | |
1289 | otherwise. | |
1290 | ||
1291 | @item -f | |
1292 | @itemx --force | |
1293 | Force overwrite of @var{logfile}. | |
1294 | ||
1295 | @item -i @var{bytes} | |
1296 | @itemx --input-position=@var{bytes} | |
1297 | Starting position of the rescue domain, in bytes. Defaults to 0. It | |
1298 | refers to a position in the original input file. | |
1299 | ||
1300 | @item -l @var{types} | |
1301 | @itemx --list-blocks=@var{types} | |
1302 | Print on standard output the block numbers of the blocks specified as | |
1303 | any of @var{types} in @var{logfile} and included in the rescue domain. | |
1304 | @var{types} contains one or more of the block status characters defined | |
1305 | in the chapter Logfile structure (@pxref{Logfile structure}). | |
1306 | ||
1307 | The list format is one block number per line in decimal, like the output | |
1308 | of the badblocks program, so that it can be used as input for e2fsck or | |
1309 | other similar filesystem repairing tool. | |
1310 | ||
1311 | @item -L | |
1312 | @itemx --loose-domain | |
1313 | Accept an incomplete synthetic (user fabricated) domain logfile or | |
1314 | compare-as-domain logfile and fill the gaps with non-tried blocks. The | |
1315 | blocks in the logfile must be strictly ascending and non-overlapping, | |
1316 | but they do not need to be contiguous. This option allows making quick | |
1317 | edits to a logfile without all the size calculations involved in making | |
1318 | all data blocks contiguous again. | |
1319 | ||
1320 | @item -m @var{file} | |
1321 | @itemx --domain-logfile=@var{file} | |
1322 | Restrict the rescue domain to the blocks marked as finished in the | |
1323 | logfile @var{file}. | |
1324 | ||
1325 | @item -n | |
1326 | @itemx --invert-logfile | |
1327 | Invert the types of the blocks in @var{logfile} which are included in | |
1328 | the rescue domain, and write the resulting logfile to standard output. | |
1329 | Finished blocks (@samp{+}) are changed to bad-sector (@samp{-}), all | |
1330 | other types are changed to finished. @samp{--invert-logfile} is | |
1331 | equivalent to @samp{--change-types=?*/-+,++++-} | |
1332 | ||
1333 | @item -o @var{bytes} | |
1334 | @itemx --output-position=@var{bytes} | |
1335 | Starting position of the image of the rescue domain in the output file, | |
1336 | in bytes. Is used by the @samp{--list-blocks} option. Defaults to | |
1337 | @samp{--input-position}. | |
1338 | ||
1339 | @item -p @var{file} | |
1340 | @itemx --compare-logfile=@var{file} | |
1341 | Compare the types of the blocks included in the rescue domain. The exit | |
1342 | status is 0 if all tested blocks are the same in both @var{file} and | |
1343 | @var{logfile}, 1 otherwise. | |
1344 | ||
1345 | @item -P @var{file} | |
1346 | @itemx --compare-as-domain=@var{file} | |
1347 | Compare only the blocks marked as finished in the rescue domain. The | |
1348 | exit status is 0 if all tested blocks are the same in both @var{file} | |
1349 | and @var{logfile}, 1 otherwise. Two files comparing equal with this | |
1350 | option are equivalent when used as domain logfiles. | |
1351 | ||
1352 | @item -q | |
1353 | @itemx --quiet | |
1354 | Quiet operation. Suppress all messages. | |
1355 | ||
1356 | @item -s @var{bytes} | |
1357 | @itemx --size=@var{bytes} | |
1358 | Maximum size of the rescue domain, in bytes. It refers to a size in the | |
1359 | original input file. | |
1360 | ||
1361 | @item -t | |
1362 | @itemx --show-status | |
1363 | Print a summary of @var{logfile} contents on the standard output. The | |
1364 | summary can be restricted to one or several parts of @var{logfile} if | |
1365 | the domain setting options are used. | |
1366 | ||
1367 | @item -v | |
1368 | @itemx --verbose | |
1369 | Verbose mode. Further -v's (up to 4) increase the verbosity level. | |
1370 | ||
1371 | @item -x @var{file} | |
1372 | @itemx --xor-logfile=@var{file} | |
1373 | Perform a logical XOR (exclusive OR) operation between the finished | |
1374 | blocks in @var{file} and those in @var{logfile}, and write the resulting | |
1375 | logfile to standard output. In other words, in the resulting logfile a | |
1376 | block is only shown as finished if it was finished in either of the two | |
1377 | input logfiles but not in both. | |
1378 | ||
1379 | @item -y @var{file} | |
1380 | @itemx --and-logfile=@var{file} | |
1381 | Perform a logical AND operation between the finished blocks in | |
1382 | @var{file} and those in @var{logfile}, and write the resulting logfile | |
1383 | to standard output. In other words, in the resulting logfile a block is | |
1384 | only shown as finished if it was finished in both input logfiles. | |
1385 | ||
1386 | @item -z @var{file} | |
1387 | @itemx --or-logfile=@var{file} | |
1388 | Perform a logical OR operation between the finished blocks in @var{file} | |
1389 | and those in @var{logfile}, and write the resulting logfile to standard | |
1390 | output. In other words, in the resulting logfile a block is shown as | |
1391 | finished if it was finished in either of the two input logfiles. | |
1392 | ||
1393 | @end table | |
1394 | ||
1395 | Exit status: 0 for a normal exit, 1 for environmental problems (file not | |
1396 | found, invalid flags, I/O errors, etc), 2 to indicate a corrupt or | |
1397 | invalid input file, 3 for an internal consistency error (eg, bug) which | |
1398 | caused ddrescuelog to panic. | |
1399 | ||
1400 | ||
1401 | @node Problems | |
1402 | @chapter Reporting bugs | |
1403 | @cindex bugs | |
1404 | @cindex getting help | |
1405 | ||
1406 | There are probably bugs in ddrescue. There are certainly errors and | |
1407 | omissions in this manual. If you report them, they will get fixed. If | |
1408 | you don't, no one will ever know about them and they will remain unfixed | |
1409 | for all eternity, if not longer. | |
1410 | ||
1411 | If you find a bug in GNU ddrescue, please send electronic mail to | |
1412 | @email{bug-ddrescue@@gnu.org}. Include the version number, which you can | |
1413 | find by running @w{@samp{ddrescue --version}}. | |
1414 | ||
1415 | ||
1416 | @node Concept index | |
1417 | @unnumbered Concept index | |
1418 | ||
1419 | @printindex cp | |
1420 | ||
1421 | @bye |
0 | \input texinfo @c -*-texinfo-*- | |
1 | @c %**start of header | |
2 | @setfilename ddrescue.info | |
3 | @documentencoding ISO-8859-15 | |
4 | @settitle GNU ddrescue Manual | |
5 | @finalout | |
6 | @c %**end of header | |
7 | ||
8 | @set UPDATED 9 July 2013 | |
9 | @set VERSION 1.17 | |
10 | ||
11 | @dircategory GNU Packages | |
12 | @direntry | |
13 | * Ddrescue: (ddrescue). Data recovery tool | |
14 | @end direntry | |
15 | ||
16 | ||
17 | @ifnothtml | |
18 | @titlepage | |
19 | @title GNU ddrescue | |
20 | @subtitle Data recovery tool | |
21 | @subtitle for Ddrescue version @value{VERSION}, @value{UPDATED} | |
22 | @author by Antonio Diaz Diaz | |
23 | ||
24 | @page | |
25 | @vskip 0pt plus 1filll | |
26 | @end titlepage | |
27 | ||
28 | @contents | |
29 | @end ifnothtml | |
30 | ||
31 | @node Top | |
32 | @top | |
33 | ||
34 | This manual is for GNU ddrescue (version @value{VERSION}, @value{UPDATED}). | |
35 | ||
36 | @menu | |
37 | * Introduction:: Purpose and features of GNU ddrescue | |
38 | * Basic concepts:: Blocks, clusters, devices, files, sectors, etc | |
39 | * Important advice:: Read this or risk losing your data | |
40 | * Algorithm:: How ddrescue recovers the data | |
41 | * Invoking ddrescue:: Command line interface | |
42 | * Logfile structure:: Detailed format of the logfile | |
43 | * Examples:: A small tutorial with examples | |
44 | * Direct disc access:: Bypassing the kernel cache | |
45 | * Fill mode:: Selectively overwriting the output file | |
46 | * Generate mode:: Generating an approximate logfile | |
47 | * Ddrescuelog:: Tool for ddrescue logfiles | |
48 | * Invoking ddrescuelog:: Command line interface | |
49 | * Problems:: Reporting bugs | |
50 | * Concept index:: Index of concepts | |
51 | @end menu | |
52 | ||
53 | @sp 1 | |
54 | Copyright @copyright{} 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, | |
55 | 2012, 2013 Antonio Diaz Diaz. | |
56 | ||
57 | This manual is free documentation: you have unlimited permission | |
58 | to copy, distribute and modify it. | |
59 | ||
60 | ||
61 | @node Introduction | |
62 | @chapter Introduction | |
63 | @cindex introduction | |
64 | ||
65 | GNU ddrescue is a data recovery tool. It copies data from one file or | |
66 | block device (hard disc, cdrom, etc) to another, trying hard to rescue | |
67 | data in case of read errors. | |
68 | ||
69 | The basic operation of ddrescue is fully automatic. That is, you don't | |
70 | have to wait for an error, stop the program, read the log, restart it | |
71 | from a new position, etc. | |
72 | ||
73 | If you use the logfile feature of ddrescue, the data is rescued very | |
74 | efficiently, (only the needed blocks are read). Also you can interrupt | |
75 | the rescue at any time and resume it later at the same point. | |
76 | ||
77 | Ddrescue does not write zeros to the output when it finds bad sectors in | |
78 | the input, and does not truncate the output file if not asked to. So, | |
79 | every time you run it on the same output file, it tries to fill in the | |
80 | gaps without wiping out the data already rescued. | |
81 | ||
82 | Automatic merging of backups: If you have two or more damaged copies of | |
83 | a file, cdrom, etc, and run ddrescue on all of them, one at a time, | |
84 | with the same output file, you will probably obtain a complete and | |
85 | error-free file. This is so because the probability of having damaged | |
86 | areas at the same places on different input files is very low. Using | |
87 | the logfile, only the needed blocks are read from the second and | |
88 | successive copies. | |
89 | ||
90 | Ddrescue recommends lzip for compression of backups because of its | |
91 | reliability and data recovery capabilities, including error-checked | |
92 | merging of backup copies. Lziprecover makes lzip files resistant to | |
93 | bit-flip, one of the most common forms of data corruption, and its | |
94 | recovery capabilities contribute to make of the lzip format one of the | |
95 | best formats for long-term data archiving. The combination ddrescue + | |
96 | lziprecover is the best option for recovering data from multiple damaged | |
97 | copies. @xref{lziprecover-example}, for an example. | |
98 | ||
99 | Recordable CD and DVD media keep their data only for a finite time | |
100 | (typically for many years). After that time, data loss develops slowly | |
101 | with read errors growing from the outer media region towards the inside. | |
102 | Just make two (or more) copies of every important CD/DVD you burn so | |
103 | that you can later recover them with ddrescue. | |
104 | ||
105 | Because ddrescue needs to read and write at random places, it only works | |
106 | on seekable (random access) input and output files. | |
107 | ||
108 | If your system supports it, ddrescue can use direct disc access to read | |
109 | the input file, bypassing the kernel cache. | |
110 | ||
111 | Ddrescue also features a "fill mode" able to selectively overwrite parts | |
112 | of the output file, which has a number of interesting uses like wiping | |
113 | data, marking bad areas or even, in some cases, "repair" damaged | |
114 | sectors. | |
115 | ||
116 | ||
117 | @node Basic concepts | |
118 | @chapter Basic concepts | |
119 | @cindex basic concepts | |
120 | ||
121 | @table @asis | |
122 | @item Block | |
123 | Any amount of data. A block is described by its starting position and | |
124 | its size. | |
125 | ||
126 | @item Cluster | |
127 | Group of consecutive sectors read or written in one go. | |
128 | ||
129 | @item Device | |
130 | Piece of hardware containing data. Hard disc drives, cdrom drives, USB | |
131 | pendrives, are devices. /dev/hda, /dev/sdb, are device names. | |
132 | ||
133 | @item File | |
134 | Files are named units of data which are stored by the operating system | |
135 | for you to retrieve later by name. Devices and partitions are accessed | |
136 | by means of their associated file names. | |
137 | ||
138 | @item Partition | |
139 | Every part in which a device is divided. A partition normally contains a | |
140 | file system. /dev/hda1, /dev/sdb3, are partition names. | |
141 | ||
142 | @item Recoverable formats | |
143 | As ddrescue uses standard library functions to read data from the device | |
144 | being rescued, only mountable device formats can be rescued with | |
145 | ddrescue. DVDs can be mounted and they can be rescued, "compact disc | |
146 | digital audio" CDs can't, "video CDs"[1] maybe.@* | |
147 | [1] http://en.wikipedia.org/wiki/Video_CD | |
148 | ||
149 | @item Rescue domain | |
150 | Block or set of blocks to be acted upon (rescued, listed, etc). You can | |
151 | define it with the options @samp{--input-position}, @samp{--size} and | |
152 | @samp{--domain-logfile}. The rescue domain defaults to the whole input | |
153 | file or logfile. | |
154 | ||
155 | @item Sector | |
156 | Hardware block. Smallest accessible amount of data on a device. | |
157 | ||
158 | @end table | |
159 | ||
160 | ||
161 | @node Important advice | |
162 | @chapter Using ddrescue safely | |
163 | @cindex using ddrescue safely | |
164 | ||
165 | Ddrescue is like any other power tool. You need to understand what it | |
166 | does, and you need to understand some things about the machines it does | |
167 | those things to, in order to use it safely. | |
168 | ||
169 | Always use a logfile unless you know you won't need it. Without a | |
170 | logfile, ddrescue can't resume a rescue, only reinitiate it. | |
171 | ||
172 | Never try to rescue a r/w mounted partition. The resulting copy may be | |
173 | useless. | |
174 | ||
175 | Never try to repair a file system on a drive with I/O errors; you will | |
176 | probably lose even more data. | |
177 | ||
178 | If you use a device or a partition as destination, any data stored there | |
179 | will be overwritten. | |
180 | ||
181 | Some systems may change device names on reboot (eg. udev enabled | |
182 | systems). If you reboot, check the device names before restarting | |
183 | ddrescue. | |
184 | ||
185 | If you interrupt the rescue and then reboot, any partially copied | |
186 | partitions should be hidden before allowing them to be touched by any | |
187 | operating system that tries to mount and "fix" the partitions it sees. | |
188 | ||
189 | ||
190 | @node Algorithm | |
191 | @chapter Algorithm | |
192 | @cindex algorithm | |
193 | ||
194 | GNU ddrescue manages efficiently the status of the rescue in progress | |
195 | and tries to rescue the good parts first, scheduling reads inside bad | |
196 | (or slow) areas for later. This maximizes the amount of data that can be | |
197 | finally recovered from a failing drive. | |
198 | ||
199 | The standard dd utility can be used to save data from a failing drive, | |
200 | but it reads the data sequentially, which may wear out the drive without | |
201 | rescuing anything if the errors are at the beginning of the drive. | |
202 | ||
203 | Other programs switch to small size reads when they find errors, but | |
204 | they still read the data sequentially. This is a bad idea because it | |
205 | means spending more time at error areas, damaging the surface, the heads | |
206 | and the drive mechanics, instead of getting out of them as fast as | |
207 | possible. This behavior reduces the chances of rescuing the remaining | |
208 | good data. | |
209 | ||
210 | The algorithm of ddrescue is as follows (the user may interrupt the | |
211 | process at any point, but be aware that a bad drive can block ddrescue | |
212 | for a long time until the kernel gives up): | |
213 | ||
214 | 1) Optionally read a logfile describing the status of a multi-part or | |
215 | previously interrupted rescue. If no logfile is specified or is empty or | |
216 | does not exist, mark all the rescue domain as non-tried. | |
217 | ||
218 | 2) (First phase; Copying) Read the non-tried parts of the input file, | |
219 | marking the failed blocks as non-trimmed and skipping beyond them, until | |
220 | all the rescue domain is tried. Only non-tried areas are read in large | |
221 | blocks. Trimming, splitting and retrying are done sector by sector. Each | |
222 | sector is tried at most two times; the first in this step as part of a | |
223 | large block read, the second in one of the steps below as a single | |
224 | sector read. | |
225 | ||
226 | 3) (Second phase; Trimming) Read forwards one sector at a time from the | |
227 | leading edge of the largest non-trimmed block, until a bad sector is | |
228 | found. Then read backwards one sector at a time from the trailing edge | |
229 | of the same block, until a bad sector is found. For each non-trimmed | |
230 | block, mark the bad sectors found as bad-sector and mark the rest of | |
231 | that block as non-split. Repeat until there are no more non-trimmed | |
232 | blocks. | |
233 | ||
234 | 4) (Third phase; Splitting) Read forwards one sector at a time from the | |
235 | center of the largest non-split block, until a bad sector is found. Then | |
236 | read backwards one sector at a time from the center of the same block, | |
237 | until a bad sector is found. If the logfile is larger than | |
238 | @samp{--logfile-size}, read the smallest non-split blocks until the | |
239 | number of entries in the logfile drops below @samp{--logfile-size}. | |
240 | Repeat until all remaining non-split blocks have less than 5 sectors. | |
241 | Then read the remaining non-split blocks sequentially. | |
242 | ||
243 | 5) (Fourth phase; Retrying) Optionally try to read again the bad sectors | |
244 | until the specified number of retries is reached. | |
245 | ||
246 | 6) Optionally write a logfile for later use. | |
247 | ||
248 | @sp 1 | |
249 | Note that as ddrescue splits the failed blocks, making them smaller, the | |
250 | total error size may diminish while the number of errors increases. | |
251 | ||
252 | The logfile is periodically saved to disc, as well as when ddrescue | |
253 | finishes or is interrupted. So in case of a crash you can resume the | |
254 | rescue with little recopying. | |
255 | ||
256 | Also, the same logfile can be used for multiple commands that copy | |
257 | different areas of the input file, and for multiple recovery attempts | |
258 | over different subsets. See this example: | |
259 | ||
260 | @noindent | |
261 | Rescue the most important part of the disc first. | |
262 | @example | |
263 | ddrescue -i0 -s50MiB /dev/hdc hdimage logfile | |
264 | ddrescue -i0 -s1MiB -d -r3 /dev/hdc hdimage logfile | |
265 | @end example | |
266 | ||
267 | @noindent | |
268 | Then rescue some key disc areas. | |
269 | @example | |
270 | ddrescue -i30GiB -s10GiB /dev/hdc hdimage logfile | |
271 | ddrescue -i230GiB -s5GiB /dev/hdc hdimage logfile | |
272 | @end example | |
273 | ||
274 | @noindent | |
275 | Now rescue the rest (does not recopy what is already done). | |
276 | @example | |
277 | ddrescue /dev/hdc hdimage logfile | |
278 | ddrescue -d -r3 /dev/hdc hdimage logfile | |
279 | @end example | |
280 | ||
281 | ||
282 | @node Invoking ddrescue | |
283 | @chapter Invoking ddrescue | |
284 | @cindex invoking ddrescue | |
285 | @cindex options | |
286 | @cindex usage | |
287 | @cindex version | |
288 | ||
289 | The format for running ddrescue is: | |
290 | ||
291 | @example | |
292 | ddrescue [@var{options}] @var{infile} @var{outfile} [@var{logfile}] | |
293 | @end example | |
294 | ||
295 | ddrescue supports the following options: | |
296 | ||
297 | @table @samp | |
298 | @item -h | |
299 | @itemx --help | |
300 | Print an informative help message describing the options and exit. | |
301 | ||
302 | @item -V | |
303 | @itemx --version | |
304 | Print the version number of ddrescue on the standard output and exit. | |
305 | ||
306 | @item -a @var{bytes} | |
307 | @itemx --min-read-rate=@var{bytes} | |
308 | Minimum read rate of good non-tried areas, in bytes per second. If the | |
309 | read rate falls below this value, ddrescue will skip ahead a variable | |
310 | amount depending on rate and error history. The skipped blocks are tried | |
311 | in additional passes (before trimming) where the minimum read rate is | |
312 | divided by ten before each pass, until there are no more non-tried | |
313 | blocks left. If @var{bytes} is 0 (auto), the minimum read rate is | |
314 | recalculated for each block read as @w{(average_rate / 10)}. | |
315 | ||
316 | @item -A | |
317 | @itemx --try-again | |
318 | Mark all non-split and non-trimmed blocks inside the rescue domain as | |
319 | non-tried before beginning the rescue. Try this if the drive stops | |
320 | responding and ddrescue immediately starts splitting failed blocks when | |
321 | restarted. If @samp{--retrim} is also specified, mark all failed blocks | |
322 | inside the rescue domain as non-tried. | |
323 | ||
324 | @item -b @var{bytes} | |
325 | @itemx --sector-size=@var{bytes} | |
326 | Sector (hardware block) size of input device in bytes (usually 512 for | |
327 | hard discs and 3.5" floppies, 1024 for 5.25" floppies, and 2048 for | |
328 | cdroms). Defaults to 512. | |
329 | ||
330 | @item -B | |
331 | @itemx --binary-prefixes | |
332 | Show units with binary prefixes (powers of 1024).@* | |
333 | SI prefixes (powers of 1000) are used by default. (See table below). | |
334 | ||
335 | @item -c @var{sectors} | |
336 | @itemx --cluster-size=@var{sectors} | |
337 | Number of sectors to copy at a time. Defaults to @w{64 KiB / sector_size}. | |
338 | Try smaller values for slow drives. The number of sectors per track (18 | |
339 | or 9) is a good value for floppies. | |
340 | ||
341 | @item -C | |
342 | @itemx --complete-only | |
343 | Limit rescue domain to the blocks listed in the @var{logfile}. Do not | |
344 | read new data beyond @var{logfile} limits. This is useful when reading | |
345 | from devices of undefined size (like raw devices), when the drive | |
346 | returns an incorrect size, or when reading from a partial copy. It can | |
347 | only be used after a first rescue attempt, possibly limited with the | |
348 | @samp{--size} option, has produced a complete @var{logfile}. | |
349 | ||
350 | @item -d | |
351 | @itemx --direct | |
352 | Use direct disc access to read from @var{infile}, bypassing the kernel | |
353 | cache. (Open the file with the O_DIRECT flag). Use it only on devices or | |
354 | partitions, not on regular files. Sector size must be correctly set for | |
355 | this to work. Not all systems support this. | |
356 | ||
357 | If your system does not support direct disc access, ddrescue will warn | |
358 | you. If the sector size is not correctly set, all reads will result in | |
359 | errors, and no data will be rescued. | |
360 | ||
361 | @item -D | |
362 | @itemx --synchronous | |
363 | Use synchronous writes for @var{outfile}. (Issue a fsync call after | |
364 | every write). May be useful when forcing the drive to remap its bad | |
365 | sectors. | |
366 | ||
367 | @item -e [+]@var{n} | |
368 | @itemx --max-errors=[+]@var{n} | |
369 | Maximum number of error areas allowed before giving up. Defaults to | |
370 | infinity. If @var{n} is preceded by @samp{+} the number refers to new | |
371 | error areas found in this run, not counting those already annotated in | |
372 | the @var{logfile}. | |
373 | ||
374 | @item -E @var{bytes} | |
375 | @itemx --max-error-rate=@var{bytes} | |
376 | Maximum rate of errors allowed before giving up, in bytes per second. | |
377 | Defaults to infinity. The rate being measured is that of actually failed | |
378 | reads, so the rescue may finish because of this rate being exceeded even | |
379 | if the total error size (errsize) does not change because the areas | |
380 | being tried are already marked as errors. | |
381 | ||
382 | @item -f | |
383 | @itemx --force | |
384 | Force overwrite of @var{outfile}. Needed when @var{outfile} is not a | |
385 | regular file, but a device or partition. | |
386 | ||
387 | @item -F @var{types} | |
388 | @itemx --fill-mode=@var{types} | |
389 | Fill the blocks in @var{outfile} specified as any of @var{types} in | |
390 | @var{logfile}, with data read from @var{infile}. @var{types} contains | |
391 | one or more of the status characters defined in the chapter Logfile | |
392 | Structure (@pxref{Logfile structure}). See the chapter Fill mode | |
393 | (@pxref{Fill mode}) for a complete description of the fill mode. | |
394 | ||
395 | @item -g | |
396 | @itemx --generate-mode | |
397 | Generate an approximate @var{logfile} from the @var{infile} and | |
398 | @var{outfile} of the original rescue run. Note that you must keep the | |
399 | original offset between @samp{--input-position} and | |
400 | @samp{--output-position} of the original rescue run. See the chapter | |
401 | Generate mode (@pxref{Generate mode}) for a complete description of the | |
402 | generate mode. | |
403 | ||
404 | @item -i @var{bytes} | |
405 | @itemx --input-position=@var{bytes} | |
406 | Starting position in @var{infile}, in bytes. Defaults to 0. In fill mode | |
407 | it refers to a position in the @var{infile} of the original rescue run. | |
408 | See the chapter Fill mode (@pxref{Fill mode}) for details. | |
409 | ||
410 | @item -I | |
411 | @itemx --verify-input-size | |
412 | Compare the size of @var{infile} with the size calculated from the list | |
413 | of blocks contained in the @var{logfile}, and exit with status 1 if they | |
414 | differ. This is not enabled by default because the size of some devices | |
415 | can't be known in advance and because the size derived from the | |
416 | @var{logfile} may be incomplete, for example after doing a partial | |
417 | rescue. | |
418 | ||
419 | @item -K @var{bytes} | |
420 | @itemx --skip-size=@var{bytes} | |
421 | Set the initial size to skip on the first read error or slow read. The | |
422 | value given will be rounded to the next multiple of sector size. For | |
423 | subsequent read errors or slow reads, ddrescue will modify the skip size | |
424 | depending on error rate and error history. Valid values range from 64 KiB | |
425 | to 1 GiB. Defaults to 64 KiB. | |
426 | ||
427 | @item -l @var{entries} | |
428 | @itemx --logfile-size=@var{entries} | |
429 | During the splitting phase, do not grow logfile beyond this number of | |
430 | entries. Logfile may be larger if it was larger at startup or if it | |
431 | became larger during the copying or trimming phases. Defaults to 1000. | |
432 | (Each entry is about 26-30 bytes in size). | |
433 | ||
434 | @item -m @var{file} | |
435 | @itemx --domain-logfile=@var{file} | |
436 | Restrict the rescue domain to the blocks marked as finished in the | |
437 | logfile @var{file}. This is useful if the destination drive fails during | |
438 | the rescue. | |
439 | ||
440 | @item -M | |
441 | @itemx --retrim | |
442 | Mark all failed blocks inside the rescue domain as non-trimmed before | |
443 | beginning the rescue. The effect is similar to @samp{--retries=1}, but | |
444 | the bad sectors are tried in a different order, making perhaps possible | |
445 | to rescue some of them. | |
446 | ||
447 | @item -n | |
448 | @itemx --no-split | |
449 | Skip the splitting phase. Avoids spending a lot of time trying to rescue | |
450 | the most difficult parts of the file. | |
451 | ||
452 | @item -o @var{bytes} | |
453 | @itemx --output-position=@var{bytes} | |
454 | Starting position in @var{outfile}, in bytes. Defaults to | |
455 | @samp{--input-position}. The bytes below @var{bytes} aren't touched if | |
456 | they exist and truncation is not requested. Else they are set to 0. | |
457 | ||
458 | @item -p | |
459 | @itemx --preallocate | |
460 | Preallocate space on disc for @var{outfile}. Only space for regular | |
461 | files can be preallocated. If preallocation succeeds, rescue will not | |
462 | fail due to lack of free space on disc. If ddrescue can't determine the | |
463 | size to preallocate, you may need to specify it with some combination of | |
464 | the @samp{--input-position}, @samp{--output-position}, @samp{--size}, | |
465 | and @samp{--domain-logfile} options. | |
466 | ||
467 | @item -q | |
468 | @itemx --quiet | |
469 | Quiet operation. Suppress all messages. | |
470 | ||
471 | @item -r @var{n} | |
472 | @itemx --retries=@var{n} | |
473 | Exit after given number of retry passes. Defaults to 0. -1 means | |
474 | infinity. Every bad sector is tried only one time per pass. To retry bad | |
475 | sectors detected on a previous run, you must specify a non-zero number | |
476 | of retries. | |
477 | ||
478 | @item -R | |
479 | @itemx --reverse | |
480 | Reverse direction of copying, retrying, and the sequential part of | |
481 | splitting, running them backwards from the end of the input file. | |
482 | ||
483 | @item -s @var{bytes} | |
484 | @itemx --size=@var{bytes} | |
485 | Maximum size of the input data to be copied, in bytes. If ddrescue can't | |
486 | determine the size of the input device, you may need to specify it with | |
487 | this option. Note that this option specifies the size of the input data | |
488 | to be copied, not the size of the resulting @var{outfile}. So, for | |
489 | example, the following command creates an @var{outfile} 300 bytes long, | |
490 | but only writes data on the last 200 bytes: | |
491 | ||
492 | @example | |
493 | ddrescue -i 100 -s 200 infile outfile logfile | |
494 | @end example | |
495 | ||
496 | @item -S | |
497 | @itemx --sparse | |
498 | Use sparse writes for @var{outfile}. (The blocks of zeros are not | |
499 | actually allocated on disc). May save a lot of disc space in some cases. | |
500 | Not all systems support this. Only regular files can be sparse. | |
501 | ||
502 | @item -t | |
503 | @itemx --truncate | |
504 | Truncate @var{outfile} to zero size before writing to it. Only works for | |
505 | regular files, not for drives or partitions. | |
506 | ||
507 | @item -T @var{interval} | |
508 | @itemx --timeout=@var{interval} | |
509 | Maximum time since last successful read allowed before giving up. | |
510 | Defaults to infinity. @var{interval} is a rational number (like 1.5 or | |
511 | 1/2) optionally followed by one of @samp{s}, @samp{m}, @samp{h} or | |
512 | @samp{d}, meaning seconds, minutes, hours and days respectively. If no | |
513 | unit is specified, it defaults to seconds. | |
514 | ||
515 | @item -v | |
516 | @itemx --verbose | |
517 | Verbose mode. Further -v's (up to 4) increase the verbosity level. | |
518 | ||
519 | @item -w | |
520 | @itemx --ignore-write-errors | |
521 | Make fill mode ignore write errors. This is useful to avoid ddrescue | |
522 | exiting because of new errors developing while wiping the good sectors | |
523 | of a failing drive. Fill mode normally writes to @var{outfile} one | |
524 | cluster at a time. With this option, after the first write error is | |
525 | found in an area, the rest of that area is filled sector by sector. | |
526 | ||
527 | @item -x @var{bytes} | |
528 | @itemx --extend-outfile=@var{bytes} | |
529 | Extend the size of @var{outfile} to make it at least @var{bytes} long. | |
530 | If the size of @var{outfile} is already equal or longer than @var{bytes} | |
531 | then this option does nothing. Use this option to guarantee a minimum | |
532 | size for @var{outfile}. Only regular files can be extended. | |
533 | ||
534 | @end table | |
535 | ||
536 | @sp 1 | |
537 | Numbers given as arguments to options (positions, sizes, retries, etc) | |
538 | may be followed by a multiplier and an optional @samp{B} for "byte". | |
539 | ||
540 | Table of SI and binary prefixes (unit multipliers): | |
541 | ||
542 | @multitable {Prefix} {kilobyte (10^3 = 1000)} {|} {Prefix} {kibibyte (2^10 = 1024)} | |
543 | @item Prefix @tab Value @tab | @tab Prefix @tab Value | |
544 | @item @tab @tab | @tab s @tab sectors | |
545 | @item k @tab kilobyte (10^3 = 1000) @tab | @tab Ki @tab kibibyte (2^10 = 1024) | |
546 | @item M @tab megabyte (10^6) @tab | @tab Mi @tab mebibyte (2^20) | |
547 | @item G @tab gigabyte (10^9) @tab | @tab Gi @tab gibibyte (2^30) | |
548 | @item T @tab terabyte (10^12) @tab | @tab Ti @tab tebibyte (2^40) | |
549 | @item P @tab petabyte (10^15) @tab | @tab Pi @tab pebibyte (2^50) | |
550 | @item E @tab exabyte (10^18) @tab | @tab Ei @tab exbibyte (2^60) | |
551 | @item Z @tab zettabyte (10^21) @tab | @tab Zi @tab zebibyte (2^70) | |
552 | @item Y @tab yottabyte (10^24) @tab | @tab Yi @tab yobibyte (2^80) | |
553 | @end multitable | |
554 | ||
555 | @sp 1 | |
556 | Exit status: 0 for a normal exit, 1 for environmental problems (file not | |
557 | found, invalid flags, I/O errors, etc), 2 to indicate a corrupt or | |
558 | invalid input file, 3 for an internal consistency error (eg, bug) which | |
559 | caused ddrescue to panic. | |
560 | ||
561 | ||
562 | @node Logfile structure | |
563 | @chapter Logfile structure | |
564 | @cindex logfile structure | |
565 | ||
566 | The logfile is a text file easy to read and edit. It is formed by three | |
567 | parts, the heading comments, the status line, and the list of data | |
568 | blocks. Any line beginning with @samp{#} is a comment line. | |
569 | ||
570 | NOTE: Logfiles generated by a version of ddrescue prior to 1.6 lack the | |
571 | status line. If you want to use an old logfile with ddrescue 1.6 or | |
572 | later, you will have to insert a line like @samp{0 +} at the beginning | |
573 | of the logfile. | |
574 | ||
575 | The heading comments contain the version of ddrescue and the command | |
576 | line used to create the logfile. They are intended as information for | |
577 | the user. | |
578 | ||
579 | The first non-comment line is the status line. It contains a | |
580 | non-negative integer and a status character. The integer is the position | |
581 | being tried in the input file. The status character is one of these: | |
582 | ||
583 | @multitable {Character} {generating approximate logfile} | |
584 | @item Character @tab Meaning | |
585 | @item '?' @tab copying non-tried blocks | |
586 | @item '*' @tab trimming non-trimmed blocks | |
587 | @item '/' @tab splitting non-split blocks | |
588 | @item '-' @tab retrying bad sectors | |
589 | @item 'F' @tab filling specified blocks | |
590 | @item 'G' @tab generating approximate logfile | |
591 | @item '+' @tab finished | |
592 | @end multitable | |
593 | ||
594 | The blocks in the list of data blocks must be contiguous and | |
595 | non-overlapping. | |
596 | ||
597 | Every line in the list of data blocks describes a block of data. It | |
598 | contains 2 non-negative integers and a status character. The first | |
599 | integer is the starting position of the block in the input file, the | |
600 | second integer is the size (in bytes) of the block. The status character | |
601 | is one of these: | |
602 | ||
603 | @multitable {Character} {failed block bad-sector(s)} | |
604 | @item Character @tab Meaning | |
605 | @item '?' @tab non-tried block | |
606 | @item '*' @tab failed block non-trimmed | |
607 | @item '/' @tab failed block non-split | |
608 | @item '-' @tab failed block bad-sector(s) | |
609 | @item '+' @tab finished block | |
610 | @end multitable | |
611 | ||
612 | @noindent | |
613 | And here is an example logfile: | |
614 | ||
615 | @noindent | |
616 | # Rescue Logfile. Created by GNU ddrescue version @value{VERSION}@* | |
617 | # Command line: ddrescue /dev/fd0 fdimage logfile@* | |
618 | # current_pos current_status@* | |
619 | 0x00120000 ?@* | |
620 | # pos size status@* | |
621 | @multitable {0x00000000} {0x00000000} {status} | |
622 | @item 0x00000000 @tab 0x00117000 @tab + | |
623 | @item 0x00117000 @tab 0x00000200 @tab - | |
624 | @item 0x00117200 @tab 0x00001000 @tab / | |
625 | @item 0x00118200 @tab 0x00007E00 @tab * | |
626 | @item 0x00120000 @tab 0x00048000 @tab ? | |
627 | @end multitable | |
628 | ||
629 | If you edit the file, you may use decimal, hexadecimal or octal values, | |
630 | using the same syntax that integer constants in C++. | |
631 | ||
632 | ||
633 | @node Examples | |
634 | @chapter A small tutorial with examples | |
635 | @cindex examples | |
636 | ||
637 | This tutorial is for those already able to use the dd command. If you | |
638 | don't know what dd is, better search the net for some introductory | |
639 | material about dd and GNU ddrescue first. | |
640 | ||
641 | A failing drive tends to develop more and more errors as time passes. | |
642 | Because of this, you should rescue the data from a drive as soon as you | |
643 | notice the first error. Be diligent because every time a physically | |
644 | damaged drive powers up and is able to output some data, it may be the | |
645 | very last time that it ever will. | |
646 | ||
647 | You should make a copy of the failing drive with ddrescue, and then try | |
648 | to repair the copy. If your data is really important, use the first copy | |
649 | as a master for a second copy, and try to repair the second copy. If | |
650 | something goes wrong, you have the master intact to try again. | |
651 | ||
652 | If you are trying to rescue a whole partition, first repair the copy | |
653 | with e2fsck or some other tool appropiate for the type of partition you | |
654 | are trying to rescue, then mount the repaired copy somewhere and try to | |
655 | recover the files in it. | |
656 | ||
657 | If the drive is so damaged that the file system in the rescued partition | |
658 | can't be repaired or mounted, you will have to browse the rescued data | |
659 | with an hex editor and extract the desired parts by hand or use a file | |
660 | recovery tool like photorec. | |
661 | ||
662 | If the partition table is damaged, you may try to rescue the whole disc, | |
663 | then try to repair the partition table and the partitions on the copy. | |
664 | ||
665 | If the damaged drive is not listed in /dev, then you cannot rescue it. | |
666 | At least not with ddrescue. | |
667 | ||
668 | @sp 1 | |
669 | @noindent | |
670 | Example 1: Rescue a whole disc with two ext2 partitions in /dev/hda to | |
671 | /dev/hdb.@* | |
672 | Note: you do not need to partition /dev/hdb beforehand, but if the | |
673 | partition table on /dev/hda is damaged, you'll need to recreate it | |
674 | somehow on /dev/hdb. | |
675 | ||
676 | @example | |
677 | ddrescue -f -n /dev/hda /dev/hdb logfile | |
678 | ddrescue -d -f -r3 /dev/hda /dev/hdb logfile | |
679 | fdisk /dev/hdb | |
680 | e2fsck -v -f /dev/hdb1 | |
681 | e2fsck -v -f /dev/hdb2 | |
682 | @end example | |
683 | ||
684 | @sp 1 | |
685 | @noindent | |
686 | Example 2: Rescue an ext2 partition in /dev/hda2 to /dev/hdb2.@* | |
687 | Note: you need to create the hdb2 partition with fdisk first. hdb2 | |
688 | should be of appropiate type and size. | |
689 | ||
690 | @example | |
691 | ddrescue -f -n /dev/hda2 /dev/hdb2 logfile | |
692 | ddrescue -d -f -r3 /dev/hda2 /dev/hdb2 logfile | |
693 | e2fsck -v -f /dev/hdb2 | |
694 | mount -t ext2 -o ro /dev/hdb2 /mnt | |
695 | (read rescued files from /mnt) | |
696 | @end example | |
697 | ||
698 | @sp 1 | |
699 | @noindent | |
700 | Example 3: Rescue a CD-ROM in /dev/cdrom. | |
701 | ||
702 | @example | |
703 | ddrescue -n -b2048 /dev/cdrom cdimage logfile | |
704 | ddrescue -d -b2048 /dev/cdrom cdimage logfile | |
705 | (write cdimage to a blank CD-ROM) | |
706 | @end example | |
707 | ||
708 | @sp 1 | |
709 | @noindent | |
710 | Example 4: Rescue a CD-ROM in /dev/cdrom from two copies. | |
711 | ||
712 | @example | |
713 | ddrescue -n -b2048 /dev/cdrom cdimage logfile | |
714 | ddrescue -d -b2048 /dev/cdrom cdimage logfile | |
715 | (insert second copy in the CD drive) | |
716 | ddrescue -d -r1 -b2048 /dev/cdrom cdimage logfile | |
717 | (write cdimage to a blank CD-ROM) | |
718 | @end example | |
719 | ||
720 | @sp 1 | |
721 | @anchor{lziprecover-example} | |
722 | @noindent | |
723 | Example 5: Rescue a lzip compressed backup from two copies on CD-ROM | |
724 | with error-checked merging of copies | |
725 | @ifnothtml | |
726 | (@xref{Top,lziprecover manual,,lziprecover}, | |
727 | @end ifnothtml | |
728 | @ifhtml | |
729 | (See the | |
730 | @uref{http://www.nongnu.org/lzip/manual/lziprecover_manual.html,,lziprecover manual} | |
731 | @end ifhtml | |
732 | for details about lziprecover). | |
733 | ||
734 | @example | |
735 | ddrescue -b2048 /dev/cdrom cdimage1 logfile1 | |
736 | mount -t iso9660 -o loop,ro cdimage1 /mnt/cdimage | |
737 | cp /mnt/cdimage/backup.tar.lz rescued1.tar.lz | |
738 | umount /mnt/cdimage | |
739 | (insert second copy in the CD drive) | |
740 | ddrescue -b2048 /dev/cdrom cdimage2 logfile2 | |
741 | mount -t iso9660 -o loop,ro cdimage2 /mnt/cdimage | |
742 | cp /mnt/cdimage/backup.tar.lz rescued2.tar.lz | |
743 | umount /mnt/cdimage | |
744 | lziprecover -m -v -o backup.tar.lz rescued1.tar.lz rescued2.tar.lz | |
745 | @end example | |
746 | ||
747 | @sp 1 | |
748 | @noindent | |
749 | Example 6: While rescuing the whole drive /dev/hda to /dev/hdb, /dev/hda | |
750 | freezes up at position 12345678. | |
751 | ||
752 | @example | |
753 | ddrescue -f /dev/hda /dev/hdb logfile <-- /dev/hda freezes here | |
754 | (restart /dev/hda or reboot computer) | |
755 | (restart copy at a safe distance from the troubled sector) | |
756 | ddrescue -f -i 12350000 /dev/hda /dev/hdb logfile | |
757 | (copy backwards down to the troubled sector) | |
758 | ddrescue -f -R /dev/hda /dev/hdb logfile | |
759 | @end example | |
760 | ||
761 | @sp 1 | |
762 | @noindent | |
763 | Example 7: While rescuing the whole drive /dev/hda to /dev/hdb, /dev/hdb | |
764 | fails and you have to rescue data to a third drive, /dev/hdc. | |
765 | ||
766 | @example | |
767 | ddrescue -f -n /dev/hda /dev/hdb logfile1 <-- /dev/hdb fails here | |
768 | ddrescue -f -m logfile1 /dev/hdb /dev/hdc logfile2 | |
769 | ddrescue -f -n /dev/hda /dev/hdc logfile2 | |
770 | ddrescue -d -f -r3 /dev/hda /dev/hdc logfile2 | |
771 | @end example | |
772 | ||
773 | @sp 1 | |
774 | @noindent | |
775 | Example 8: While rescuing the whole drive /dev/hda to /dev/hdb, /dev/hda | |
776 | stops responding and disappears from /dev. | |
777 | ||
778 | @example | |
779 | ddrescue -f -n /dev/hda /dev/hdb logfile <-- /dev/hda fails here | |
780 | (restart /dev/hda or reboot computer as many times as needed) | |
781 | ddrescue -f -n -A /dev/hda /dev/hdb logfile | |
782 | ddrescue -d -f -r3 /dev/hda /dev/hdb logfile | |
783 | @end example | |
784 | ||
785 | ||
786 | @node Direct disc access | |
787 | @chapter Direct disc access | |
788 | @cindex direct disc access | |
789 | @cindex raw devices | |
790 | ||
791 | If you notice that the positions and sizes in the log file are ALWAYS | |
792 | multiples of the sector size, maybe your kernel is caching the disc | |
793 | accesses and grouping them. In this case you may want to use direct disc | |
794 | access or a raw device to bypass the kernel cache and rescue more of | |
795 | your data. | |
796 | ||
797 | NOTE! Sector size must be correctly set with the @samp{--sector-size} | |
798 | option for this to work. Only whole sectors can be read; both | |
799 | @samp{--input-position} and @samp{--size} must be a multiple of sector | |
800 | size. | |
801 | ||
802 | Try the @samp{--direct} option first. If direct disc access is not | |
803 | available in your system, try raw devices. Read your system | |
804 | documentation to find how to bind a raw device to a regular block | |
805 | device. Some OSs provide raw access through special device names, like | |
806 | /dev/rdisk. | |
807 | ||
808 | Ddrescue aligns its I/O buffer to the sector size so that it can be used | |
809 | for direct disc access or to read from raw devices. For efficiency | |
810 | reasons, also aligns it to the memory page size if page size is a | |
811 | multiple of sector size. On some systems, ddrescue can't determine the | |
812 | size of a raw device, so an explicit @samp{--size} or | |
813 | @samp{--complete-only} option may be needed. | |
814 | ||
815 | Using direct disc access, or reading from a raw device, may be slower or | |
816 | faster than normal cached reading depending on your OS and hardware. In | |
817 | case it is slower you may want to make a first pass using normal cached | |
818 | reads and use direct disc access, or a raw device, only to recover the | |
819 | good sectors inside the failed blocks. | |
820 | ||
821 | @sp 1 | |
822 | @noindent | |
823 | Example 1: using direct disc access. | |
824 | ||
825 | @example | |
826 | ddrescue -f -n /dev/hdb1 /dev/hdc1 logfile | |
827 | ddrescue -d -f -r3 /dev/hdb1 /dev/hdc1 logfile | |
828 | e2fsck -v -f /dev/hdc1 | |
829 | mount -t ext2 -o ro /dev/hdc1 /mnt | |
830 | @end example | |
831 | ||
832 | @sp 1 | |
833 | @noindent | |
834 | Example 2: using a raw device. | |
835 | ||
836 | @example | |
837 | raw /dev/raw/raw1 /dev/hdb1 | |
838 | ddrescue -f -n /dev/hdb1 /dev/hdc1 logfile | |
839 | ddrescue -C -f -r3 /dev/raw/raw1 /dev/hdc1 logfile | |
840 | raw /dev/raw/raw1 0 0 | |
841 | e2fsck -v -f /dev/hdc1 | |
842 | mount -t ext2 -o ro /dev/hdc1 /mnt | |
843 | @end example | |
844 | ||
845 | ||
846 | @node Fill mode | |
847 | @chapter Fill mode | |
848 | @cindex fill Mode | |
849 | ||
850 | When ddrescue is invoked with the @samp{--fill-mode} option it operates | |
851 | in "fill mode", which is different from the default "rescue mode". That | |
852 | is, if you use the @samp{--fill-mode} option, ddrescue does not rescue | |
853 | anything. It only fills with data read from the input file the blocks of | |
854 | the output file whose status character from the logfile coincides with | |
855 | one of the type characters specified as argument to the | |
856 | @samp{--fill-mode} option. | |
857 | ||
858 | In fill mode the input file may have any size. If it is too small, the | |
859 | data will be duplicated as many times as necessary to fill the input | |
860 | buffer. If it is too big, only the data needed to fill the input buffer | |
861 | will be read. Then the same data will be written to every cluster or | |
862 | sector to be filled. | |
863 | ||
864 | Note that in fill mode the input file is always read from position 0. If | |
865 | you specify a @samp{--input-position}, it refers to the original input | |
866 | file from which the logfile was built, and is only used to calculate the | |
867 | offset between input and output positions. | |
868 | ||
869 | Note also that when filling the input file of the original rescue run | |
870 | you should set @samp{--input-position} and @samp{--output-position} to | |
871 | identical values, whereas when filling the output file of the original | |
872 | rescue run you should keep the original offset between | |
873 | @samp{--input-position} and @samp{--output-position}. | |
874 | ||
875 | The @samp{--fill-mode} option implies the @samp{--complete-only} option. | |
876 | ||
877 | In fill mode the logfile is updated to allow resumability when | |
878 | interrupted or in case of a crash, but as nothing is being rescued the | |
879 | logfile is not destroyed. The status line is the only part of the | |
880 | logfile that is modified. | |
881 | ||
882 | @sp 1 | |
883 | @noindent | |
884 | The fill mode has a number of uses. See the following examples: | |
885 | ||
886 | @noindent | |
887 | Example 1: Mark parts of the rescued copy to allow finding them when | |
888 | examined in an hex editor. For example, the following command line fills | |
889 | all blocks marked as @samp{-} (bad-sector) with copies of the string | |
890 | @w{@samp{BAD SECTOR }}: | |
891 | ||
892 | @example | |
893 | printf "BAD SECTOR " > tmpfile | |
894 | ddrescue --fill-mode=- tmpfile outfile logfile | |
895 | @end example | |
896 | ||
897 | @noindent | |
898 | Example 2: Wipe only the good sectors, leaving the bad sectors alone. | |
899 | This way, the drive will still test bad (i.e., with unreadable sectors). | |
900 | This is the fastest way of wiping a failing drive, and is specially | |
901 | useful when sending the drive back to the manufacturer for warranty | |
902 | replacement. | |
903 | ||
904 | @example | |
905 | ddrescue --fill-mode=+ --force /dev/zero bad_drive logfile | |
906 | @end example | |
907 | ||
908 | @noindent | |
909 | Example 3: Force the drive to remap the bad sectors, making it usable | |
910 | again. If the drive has only a few bad sectors, and they are not caused | |
911 | by drive age, you can probably just rewrite those sectors, and the drive | |
912 | will reallocate them automatically to new "spare" sectors that it keeps | |
913 | for just this purpose. WARNING! This may not work on your drive. | |
914 | ||
915 | @example | |
916 | ddrescue --fill-mode=- --force --synchronous /dev/zero bad_drive logfile | |
917 | @end example | |
918 | ||
919 | @sp 1 | |
920 | @noindent | |
921 | Fill mode can also help you to figure out, independently of the file | |
922 | system used, what files are partially or entirely in the bad areas of | |
923 | the disc. Just follow these steps: | |
924 | ||
925 | 1) Copy the damaged drive with ddrescue until finished. Do not use | |
926 | sparse writes. This yields a logfile with only finished (@samp{+}) and | |
927 | bad-sector (@samp{-}) blocks. | |
928 | ||
929 | 2) Fill the bad-sector blocks of the copied drive or image file with a | |
930 | string not present in any file, for example "DEADBEEF". | |
931 | ||
932 | 3) Mount the copied drive (or the image file, via loopback device). | |
933 | ||
934 | 4) Grep for the fill string in all the files. Those files containing the | |
935 | string reside (at least partially) in damaged disc areas. | |
936 | ||
937 | 5) Unmount the copied drive or image file. | |
938 | ||
939 | 6) Optionally fill the bad-sector blocks of the copied drive or image | |
940 | file with zeros to restore the disc image. | |
941 | ||
942 | @noindent | |
943 | Example 4: Figure out what files are in the bad areas of the disc. | |
944 | ||
945 | @example | |
946 | ddrescue -b2048 /dev/cdrom cdimage logfile | |
947 | printf "DEADBEEF" > tmpfile | |
948 | ddrescue --fill-mode=- tmpfile cdimage logfile | |
949 | rm tmpfile | |
950 | mount -t iso9660 -o loop,ro cdimage /mnt/cdimage | |
951 | find /mnt/cdimage -type f -exec grep "DEADBEEF" '@{@}' ';' | |
952 | umount /mnt/cdimage | |
953 | ddrescue --fill-mode=- /dev/zero cdimage logfile | |
954 | @end example | |
955 | ||
956 | ||
957 | @node Generate mode | |
958 | @chapter Generate mode | |
959 | @cindex generate Mode | |
960 | ||
961 | NOTE: When ddrescue is invoked with the @samp{--generate-mode} option it | |
962 | operates in "generate mode", which is different from the default "rescue | |
963 | mode". That is, if you use the @samp{--generate-mode} option, ddrescue | |
964 | does not rescue anything. It only tries to generate a logfile for later | |
965 | use. | |
966 | ||
967 | So you didn't read the tutorial and started ddrescue without a logfile. | |
968 | Now, two days later, your computer crashed and you can't know how much | |
969 | data ddrescue managed to save. And even worse, you can't resume the | |
970 | rescue; you have to restart it from the very beginning. | |
971 | ||
972 | Or maybe you started copying a drive with @w{@code{dd | |
973 | conv=noerror,sync}} and are now in the same situation described above. | |
974 | In this case, note that you can't use a copy made by dd unless it was | |
975 | invoked with the @samp{sync} conversion argument. | |
976 | ||
977 | Don't despair (yet). Ddrescue can in some cases generate an approximate | |
978 | logfile, from the input file and the (partial) copy, that is almost as | |
979 | good as an exact logfile. It makes this by simply assuming that sectors | |
980 | containing all zeros were not rescued. | |
981 | ||
982 | However, if the destination of the copy was a drive or a partition, (or | |
983 | an existing regular file and truncation was not requested), most | |
984 | probably you will need to restart ddrescue from the very beginning. | |
985 | (This time with a logfile, of course). The reason is that old data may | |
986 | be present in the drive that have not been overwritten yet, and may be | |
987 | thus non-tried but non-zero. | |
988 | ||
989 | For example, if you first tried one of these commands: | |
990 | @example | |
991 | ddrescue infile outfile | |
992 | or | |
993 | dd if=infile of=outfile conv=noerror,sync | |
994 | @end example | |
995 | ||
996 | then you can generate an approximate logfile with this command: | |
997 | @example | |
998 | ddrescue --generate-mode infile outfile logfile | |
999 | @end example | |
1000 | ||
1001 | @noindent | |
1002 | Note that you must keep the original offset between | |
1003 | @samp{--input-position} and @samp{--output-position} of the original | |
1004 | rescue run. | |
1005 | ||
1006 | ||
1007 | @node Ddrescuelog | |
1008 | @chapter Ddrescuelog | |
1009 | @cindex ddrescuelog | |
1010 | ||
1011 | Ddrescuelog is a tool that manipulates ddrescue logfiles, shows logfile | |
1012 | contents, converts logfiles to/from other formats, compares logfiles, | |
1013 | tests rescue status, and can delete a logfile if the rescue is done. | |
1014 | Ddrescuelog operations can be restricted to one or several parts of the | |
1015 | logfile if the domain setting options are used. | |
1016 | ||
1017 | Here are some examples of how to use ddrescuelog, alone or in | |
1018 | combination with other tools. | |
1019 | ||
1020 | @sp 1 | |
1021 | @noindent | |
1022 | Example 1: Delete the logfile if the rescue is finished (all data is | |
1023 | recovered without errors left). | |
1024 | ||
1025 | @example | |
1026 | ddrescue -f /dev/hda /dev/hdb logfile | |
1027 | ddrescuelog -d logfile | |
1028 | @end example | |
1029 | ||
1030 | @sp 1 | |
1031 | @noindent | |
1032 | Example 2: Rescue two ext2 partitions in /dev/hda to | |
1033 | /dev/hdb and repair the file systems using badblock lists generated with | |
1034 | ddrescuelog. File system block size is 4096.@* | |
1035 | Note: you do need to partition /dev/hdb beforehand. | |
1036 | ||
1037 | @example | |
1038 | fdisk /dev/hdb <-- partition /deb/hdb | |
1039 | ddrescue -f /dev/hda1 /dev/hdb1 logfile1 | |
1040 | ddrescue -f /dev/hda2 /dev/hdb2 logfile2 | |
1041 | ddrescuelog -l- -b4096 logfile1 > badblocks1 | |
1042 | ddrescuelog -l- -b4096 logfile2 > badblocks2 | |
1043 | e2fsck -v -f -L badblocks1 /dev/hdb1 | |
1044 | e2fsck -v -f -L badblocks2 /dev/hdb2 | |
1045 | @end example | |
1046 | ||
1047 | @sp 1 | |
1048 | @noindent | |
1049 | Example 3: Rescue a whole disc with two ext2 partitions in /dev/hda to | |
1050 | /dev/hdb and repair the file systems using badblock lists generated with | |
1051 | ddrescuelog. Disc sector size is 512, file system block size is 4096. | |
1052 | Arguments to options @samp{-i} and @samp{-s} are the starting positions | |
1053 | and sizes of the partitions being rescued.@* | |
1054 | Note: you do not need to partition /dev/hdb beforehand, but if the | |
1055 | partition table on /dev/hda is damaged, you'll need to recreate it | |
1056 | somehow on /dev/hdb. | |
1057 | ||
1058 | @example | |
1059 | ddrescue -f /dev/hda /dev/hdb logfile | |
1060 | fdisk /dev/hdb <-- get partition sizes | |
1061 | ddrescuelog -l- -b512 -i63b -o0 -s9767457b -b4096 logfile > badblocks1 | |
1062 | ddrescuelog -l- -b512 -i9767520b -o0 -s128520b -b4096 logfile > badblocks2 | |
1063 | e2fsck -v -f -L badblocks1 /dev/hdb1 | |
1064 | e2fsck -v -f -L badblocks2 /dev/hdb2 | |
1065 | @end example | |
1066 | ||
1067 | ||
1068 | @node Invoking ddrescuelog | |
1069 | @chapter Invoking ddrescuelog | |
1070 | @cindex invoking ddrescuelog | |
1071 | ||
1072 | The format for running ddrescuelog is: | |
1073 | ||
1074 | @example | |
1075 | ddrescuelog [@var{options}] @var{logfile} | |
1076 | @end example | |
1077 | ||
1078 | Ddrescuelog supports the following options: | |
1079 | ||
1080 | @table @samp | |
1081 | @item -h | |
1082 | @itemx --help | |
1083 | Print an informative help message describing the options and exit. | |
1084 | ||
1085 | @item -V | |
1086 | @itemx --version | |
1087 | Print the version number of ddrescuelog on the standard output and exit. | |
1088 | ||
1089 | @item -a @var{old_types},@var{new_types} | |
1090 | @itemx --change-types=@var{old_types},@var{new_types} | |
1091 | Change the status of every block in the rescue domain from one type in | |
1092 | @var{old_types} to the corresponding type in @var{new_types}, much like | |
1093 | the @samp{tr} command does, and write the resulting logfile to standard | |
1094 | output. @var{old_types} and @var{new_types} are strings of block status | |
1095 | characters as defined in the chapter Logfile structure (@pxref{Logfile | |
1096 | structure}). Blocks whose status is not in @var{old_types} are left | |
1097 | unchanged. If @var{new_types} is shorter than @var{old_types} the last | |
1098 | type of @var{new_types} is repeated as many times as necessary. | |
1099 | ||
1100 | @item -b @var{bytes} | |
1101 | @itemx --block-size=@var{bytes} | |
1102 | Block size used by ddrescuelog. Depending on the requested operation it | |
1103 | may be the sector size of the input device, the block size of the | |
1104 | rescued file system, etc. Defaults to 512. | |
1105 | ||
1106 | @item -c[@var{type1}@var{type2}] | |
1107 | @itemx --create-logfile[=@var{type1}@var{type2}] | |
1108 | Create a logfile from a list of block numbers read from standard input. | |
1109 | Only blocks included in the rescue domain will be added to @var{logfile}. | |
1110 | ||
1111 | @var{type1} and @var{type2} are block status characters as defined in | |
1112 | the chapter Logfile structure (@pxref{Logfile structure}). @var{type1} | |
1113 | sets the type for blocks included in the list, while @var{type2} sets | |
1114 | the type for the rest of the logfile. If not specified, @var{type1} | |
1115 | defaults to @samp{+} and @var{type2} defaults to @samp{-}. | |
1116 | ||
1117 | @item -d | |
1118 | @itemx --delete-if-done | |
1119 | Delete the given @var{logfile} if all the blocks in the rescue domain | |
1120 | have been successfuly recovered. The exit status is 0 if @var{logfile} | |
1121 | could be deleted, 1 otherwise. | |
1122 | ||
1123 | @item -D | |
1124 | @itemx --done-status | |
1125 | Test if all the blocks in the rescue domain have been successfuly | |
1126 | recovered. The exit status is 0 if all tested blocks are finished, 1 | |
1127 | otherwise. | |
1128 | ||
1129 | @item -f | |
1130 | @itemx --force | |
1131 | Force overwrite of @var{logfile}. | |
1132 | ||
1133 | @item -i @var{bytes} | |
1134 | @itemx --input-position=@var{bytes} | |
1135 | Starting position of the rescue domain, in bytes. Defaults to 0. It | |
1136 | refers to a position in the original input file. | |
1137 | ||
1138 | @item -l @var{types} | |
1139 | @itemx --list-blocks=@var{types} | |
1140 | Print on standard output the block numbers of the blocks specified as | |
1141 | any of @var{types} in @var{logfile} and included in the rescue domain. | |
1142 | @var{types} contains one or more of the block status characters defined | |
1143 | in the chapter Logfile structure (@pxref{Logfile structure}). | |
1144 | ||
1145 | The list format is one block number per line in decimal, like the output | |
1146 | of the badblocks program, so that it can be used as input for e2fsck or | |
1147 | other similar filesystem repairing tool. | |
1148 | ||
1149 | @item -m @var{file} | |
1150 | @itemx --domain-logfile=@var{file} | |
1151 | Restrict the rescue domain to the blocks marked as finished in the | |
1152 | logfile @var{file}. | |
1153 | ||
1154 | @item -n | |
1155 | @itemx --invert-logfile | |
1156 | Invert the types of the blocks in @var{logfile} which are included in | |
1157 | the rescue domain, and write the resulting logfile to standard output. | |
1158 | Finished blocks (@samp{+}) are changed to bad-sector (@samp{-}), all | |
1159 | other types are changed to finished. @samp{--invert-logfile} is | |
1160 | equivalent to @samp{--change-types=?*/-+,++++-} | |
1161 | ||
1162 | @item -o @var{bytes} | |
1163 | @itemx --output-position=@var{bytes} | |
1164 | Starting position in output file, in bytes. Is used by the | |
1165 | @samp{--list-blocks} option. Defaults to @samp{--input-position}. | |
1166 | ||
1167 | @item -p @var{file} | |
1168 | @itemx --compare-logfile=@var{file} | |
1169 | Compare the types of the blocks included in the rescue domain. The exit | |
1170 | status is 0 if all tested blocks are the same in both files, 1 | |
1171 | otherwise. | |
1172 | ||
1173 | @item -q | |
1174 | @itemx --quiet | |
1175 | Quiet operation. Suppress all messages. | |
1176 | ||
1177 | @item -s @var{bytes} | |
1178 | @itemx --size=@var{bytes} | |
1179 | Maximum size of the rescue domain, in bytes. It refers to a size in the | |
1180 | original input file. | |
1181 | ||
1182 | @item -t | |
1183 | @itemx --show-status | |
1184 | Print a summary of @var{logfile} contents on the standard output. The | |
1185 | summary can be restricted to one or several parts of @var{logfile} if | |
1186 | the domain setting options are used. | |
1187 | ||
1188 | @item -v | |
1189 | @itemx --verbose | |
1190 | Verbose mode. Further -v's (up to 4) increase the verbosity level. | |
1191 | ||
1192 | @item -x @var{file} | |
1193 | @itemx --xor-logfile=@var{file} | |
1194 | Perform a logical XOR (exclusive OR) operation between the finished | |
1195 | blocks in @var{file} and those in @var{logfile}, and write the resulting | |
1196 | logfile to standard output. In other words, in the resulting logfile a | |
1197 | block is only shown as finished if it was finished in either of the two | |
1198 | input logfiles but not in both. | |
1199 | ||
1200 | @item -y @var{file} | |
1201 | @itemx --and-logfile=@var{file} | |
1202 | Perform a logical AND operation between the finished blocks in | |
1203 | @var{file} and those in @var{logfile}, and write the resulting logfile | |
1204 | to standard output. In other words, in the resulting logfile a block is | |
1205 | only shown as finished if it was finished in both input logfiles. | |
1206 | ||
1207 | @item -z @var{file} | |
1208 | @itemx --or-logfile=@var{file} | |
1209 | Perform a logical OR operation between the finished blocks in @var{file} | |
1210 | and those in @var{logfile}, and write the resulting logfile to standard | |
1211 | output. In other words, in the resulting logfile a block is shown as | |
1212 | finished if it was finished in either of the two input logfiles. | |
1213 | ||
1214 | @end table | |
1215 | ||
1216 | Exit status: 0 for a normal exit, 1 for environmental problems (file not | |
1217 | found, invalid flags, I/O errors, etc), 2 to indicate a corrupt or | |
1218 | invalid input file, 3 for an internal consistency error (eg, bug) which | |
1219 | caused ddrescuelog to panic. | |
1220 | ||
1221 | ||
1222 | @node Problems | |
1223 | @chapter Reporting Bugs | |
1224 | @cindex bugs | |
1225 | @cindex getting help | |
1226 | ||
1227 | There are probably bugs in ddrescue. There are certainly errors and | |
1228 | omissions in this manual. If you report them, they will get fixed. If | |
1229 | you don't, no one will ever know about them and they will remain unfixed | |
1230 | for all eternity, if not longer. | |
1231 | ||
1232 | If you find a bug in GNU ddrescue, please send electronic mail to | |
1233 | @email{bug-ddrescue@@gnu.org}. Include the version number, which you can | |
1234 | find by running @w{@samp{ddrescue --version}}. | |
1235 | ||
1236 | ||
1237 | @node Concept index | |
1238 | @unnumbered Concept index | |
1239 | ||
1240 | @printindex cp | |
1241 | ||
1242 | @bye |
0 | 0 | .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.37.1. |
1 | .TH DDRESCUELOG "1" "July 2013" "ddrescuelog 1.17" "User Commands" | |
1 | .TH DDRESCUELOG "1" "June 2014" "ddrescuelog 1.18.1" "User Commands" | |
2 | 2 | .SH NAME |
3 | 3 | ddrescuelog \- tool for ddrescue logfiles |
4 | 4 | .SH SYNOPSIS |
17 | 17 | output version information and exit |
18 | 18 | .TP |
19 | 19 | \fB\-a\fR, \fB\-\-change\-types=\fR<ot>,<nt> |
20 | change the block types of a logfile | |
20 | change the block types of logfile | |
21 | 21 | .TP |
22 | 22 | \fB\-b\fR, \fB\-\-block\-size=\fR<bytes> |
23 | 23 | block size in bytes [default 512] |
24 | 24 | .TP |
25 | \fB\-B\fR, \fB\-\-binary\-prefixes\fR | |
26 | show binary multipliers in numbers [SI] | |
27 | .TP | |
25 | 28 | \fB\-c\fR, \fB\-\-create\-logfile[=\fR<tt>] |
26 | 29 | create logfile from list of blocks [+\-] |
30 | .TP | |
31 | \fB\-C\fR, \fB\-\-complete\-logfile[=\fR<t>] | |
32 | complete logfile adding blocks of type t [?] | |
27 | 33 | .TP |
28 | 34 | \fB\-d\fR, \fB\-\-delete\-if\-done\fR |
29 | 35 | delete the logfile if rescue is finished |
40 | 46 | \fB\-l\fR, \fB\-\-list\-blocks=\fR<types> |
41 | 47 | print block numbers of given types (?*/\-+) |
42 | 48 | .TP |
49 | \fB\-L\fR, \fB\-\-loose\-domain\fR | |
50 | accept an incomplete domain logfile | |
51 | .TP | |
43 | 52 | \fB\-m\fR, \fB\-\-domain\-logfile=\fR<file> |
44 | 53 | restrict domain to finished blocks in file |
45 | 54 | .TP |
46 | 55 | \fB\-n\fR, \fB\-\-invert\-logfile\fR |
47 | invert block types (finished <\-> others) | |
56 | invert block types (finished <\-\-> others) | |
48 | 57 | .TP |
49 | 58 | \fB\-o\fR, \fB\-\-output\-position=\fR<bytes> |
50 | 59 | starting position in output file [ipos] |
51 | 60 | .TP |
52 | 61 | \fB\-p\fR, \fB\-\-compare\-logfile=\fR<file> |
53 | 62 | compare block types in domain of both files |
63 | .TP | |
64 | \fB\-P\fR, \fB\-\-compare\-as\-domain=\fR<file> | |
65 | like \fB\-p\fR but compare finished blocks only | |
54 | 66 | .TP |
55 | 67 | \fB\-q\fR, \fB\-\-quiet\fR |
56 | 68 | suppress all messages |
73 | 85 | \fB\-z\fR, \fB\-\-or\-logfile=\fR<file> |
74 | 86 | OR the finished blocks in file with logfile |
75 | 87 | .PP |
76 | Numbers may be followed by a multiplier: s = sectors, k = kB = 10^3 = 1000, | |
77 | Ki = KiB = 2^10 = 1024, M = 10^6, Mi = 2^20, G = 10^9, Gi = 2^30, etc... | |
88 | Numbers may be in decimal, hexadecimal or octal, and may be followed by a | |
89 | multiplier: s = sectors, k = 1000, Ki = 1024, M = 10^6, Mi = 2^20, etc... | |
78 | 90 | .PP |
79 | 91 | Exit status: 0 for a normal exit, 1 for environmental problems (file |
80 | 92 | not found, invalid flags, I/O errors, etc), 2 to indicate a corrupt or |
87 | 99 | .br |
88 | 100 | General help using GNU software: http://www.gnu.org/gethelp |
89 | 101 | .SH COPYRIGHT |
90 | Copyright \(co 2013 Antonio Diaz Diaz. | |
102 | Copyright \(co 2014 Antonio Diaz Diaz. | |
91 | 103 | License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> |
92 | 104 | .br |
93 | 105 | This is free software: you are free to change and redistribute it. |
0 | 0 | /* GNU ddrescue - Data recovery tool |
1 | 1 | Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, |
2 | 2013 Antonio Diaz Diaz. | |
2 | 2013, 2014 Antonio Diaz Diaz. | |
3 | 3 | |
4 | 4 | This program is free software: you can redistribute it and/or modify |
5 | 5 | it under the terms of the GNU General Public License as published by |
17 | 17 | |
18 | 18 | #define _FILE_OFFSET_BITS 64 |
19 | 19 | |
20 | #include <algorithm> | |
20 | 21 | #include <climits> |
21 | 22 | #include <cstdio> |
23 | #include <cstring> | |
24 | #include <ctime> | |
22 | 25 | #include <string> |
23 | 26 | #include <vector> |
24 | 27 | #include <stdint.h> |
31 | 34 | // |
32 | 35 | int Fillbook::fill_areas( const std::string & filltypes ) |
33 | 36 | { |
37 | const char * const msg = "Filling blocks..."; | |
34 | 38 | bool first_post = true; |
35 | 39 | |
36 | 40 | for( int index = 0; index < sblocks(); ++index ) |
42 | 46 | Block b( sb.pos(), softbs() ); // fill the area a softbs at a time |
43 | 47 | if( sb.includes( current_pos() ) ) b.pos( current_pos() ); |
44 | 48 | if( b.end() > sb.end() ) b.crop( sb ); |
45 | current_status( filling ); | |
49 | current_status( filling, msg ); | |
46 | 50 | while( b.size() > 0 ) |
47 | 51 | { |
48 | 52 | current_pos( b.pos() ); |
49 | 53 | if( verbosity >= 0 ) |
50 | { show_status( b.pos(), first_post ); first_post = false; } | |
54 | { show_status( b.pos(), msg, first_post ); first_post = false; } | |
51 | 55 | if( interrupted() ) return -1; |
52 | 56 | const int retval = fill_block( b ); |
53 | 57 | if( retval ) // write error |
63 | 67 | ++filled_areas; --remaining_areas; |
64 | 68 | } |
65 | 69 | return 0; |
70 | } | |
71 | ||
72 | ||
73 | void Fillbook::show_status( const long long ipos, const char * const msg, | |
74 | bool force ) | |
75 | { | |
76 | const char * const up = "\x1b[A"; | |
77 | if( t0 == 0 ) | |
78 | { | |
79 | t0 = t1 = initial_time(); | |
80 | first_size = last_size = filled_size; | |
81 | force = true; | |
82 | std::printf( "\n\n\n" ); | |
83 | } | |
84 | ||
85 | if( ipos >= 0 ) last_ipos = ipos; | |
86 | const long t2 = std::time( 0 ); | |
87 | if( t2 > t1 || force ) | |
88 | { | |
89 | if( t2 > t1 ) | |
90 | { | |
91 | a_rate = ( filled_size - first_size ) / ( t2 - t0 ); | |
92 | c_rate = ( filled_size - last_size ) / ( t2 - t1 ); | |
93 | t1 = t2; | |
94 | last_size = filled_size; | |
95 | } | |
96 | std::printf( "\r%s%s%s", up, up, up ); | |
97 | std::printf( "filled size: %10sB, filled areas: %6u, current rate: %9sB/s\n", | |
98 | format_num( filled_size ), filled_areas, | |
99 | format_num( c_rate, 99999 ) ); | |
100 | std::printf( "remain size: %10sB, remain areas: %6u, average rate: %9sB/s\n", | |
101 | format_num( remaining_size ), remaining_areas, | |
102 | format_num( a_rate, 99999 ) ); | |
103 | std::printf( "current pos: %10sB, run time: %9s\n", | |
104 | format_num( last_ipos + offset() ), format_time( t1 - t0 ) ); | |
105 | if( msg && msg[0] ) | |
106 | { | |
107 | const int len = std::strlen( msg ); std::printf( "\r%s", msg ); | |
108 | for( int i = len; i < oldlen; ++i ) std::fputc( ' ', stdout ); | |
109 | oldlen = len; | |
110 | } | |
111 | std::fflush( stdout ); | |
112 | } | |
113 | else if( t2 < t1 ) // clock jumped back | |
114 | { | |
115 | t0 -= std::min( t0, t1 - t2 ); | |
116 | t1 = t2; | |
117 | } | |
66 | 118 | } |
67 | 119 | |
68 | 120 | |
96 | 148 | if( logfile_exists() ) |
97 | 149 | { |
98 | 150 | std::printf( "Initial status (read from logfile)\n" ); |
99 | std::printf( "filled size: %10sB,", format_num( filled_size ) ); | |
100 | std::printf( " filled areas: %7u\n", filled_areas ); | |
101 | std::printf( "remaining size: %10sB,", format_num( remaining_size ) ); | |
102 | std::printf( " remaining areas: %7u\n", remaining_areas ); | |
151 | std::printf( "filled size: %10sB, filled areas: %7u\n", | |
152 | format_num( filled_size ), filled_areas ); | |
153 | std::printf( "remaining size: %10sB, remaining areas: %7u\n", | |
154 | format_num( remaining_size ), remaining_areas ); | |
103 | 155 | std::printf( "Current status\n" ); |
104 | 156 | } |
105 | 157 | } |
106 | 158 | int retval = fill_areas( filltypes ); |
159 | const bool signaled = ( retval == -1 ); | |
160 | if( signaled ) retval = 0; | |
107 | 161 | if( verbosity >= 0 ) |
108 | 162 | { |
109 | show_status( -1, true ); | |
110 | if( retval == 0 ) std::printf( "Finished" ); | |
111 | else if( retval == -2 ) std::printf( "\nLogfile error" ); | |
112 | else if( retval < 0 ) std::printf( "\nInterrupted by user" ); | |
163 | show_status( -1, ( retval || signaled ) ? 0 : "Finished", true ); | |
164 | if( retval == -2 ) std::printf( "\nLogfile error" ); | |
165 | else if( signaled ) std::printf( "\nInterrupted by user" ); | |
113 | 166 | std::fputc( '\n', stdout ); |
114 | 167 | } |
115 | 168 | if( retval == -2 ) retval = 1; // logfile error |
116 | 169 | else |
117 | 170 | { |
118 | if( retval == 0 ) current_status( finished ); | |
119 | else if( retval < 0 ) retval = 0; // interrupted by user | |
171 | if( retval == 0 && !signaled ) current_status( finished ); | |
120 | 172 | compact_sblock_vector(); |
121 | 173 | if( !update_logfile( odes_, true ) && retval == 0 ) retval = 1; |
122 | 174 | } |
123 | 175 | if( final_msg() ) show_error( final_msg(), final_errno() ); |
124 | return retval; | |
176 | if( retval ) return retval; // errors have priority over signals | |
177 | if( signaled ) return signaled_exit(); | |
178 | return 0; | |
125 | 179 | } |
0 | 0 | /* GNU ddrescue - Data recovery tool |
1 | 1 | Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, |
2 | 2013 Antonio Diaz Diaz. | |
2 | 2013, 2014 Antonio Diaz Diaz. | |
3 | 3 | |
4 | 4 | This program is free software: you can redistribute it and/or modify |
5 | 5 | it under the terms of the GNU General Public License as published by |
17 | 17 | |
18 | 18 | #define _FILE_OFFSET_BITS 64 |
19 | 19 | |
20 | #include <algorithm> | |
20 | 21 | #include <cerrno> |
21 | 22 | #include <climits> |
22 | 23 | #include <cstdio> |
23 | 24 | #include <cstring> |
25 | #include <ctime> | |
24 | 26 | #include <string> |
25 | 27 | #include <vector> |
26 | 28 | #include <stdint.h> |
34 | 36 | // |
35 | 37 | int Genbook::check_all() |
36 | 38 | { |
37 | long long pos = ( ( offset() >= 0 ) ? 0 : -offset() ); | |
39 | const char * const msg = "Generating logfile..."; | |
40 | long long pos = ( offset() >= 0 ) ? 0 : -offset(); | |
38 | 41 | if( current_status() == generating && domain().includes( current_pos() ) && |
39 | 42 | ( offset() >= 0 || current_pos() >= -offset() ) ) |
40 | 43 | pos = current_pos(); |
43 | 46 | while( pos >= 0 ) |
44 | 47 | { |
45 | 48 | Block b( pos, softbs() ); |
46 | find_chunk( b, Sblock::non_tried ); | |
49 | find_chunk( b, Sblock::non_tried, domain(), hardbs() ); | |
47 | 50 | if( b.size() <= 0 ) break; |
48 | 51 | pos = b.end(); |
49 | current_status( generating ); | |
52 | current_status( generating, msg ); | |
50 | 53 | current_pos( b.pos() ); |
51 | 54 | if( verbosity >= 0 ) |
52 | { show_status( b.pos(), "Generating logfile...", first_post ); | |
53 | first_post = false; } | |
55 | { show_status( b.pos(), msg, first_post ); first_post = false; } | |
54 | 56 | if( interrupted() ) return -1; |
55 | 57 | int copied_size = 0, error_size = 0; |
56 | 58 | check_block( b, copied_size, error_size ); |
57 | 59 | if( copied_size + error_size < b.size() && // EOF |
58 | 60 | !truncate_vector( b.pos() + copied_size + error_size ) ) |
59 | { | |
60 | final_msg( "EOF found before end of logfile" ); | |
61 | return 1; | |
62 | } | |
61 | { final_msg( "EOF found before end of logfile" ); return 1; } | |
63 | 62 | if( !update_logfile() ) return -2; |
64 | 63 | } |
65 | 64 | return 0; |
65 | } | |
66 | ||
67 | ||
68 | void Genbook::show_status( const long long ipos, const char * const msg, | |
69 | bool force ) | |
70 | { | |
71 | const char * const up = "\x1b[A"; | |
72 | if( t0 == 0 ) | |
73 | { | |
74 | t0 = t1 = initial_time(); | |
75 | first_size = last_size = gensize; | |
76 | force = true; | |
77 | std::printf( "\n\n" ); | |
78 | } | |
79 | ||
80 | if( ipos >= 0 ) last_ipos = ipos; | |
81 | const long t2 = std::time( 0 ); | |
82 | if( t2 > t1 || force ) | |
83 | { | |
84 | if( t2 > t1 ) | |
85 | { | |
86 | a_rate = ( gensize - first_size ) / ( t2 - t0 ); | |
87 | c_rate = ( gensize - last_size ) / ( t2 - t1 ); | |
88 | t1 = t2; | |
89 | last_size = gensize; | |
90 | } | |
91 | std::printf( "\r%s%s", up, up ); | |
92 | std::printf( "rescued: %10sB, generated:%10sB, current rate: %9sB/s\n", | |
93 | format_num( recsize ), format_num( gensize ), | |
94 | format_num( c_rate, 99999 ) ); | |
95 | std::printf( " opos: %10sB, run time: %9s, average rate: %9sB/s\n", | |
96 | format_num( last_ipos + offset() ), format_time( t1 - t0 ), | |
97 | format_num( a_rate, 99999 ) ); | |
98 | if( msg && msg[0] ) | |
99 | { | |
100 | const int len = std::strlen( msg ); std::printf( "\r%s", msg ); | |
101 | for( int i = len; i < oldlen; ++i ) std::fputc( ' ', stdout ); | |
102 | oldlen = len; | |
103 | } | |
104 | std::fflush( stdout ); | |
105 | } | |
106 | else if( t2 < t1 ) // clock jumped back | |
107 | { | |
108 | t0 -= std::min( t0, t1 - t2 ); | |
109 | t1 = t2; | |
110 | } | |
66 | 111 | } |
67 | 112 | |
68 | 113 | |
89 | 134 | if( logfile_exists() ) |
90 | 135 | { |
91 | 136 | std::printf( "Initial status (read from logfile)\n" ); |
92 | std::printf( "rescued: %10sB,", format_num( recsize ) ); | |
93 | std::printf( " generated:%10sB\n", format_num( gensize ) ); | |
137 | std::printf( "rescued: %10sB, generated:%10sB\n", | |
138 | format_num( recsize ), format_num( gensize ) ); | |
94 | 139 | std::printf( "Current status\n" ); |
95 | 140 | } |
96 | 141 | } |
97 | 142 | int retval = check_all(); |
143 | const bool signaled = ( retval == -1 ); | |
144 | if( signaled ) retval = 0; | |
98 | 145 | if( verbosity >= 0 ) |
99 | 146 | { |
100 | show_status( -1, (retval ? 0 : "Finished"), true ); | |
147 | show_status( -1, ( retval || signaled ) ? 0 : "Finished", true ); | |
101 | 148 | if( retval == -2 ) std::printf( "\nLogfile error" ); |
102 | else if( retval < 0 ) std::printf( "\nInterrupted by user" ); | |
149 | else if( signaled ) std::printf( "\nInterrupted by user" ); | |
103 | 150 | std::fputc( '\n', stdout ); |
104 | 151 | } |
105 | 152 | if( retval == -2 ) retval = 1; // logfile error |
106 | 153 | else |
107 | 154 | { |
108 | if( retval == 0 ) current_status( finished ); | |
109 | else if( retval < 0 ) retval = 0; // interrupted by user | |
155 | if( retval == 0 && !signaled ) current_status( finished ); | |
110 | 156 | compact_sblock_vector(); |
111 | 157 | if( !update_logfile( -1, true ) && retval == 0 ) retval = 1; |
112 | 158 | } |
113 | 159 | if( final_msg() ) show_error( final_msg(), final_errno() ); |
114 | return retval; | |
160 | if( retval ) return retval; // errors have priority over signals | |
161 | if( signaled ) return signaled_exit(); | |
162 | return 0; | |
115 | 163 | } |
0 | 0 | /* GNU ddrescue - Data recovery tool |
1 | 1 | Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, |
2 | 2013 Antonio Diaz Diaz. | |
2 | 2013, 2014 Antonio Diaz Diaz. | |
3 | 3 | |
4 | 4 | This program is free software: you can redistribute it and/or modify |
5 | 5 | it under the terms of the GNU General Public License as published by |
32 | 32 | |
33 | 33 | #include "block.h" |
34 | 34 | #include "ddrescue.h" |
35 | #include "loggers.h" | |
35 | 36 | |
36 | 37 | |
37 | 38 | namespace { |
38 | 39 | |
39 | bool volatile interrupted_ = false; // user pressed Ctrl-C | |
40 | extern "C" void sighandler( int ) { interrupted_ = true; } | |
40 | int volatile signum_ = 0; // user pressed Ctrl-C or similar | |
41 | extern "C" void sighandler( int signum ) | |
42 | { if( signum_ == 0 && signum > 0 ) signum_ = signum; } | |
43 | ||
44 | ||
45 | int set_signal( const int signum, void (*handler)( int ) ) | |
46 | { | |
47 | struct sigaction new_action; | |
48 | ||
49 | new_action.sa_handler = handler; | |
50 | sigemptyset( &new_action.sa_mask ); | |
51 | new_action.sa_flags = SA_RESTART; | |
52 | return sigaction( signum, &new_action, 0 ); | |
53 | } | |
41 | 54 | |
42 | 55 | |
43 | 56 | bool block_is_zero( const uint8_t * const buf, const int size ) |
53 | 66 | int readblock( const int fd, uint8_t * const buf, const int size, |
54 | 67 | const long long pos ) |
55 | 68 | { |
56 | int rest = size; | |
69 | int sz = 0; | |
57 | 70 | errno = 0; |
58 | 71 | if( lseek( fd, pos, SEEK_SET ) >= 0 ) |
59 | while( rest > 0 ) | |
72 | while( sz < size ) | |
60 | 73 | { |
61 | 74 | errno = 0; |
62 | const int n = read( fd, buf + size - rest, rest ); | |
63 | if( n > 0 ) rest -= n; | |
75 | const int n = read( fd, buf + sz, size - sz ); | |
76 | if( n > 0 ) sz += n; | |
64 | 77 | else if( n == 0 ) break; // EOF |
65 | else if( errno != EINTR && errno != EAGAIN ) break; | |
66 | } | |
67 | return size - rest; | |
78 | else if( errno != EINTR ) break; | |
79 | } | |
80 | return sz; | |
68 | 81 | } |
69 | 82 | |
70 | 83 | |
74 | 87 | int writeblock( const int fd, const uint8_t * const buf, const int size, |
75 | 88 | const long long pos ) |
76 | 89 | { |
77 | int rest = size; | |
90 | int sz = 0; | |
78 | 91 | errno = 0; |
79 | 92 | if( lseek( fd, pos, SEEK_SET ) >= 0 ) |
80 | while( rest > 0 ) | |
93 | while( sz < size ) | |
81 | 94 | { |
82 | 95 | errno = 0; |
83 | const int n = write( fd, buf + size - rest, rest ); | |
84 | if( n > 0 ) rest -= n; | |
85 | else if( n < 0 && errno != EINTR && errno != EAGAIN ) break; | |
86 | } | |
87 | return size - rest; | |
96 | const int n = write( fd, buf + sz, size - sz ); | |
97 | if( n > 0 ) sz += n; | |
98 | else if( n < 0 && errno != EINTR ) break; | |
99 | } | |
100 | return sz; | |
88 | 101 | } |
89 | 102 | |
90 | 103 | } // end namespace |
94 | 107 | // |
95 | 108 | int Fillbook::fill_block( const Block & b ) |
96 | 109 | { |
97 | if( b.size() <= 0 ) internal_error( "bad size filling a Block" ); | |
110 | if( b.size() <= 0 ) internal_error( "bad size filling a Block." ); | |
98 | 111 | const int size = b.size(); |
99 | 112 | |
100 | 113 | if( writeblock( odes_, iobuf(), size, b.pos() + offset() ) != size || |
101 | 114 | ( synchronous_ && fsync( odes_ ) < 0 && errno != EINVAL ) ) |
102 | 115 | { |
103 | if( !ignore_write_errors_ ) | |
104 | { final_msg( "write error" ); final_errno( errno ); } | |
116 | if( !ignore_write_errors_ ) final_msg( "Write error", errno ); | |
105 | 117 | return 1; |
106 | 118 | } |
107 | 119 | filled_size += size; remaining_size -= size; |
108 | 120 | return 0; |
109 | } | |
110 | ||
111 | ||
112 | void Fillbook::show_status( const long long ipos, bool force ) | |
113 | { | |
114 | const char * const up = "\x1b[A"; | |
115 | if( t0 == 0 ) | |
116 | { | |
117 | t0 = t1 = std::time( 0 ); | |
118 | first_size = last_size = filled_size; | |
119 | force = true; | |
120 | std::printf( "\n\n\n" ); | |
121 | } | |
122 | ||
123 | if( ipos >= 0 ) last_ipos = ipos; | |
124 | const long t2 = std::time( 0 ); | |
125 | if( t2 > t1 || force ) | |
126 | { | |
127 | if( t2 > t1 ) | |
128 | { | |
129 | a_rate = ( filled_size - first_size ) / ( t2 - t0 ); | |
130 | c_rate = ( filled_size - last_size ) / ( t2 - t1 ); | |
131 | t1 = t2; | |
132 | last_size = filled_size; | |
133 | } | |
134 | std::printf( "\r%s%s%s", up, up, up ); | |
135 | std::printf( "filled size: %10sB,", format_num( filled_size ) ); | |
136 | std::printf( " filled areas: %6u,", filled_areas ); | |
137 | std::printf( " current rate: %9sB/s\n", format_num( c_rate, 99999 ) ); | |
138 | std::printf( "remain size: %10sB,", format_num( remaining_size ) ); | |
139 | std::printf( " remain areas: %6u,", remaining_areas ); | |
140 | std::printf( " average rate: %9sB/s\n", format_num( a_rate, 99999 ) ); | |
141 | std::printf( "current pos: %10sB\n", format_num( last_ipos + offset() ) ); | |
142 | std::fflush( stdout ); | |
143 | } | |
144 | else if( t2 < t1 ) // clock jumped back | |
145 | { | |
146 | t0 -= std::min( t0, t1 - t2 ); | |
147 | t1 = t2; | |
148 | } | |
149 | 121 | } |
150 | 122 | |
151 | 123 | |
166 | 138 | // |
167 | 139 | void Genbook::check_block( const Block & b, int & copied_size, int & error_size ) |
168 | 140 | { |
169 | if( b.size() <= 0 ) internal_error( "bad size checking a Block" ); | |
141 | if( b.size() <= 0 ) internal_error( "bad size checking a Block." ); | |
170 | 142 | copied_size = readblock( odes_, iobuf(), b.size(), b.pos() + offset() ); |
171 | 143 | if( errno ) error_size = b.size() - copied_size; |
172 | 144 | |
175 | 147 | const int size = std::min( hardbs(), copied_size - pos ); |
176 | 148 | if( !block_is_zero( iobuf() + pos, size ) ) |
177 | 149 | { |
178 | change_chunk_status( Block( b.pos() + pos, size ), Sblock::finished ); | |
150 | change_chunk_status( Block( b.pos() + pos, size ), | |
151 | Sblock::finished, domain() ); | |
179 | 152 | recsize += size; |
180 | 153 | } |
181 | 154 | gensize += size; |
182 | 155 | pos += size; |
183 | } | |
184 | } | |
185 | ||
186 | ||
187 | void Genbook::show_status( const long long ipos, const char * const msg, | |
188 | bool force ) | |
189 | { | |
190 | const char * const up = "\x1b[A"; | |
191 | if( t0 == 0 ) | |
192 | { | |
193 | t0 = t1 = std::time( 0 ); | |
194 | first_size = last_size = gensize; | |
195 | force = true; | |
196 | std::printf( "\n\n" ); | |
197 | } | |
198 | ||
199 | if( ipos >= 0 ) last_ipos = ipos; | |
200 | const long t2 = std::time( 0 ); | |
201 | if( t2 > t1 || force ) | |
202 | { | |
203 | if( t2 > t1 ) | |
204 | { | |
205 | a_rate = ( gensize - first_size ) / ( t2 - t0 ); | |
206 | c_rate = ( gensize - last_size ) / ( t2 - t1 ); | |
207 | t1 = t2; | |
208 | last_size = gensize; | |
209 | } | |
210 | std::printf( "\r%s%s", up, up ); | |
211 | std::printf( "rescued: %10sB,", format_num( recsize ) ); | |
212 | std::printf( " generated:%10sB,", format_num( gensize ) ); | |
213 | std::printf( " current rate: %9sB/s\n", format_num( c_rate, 99999 ) ); | |
214 | std::printf( " opos: %10sB, ", | |
215 | format_num( last_ipos + offset() ) ); | |
216 | std::printf( " average rate: %9sB/s\n", format_num( a_rate, 99999 ) ); | |
217 | if( msg && msg[0] ) | |
218 | { | |
219 | const int len = std::strlen( msg ); std::printf( "\r%s", msg ); | |
220 | for( int i = len; i < oldlen; ++i ) std::fputc( ' ', stdout ); | |
221 | oldlen = len; | |
222 | } | |
223 | std::fflush( stdout ); | |
224 | } | |
225 | else if( t2 < t1 ) // clock jumped back | |
226 | { | |
227 | t0 -= std::min( t0, t1 - t2 ); | |
228 | t1 = t2; | |
229 | 156 | } |
230 | 157 | } |
231 | 158 | |
254 | 181 | // |
255 | 182 | int Rescuebook::copy_block( const Block & b, int & copied_size, int & error_size ) |
256 | 183 | { |
257 | if( b.size() <= 0 ) internal_error( "bad size copying a Block" ); | |
258 | copied_size = readblock( ides_, iobuf(), b.size(), b.pos() ); | |
259 | if( errno ) error_size = b.size() - copied_size; | |
184 | if( b.size() <= 0 ) internal_error( "bad size copying a Block." ); | |
185 | if( !test_domain || test_domain->includes( b ) ) | |
186 | { | |
187 | copied_size = readblock( ides_, iobuf(), b.size(), b.pos() ); | |
188 | error_size = errno ? b.size() - copied_size : 0; | |
189 | } | |
190 | else { copied_size = 0; error_size = b.size(); } | |
260 | 191 | |
261 | 192 | if( copied_size > 0 ) |
262 | 193 | { |
270 | 201 | ( synchronous_ && fsync( odes_ ) < 0 && errno != EINVAL ) ) |
271 | 202 | { |
272 | 203 | copied_size = 0; error_size = 0; |
273 | final_msg( "write error" ); final_errno( errno ); | |
204 | final_msg( "Write error", errno ); | |
274 | 205 | return 1; |
275 | 206 | } |
276 | 207 | } |
208 | read_logger.print_line( b.pos(), b.size(), copied_size, error_size ); | |
277 | 209 | return 0; |
278 | 210 | } |
279 | 211 | |
280 | 212 | |
281 | void Rescuebook::update_rates( const bool force ) | |
282 | { | |
283 | if( t0 == 0 ) | |
284 | { | |
285 | t0 = t1 = ts = std::time( 0 ); | |
286 | first_size = last_size = recsize; | |
287 | rates_updated = true; | |
288 | if( verbosity >= 0 ) std::printf( "\n\n\n" ); | |
289 | } | |
290 | ||
291 | long t2 = std::time( 0 ); | |
292 | if( force && t2 <= t1 ) t2 = t1 + 1; // force update of e_code | |
293 | if( t2 > t1 ) | |
294 | { | |
295 | a_rate = ( recsize - first_size ) / ( t2 - t0 ); | |
296 | c_rate = ( recsize - last_size ) / ( t2 - t1 ); | |
297 | if( !( e_code & 4 ) ) | |
298 | { | |
299 | if( recsize != last_size ) { last_size = recsize; ts = t2; } | |
300 | else if( timeout >= 0 && t2 - ts > timeout ) e_code |= 4; | |
301 | } | |
302 | if( max_error_rate >= 0 && !( e_code & 1 ) ) | |
303 | { | |
304 | error_rate /= ( t2 - t1 ); | |
305 | if( error_rate > max_error_rate ) e_code |= 1; | |
306 | else error_rate = 0; | |
307 | } | |
308 | t1 = t2; | |
309 | rates_updated = true; | |
310 | } | |
311 | else if( t2 < t1 ) // clock jumped back | |
312 | { | |
313 | const long delta = std::min( t0, t1 - t2 ); | |
314 | t0 -= delta; | |
315 | ts -= delta; | |
316 | t1 = t2; | |
317 | } | |
318 | } | |
319 | ||
320 | ||
321 | void Rescuebook::show_status( const long long ipos, const char * const msg, | |
322 | const bool force ) | |
323 | { | |
324 | const char * const up = "\x1b[A"; | |
325 | ||
326 | if( ipos >= 0 ) last_ipos = ipos; | |
327 | if( rates_updated || force ) | |
328 | { | |
329 | if( verbosity >= 0 ) | |
330 | { | |
331 | std::printf( "\r%s%s%s", up, up, up ); | |
332 | std::printf( "rescued: %10sB,", format_num( recsize ) ); | |
333 | std::printf( " errsize:%9sB,", format_num( errsize, 99999 ) ); | |
334 | std::printf( " current rate: %9sB/s\n", format_num( c_rate, 99999 ) ); | |
335 | std::printf( " ipos: %10sB, errors: %7u, ", | |
336 | format_num( last_ipos ), errors ); | |
337 | std::printf( " average rate: %9sB/s\n", format_num( a_rate, 99999 ) ); | |
338 | std::printf( " opos: %10sB,", format_num( last_ipos + offset() ) ); | |
339 | std::printf( " time since last successful read: %9s\n", | |
340 | format_time( t1 - ts ) ); | |
341 | if( msg && msg[0] && !errors_or_timeout() ) | |
342 | { | |
343 | const int len = std::strlen( msg ); std::printf( "\r%s", msg ); | |
344 | for( int i = len; i < oldlen; ++i ) std::fputc( ' ', stdout ); | |
345 | oldlen = len; | |
346 | } | |
347 | std::fflush( stdout ); | |
348 | } | |
349 | rates_updated = false; | |
350 | } | |
351 | } | |
352 | ||
353 | ||
354 | 213 | const char * format_time( long t ) |
355 | 214 | { |
356 | static char buf[16]; | |
215 | enum { buffers = 8, bufsize = 16 }; | |
216 | static char buffer[buffers][bufsize]; // circle of static buffers for printf | |
217 | static int current = 0; | |
218 | char * const buf = buffer[current++]; current %= buffers; | |
357 | 219 | int fraction = 0; |
358 | 220 | char unit = 's'; |
359 | 221 | |
360 | if( t >= 86400 ) { fraction = ( t % 86400 ) / 8640; t /= 86400; unit = 'd'; } | |
361 | else if( t >= 3600 ) { fraction = ( t % 3600 ) / 360; t /= 3600; unit = 'h'; } | |
362 | else if( t >= 60 ) { fraction = ( t % 60 ) / 6; t /= 60; unit = 'm'; } | |
222 | if( t >= 86400 ) { fraction = ( t % 86400 ) / 864; t /= 86400; unit = 'd'; } | |
223 | else if( t >= 3600 ) { fraction = ( t % 3600 ) / 36; t /= 3600; unit = 'h'; } | |
224 | else if( t >= 60 ) { fraction = (10 * ( t % 60 )) / 6; t /= 60; unit = 'm'; } | |
363 | 225 | if( fraction == 0 ) |
364 | snprintf( buf, sizeof buf, "%ld %c", t, unit ); | |
226 | snprintf( buf, bufsize, "%ld %c", t, unit ); | |
365 | 227 | else |
366 | snprintf( buf, sizeof buf, "%ld.%d %c", t, fraction, unit ); | |
228 | snprintf( buf, bufsize, "%ld.%02d %c", t, fraction, unit ); | |
367 | 229 | return buf; |
368 | 230 | } |
369 | 231 | |
370 | 232 | |
371 | bool interrupted() { return interrupted_; } | |
233 | bool interrupted() { return ( signum_ > 0 ); } | |
372 | 234 | |
373 | 235 | |
374 | 236 | void set_signals() |
375 | 237 | { |
376 | interrupted_ = false; | |
377 | std::signal( SIGINT, sighandler ); | |
378 | std::signal( SIGHUP, sighandler ); | |
379 | std::signal( SIGTERM, sighandler ); | |
380 | std::signal( SIGUSR1, SIG_IGN ); | |
381 | std::signal( SIGUSR2, SIG_IGN ); | |
382 | } | |
238 | signum_ = 0; | |
239 | set_signal( SIGHUP, sighandler ); | |
240 | set_signal( SIGINT, sighandler ); | |
241 | set_signal( SIGTERM, sighandler ); | |
242 | set_signal( SIGUSR1, SIG_IGN ); | |
243 | set_signal( SIGUSR2, SIG_IGN ); | |
244 | } | |
245 | ||
246 | int signaled_exit() | |
247 | { | |
248 | set_signal( signum_, SIG_DFL ); | |
249 | std::raise( signum_ ); | |
250 | return 128 + signum_; // in case std::raise fails to exit | |
251 | } |
0 | 0 | /* GNU ddrescue - Data recovery tool |
1 | 1 | Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, |
2 | 2013 Antonio Diaz Diaz. | |
2 | 2013, 2014 Antonio Diaz Diaz. | |
3 | 3 | |
4 | 4 | This program is free software: you can redistribute it and/or modify |
5 | 5 | it under the terms of the GNU General Public License as published by |
18 | 18 | #define _FILE_OFFSET_BITS 64 |
19 | 19 | |
20 | 20 | #include <algorithm> |
21 | #include <cctype> | |
22 | 21 | #include <cerrno> |
23 | 22 | #include <climits> |
24 | 23 | #include <cstdio> |
44 | 43 | show_error( buf ); |
45 | 44 | } |
46 | 45 | |
47 | ||
48 | int my_fgetc( FILE * const f ) | |
49 | { | |
50 | int ch; | |
51 | bool comment = false; | |
52 | ||
53 | do { | |
54 | ch = std::fgetc( f ); | |
55 | if( ch == '#' ) comment = true; | |
56 | else if( ch == '\n' || ch == EOF ) comment = false; | |
57 | } | |
58 | while( comment ); | |
59 | return ch; | |
60 | } | |
61 | ||
62 | ||
63 | // Read a line discarding comments, leading whitespace and blank lines. | |
64 | // Returns 0 if at EOF. | |
65 | // | |
66 | const char * my_fgets( FILE * const f, int & linenum ) | |
67 | { | |
68 | const int maxlen = 127; | |
69 | static char buf[maxlen+1]; | |
70 | int ch, len = 1; | |
71 | ||
72 | while( len == 1 ) // while line is blank | |
73 | { | |
74 | do { ch = my_fgetc( f ); if( ch == '\n' ) ++linenum; } | |
75 | while( std::isspace( ch ) ); | |
76 | len = 0; | |
77 | while( true ) | |
78 | { | |
79 | if( ch == EOF ) { if( len > 0 ) ch = '\n'; else break; } | |
80 | if( len < maxlen ) buf[len++] = ch; | |
81 | if( ch == '\n' ) { ++linenum; break; } | |
82 | ch = my_fgetc( f ); | |
83 | } | |
84 | } | |
85 | if( len > 0 ) { buf[len] = 0; return buf; } | |
86 | else return 0; | |
87 | } | |
88 | ||
89 | ||
90 | void extend_sblock_vector( std::vector< Sblock > & sblock_vector, | |
91 | const long long isize ) | |
92 | { | |
93 | if( sblock_vector.size() == 0 ) | |
94 | { | |
95 | Sblock sb( 0, ( isize > 0 ) ? isize : -1, Sblock::non_tried ); | |
96 | sb.fix_size(); | |
97 | sblock_vector.push_back( sb ); | |
98 | return; | |
99 | } | |
100 | Sblock & front = sblock_vector.front(); | |
101 | if( front.pos() > 0 ) | |
102 | sblock_vector.insert( sblock_vector.begin(), Sblock( 0, front.pos(), Sblock::non_tried ) ); | |
103 | Sblock & back = sblock_vector.back(); | |
104 | const long long end = back.end(); | |
105 | if( isize > 0 ) | |
106 | { | |
107 | if( back.pos() >= isize ) | |
108 | { | |
109 | if( back.pos() == isize && back.status() != Sblock::finished ) | |
110 | { sblock_vector.pop_back(); return; } | |
111 | show_error( "Last block in logfile begins past end of input file.\n" | |
112 | " Use '-C' if you are reading from a partial copy.", | |
113 | 0, true ); | |
114 | std::exit( 1 ); | |
115 | } | |
116 | if( end > isize ) | |
117 | { | |
118 | if( back.status() != Sblock::finished ) | |
119 | { back.size( isize - back.pos() ); return; } | |
120 | show_error( "Rescued data in logfile goes past end of input file.\n" | |
121 | " Use '-C' if you are reading from a partial copy.", | |
122 | 0, true ); | |
123 | std::exit( 1 ); | |
124 | } | |
125 | else if( end < isize ) | |
126 | sblock_vector.push_back( Sblock( end, isize - end, Sblock::non_tried ) ); | |
127 | } | |
128 | else if( end >= 0 ) | |
129 | { | |
130 | Sblock sb( end, -1, Sblock::non_tried ); | |
131 | sb.fix_size(); | |
132 | if( sb.size() > 0 ) sblock_vector.push_back( sb ); | |
133 | } | |
134 | } | |
135 | ||
136 | ||
137 | void show_logfile_error( const char * const filename, const int linenum ) | |
138 | { | |
139 | char buf[80]; | |
140 | snprintf( buf, sizeof buf, "error in logfile %s, line %d.", filename, linenum ); | |
141 | show_error( buf ); | |
142 | } | |
143 | ||
144 | ||
145 | // Returns true if logfile exists and is readable. | |
146 | // | |
147 | bool read_logfile( const char * const logname, | |
148 | std::vector< Sblock > & sblock_vector, | |
149 | long long & current_pos, Logbook::Status & current_status ) | |
150 | { | |
151 | FILE * const f = std::fopen( logname, "r" ); | |
152 | if( !f ) return false; | |
153 | int linenum = 0; | |
154 | sblock_vector.clear(); | |
155 | ||
156 | const char * line = my_fgets( f, linenum ); | |
157 | if( line ) // status line | |
158 | { | |
159 | char ch; | |
160 | int n = std::sscanf( line, "%lli %c\n", ¤t_pos, &ch ); | |
161 | if( n == 2 && current_pos >= 0 && Logbook::isstatus( ch ) ) | |
162 | current_status = Logbook::Status( ch ); | |
163 | else | |
164 | { | |
165 | show_logfile_error( logname, linenum ); | |
166 | show_error( "Are you using a logfile from ddrescue 1.5 or older?" ); | |
167 | std::exit( 2 ); | |
168 | } | |
169 | ||
170 | while( true ) | |
171 | { | |
172 | line = my_fgets( f, linenum ); | |
173 | if( !line ) break; | |
174 | long long pos, size; | |
175 | n = std::sscanf( line, "%lli %lli %c\n", &pos, &size, &ch ); | |
176 | if( n == 3 && pos >= 0 && Sblock::isstatus( ch ) && | |
177 | ( size > 0 || size == -1 || ( size == 0 && pos == 0 ) ) ) | |
178 | { | |
179 | const Sblock::Status st = Sblock::Status( ch ); | |
180 | Sblock sb( pos, size, st ); sb.fix_size(); | |
181 | if( sblock_vector.size() > 0 && !sb.follows( sblock_vector.back() ) ) | |
182 | { show_logfile_error( logname, linenum ); std::exit( 2 ); } | |
183 | sblock_vector.push_back( sb ); | |
184 | } | |
185 | else | |
186 | { show_logfile_error( logname, linenum ); std::exit( 2 ); } | |
187 | } | |
188 | } | |
189 | std::fclose( f ); | |
190 | return true; | |
191 | } | |
192 | ||
193 | 46 | } // end namespace |
194 | 47 | |
195 | 48 | |
196 | Domain::Domain( const long long p, const long long s, | |
197 | const char * const logname ) | |
198 | { | |
199 | Block b( p, s ); b.fix_size(); | |
200 | if( !logname || !logname[0] ) { block_vector.push_back( b ); return; } | |
201 | std::vector< Sblock > sblock_vector; | |
202 | long long current_pos; // not used | |
203 | Logbook::Status current_status; // not used | |
204 | if( !read_logfile( logname, sblock_vector, current_pos, current_status ) ) | |
205 | { | |
206 | char buf[80]; | |
207 | snprintf( buf, sizeof buf, | |
208 | "Logfile '%s' does not exist or is not readable.", logname ); | |
209 | show_error( buf ); | |
210 | std::exit( 1 ); | |
211 | } | |
212 | for( unsigned i = 0; i < sblock_vector.size(); ++i ) | |
213 | { | |
214 | const Sblock & sb = sblock_vector[i]; | |
215 | if( sb.status() == Sblock::finished ) block_vector.push_back( sb ); | |
216 | } | |
217 | if( block_vector.size() == 0 ) block_vector.push_back( Block( 0, 0 ) ); | |
218 | else this->crop( b ); | |
219 | } | |
220 | ||
221 | ||
222 | void Logbook::split_domain_border_sblocks() | |
223 | { | |
224 | for( unsigned i = 0; i < sblock_vector.size(); ++i ) | |
225 | { | |
226 | Sblock & sb = sblock_vector[i]; | |
227 | const long long pos = domain_.breaks_block_by( sb ); | |
228 | if( pos > 0 ) | |
229 | { | |
230 | const Sblock head( sb.split( pos ) ); | |
231 | if( head.size() > 0 ) insert_sblock( i, head ); | |
232 | else internal_error( "empty block created by split_domain_border_sblocks" ); | |
233 | } | |
234 | } | |
235 | } | |
236 | ||
237 | ||
238 | Logbook::Logbook( const long long offset, const long long isize, | |
239 | Domain & dom, const char * const logname, | |
240 | const int cluster, const int hardbs, | |
241 | const bool complete_only, const bool do_not_read ) | |
242 | : offset_( offset ), current_pos_( 0 ), logfile_isize_( 0 ), | |
243 | current_status_( copying ), domain_( dom ), filename_( logname ), | |
244 | hardbs_( hardbs ), softbs_( cluster * hardbs ), | |
245 | final_msg_( 0 ), final_errno_( 0 ), index_( 0 ), | |
246 | ul_t1( std::time( 0 ) ), logfile_exists_( false ) | |
49 | Logbook::Logbook( const long long offset, const long long isize, Domain & dom, | |
50 | const char * const logname, const int cluster, | |
51 | const int hardbs, const bool complete_only ) | |
52 | : Logfile( logname ), offset_( offset ), logfile_isize_( 0 ), | |
53 | domain_( dom ), hardbs_( hardbs ), softbs_( cluster * hardbs ), | |
54 | final_msg_( 0 ), final_errno_( 0 ), | |
55 | ul_t1( initial_time() ), logfile_exists_( false ) | |
247 | 56 | { |
248 | 57 | int alignment = sysconf( _SC_PAGESIZE ); |
249 | 58 | if( alignment < hardbs_ || alignment % hardbs_ ) alignment = hardbs_; |
261 | 70 | { input_pos_error( domain_.pos(), isize ); std::exit( 1 ); } |
262 | 71 | domain_.crop_by_file_size( isize ); |
263 | 72 | } |
264 | if( filename_ && !do_not_read ) | |
73 | if( filename() ) | |
265 | 74 | { |
266 | logfile_exists_ = | |
267 | read_logfile( filename_, sblock_vector, current_pos_, current_status_ ); | |
268 | if( logfile_exists_ && sblock_vector.size() ) | |
269 | logfile_isize_ = sblock_vector.back().end(); | |
75 | logfile_exists_ = read_logfile(); | |
76 | if( logfile_exists_ ) logfile_isize_ = extent().end(); | |
270 | 77 | } |
271 | if( !complete_only ) extend_sblock_vector( sblock_vector, isize ); | |
272 | else if( sblock_vector.size() ) // limit domain to blocks read from logfile | |
273 | { | |
274 | const Block b( sblock_vector.front().pos(), | |
275 | sblock_vector.back().end() - sblock_vector.front().pos() ); | |
276 | domain_.crop( b ); | |
277 | } | |
78 | if( !complete_only ) extend_sblock_vector( isize ); | |
79 | else domain_.crop( extent() ); // limit domain to blocks read from logfile | |
278 | 80 | compact_sblock_vector(); |
279 | split_domain_border_sblocks(); | |
280 | if( sblock_vector.size() == 0 ) domain_.clear(); | |
281 | } | |
282 | ||
283 | ||
284 | bool Logbook::blank() const | |
285 | { | |
286 | for( unsigned i = 0; i < sblock_vector.size(); ++i ) | |
287 | if( sblock_vector[i].status() != Sblock::non_tried ) | |
288 | return false; | |
289 | return true; | |
290 | } | |
291 | ||
292 | ||
293 | void Logbook::compact_sblock_vector() | |
294 | { | |
295 | for( unsigned i = sblock_vector.size(); i >= 2; ) | |
296 | { | |
297 | --i; | |
298 | if( sblock_vector[i-1].join( sblock_vector[i] ) ) | |
299 | sblock_vector.erase( sblock_vector.begin() + i ); | |
300 | } | |
81 | split_by_domain_borders( domain_ ); | |
82 | if( sblocks() == 0 ) domain_.clear(); | |
301 | 83 | } |
302 | 84 | |
303 | 85 | |
304 | 86 | // Writes periodically the logfile to disc. |
305 | 87 | // Returns false only if update is attempted and fails. |
306 | 88 | // |
307 | bool Logbook::update_logfile( const int odes, const bool force, | |
308 | const bool retry ) | |
89 | bool Logbook::update_logfile( const int odes, const bool force ) | |
309 | 90 | { |
310 | if( !filename_ ) return true; | |
91 | if( !filename() ) return true; | |
311 | 92 | const int interval = 30 + std::min( 270, sblocks() / 38 ); // 30s to 5m |
312 | 93 | const long t2 = std::time( 0 ); |
313 | 94 | if( !force && t2 - ul_t1 < interval ) return true; |
314 | 95 | ul_t1 = t2; |
315 | 96 | if( odes >= 0 ) fsync( odes ); |
316 | 97 | |
317 | errno = 0; | |
318 | FILE * const f = std::fopen( filename_, "w" ); | |
319 | if( f ) | |
98 | while( true ) | |
320 | 99 | { |
321 | write_logfile( f ); | |
322 | if( std::fclose( f ) == 0 ) return true; | |
323 | } | |
324 | ||
325 | if( verbosity >= 0 ) | |
326 | { | |
100 | errno = 0; | |
101 | if( write_logfile( 0, true ) ) return true; | |
102 | if( verbosity < 0 ) return false; | |
103 | const int saved_errno = errno; | |
104 | std::fprintf( stderr, "\n" ); | |
327 | 105 | char buf[80]; |
328 | snprintf( buf, sizeof buf, f ? "Error writing logfile '%s'." : | |
329 | "Error opening logfile '%s' for writing.", | |
330 | filename_ ); | |
331 | if( retry ) std::fprintf( stderr, "\n" ); | |
332 | show_error( buf, errno ); | |
333 | if( retry ) | |
106 | snprintf( buf, sizeof buf, "Error writing logfile '%s'", filename() ); | |
107 | show_error( buf, saved_errno ); | |
108 | std::fprintf( stderr, "Fix the problem and press ENTER to retry, " | |
109 | "or Q+ENTER to abort. " ); | |
110 | std::fflush( stderr ); | |
111 | while( true ) | |
334 | 112 | { |
335 | std::fprintf( stderr, "Fix the problem and press ENTER to retry, or Q+ENTER to abort. " ); | |
336 | std::fflush( stderr ); | |
337 | while( true ) | |
338 | { | |
339 | const char c = std::tolower( std::fgetc( stdin ) ); | |
340 | if( c == '\r' || c == '\n' ) | |
341 | { | |
342 | std::fprintf( stderr, "\n\n\n\n" ); | |
343 | return update_logfile( -1, true ); | |
344 | } | |
345 | if( c == 'q' ) break; | |
346 | } | |
113 | const char c = std::tolower( std::fgetc( stdin ) ); | |
114 | if( c == '\r' || c == '\n' || c == 'q' ) | |
115 | { std::fprintf( stderr, "\n\n\n\n" ); | |
116 | if( c == 'q' ) return false; else break; } | |
347 | 117 | } |
348 | 118 | } |
349 | return false; | |
350 | 119 | } |
351 | ||
352 | ||
353 | void Logbook::write_logfile( FILE * const f ) const | |
354 | { | |
355 | write_logfile_header( f ); | |
356 | std::fprintf( f, "# current_pos current_status\n" ); | |
357 | std::fprintf( f, "0x%08llX %c\n", current_pos_, current_status_ ); | |
358 | std::fprintf( f, "# pos size status\n" ); | |
359 | for( unsigned i = 0; i < sblock_vector.size(); ++i ) | |
360 | { | |
361 | const Sblock & sb = sblock_vector[i]; | |
362 | std::fprintf( f, "0x%08llX 0x%08llX %c\n", sb.pos(), sb.size(), sb.status() ); | |
363 | } | |
364 | } | |
365 | ||
366 | ||
367 | // Returns false only if truncation would remove finished blocks and | |
368 | // force is false. | |
369 | // | |
370 | bool Logbook::truncate_vector( const long long end, const bool force ) | |
371 | { | |
372 | int i = sblocks() - 1; | |
373 | while( i >= 0 && sblock_vector[i].pos() >= end ) --i; | |
374 | if( i < 0 ) | |
375 | { | |
376 | sblock_vector.clear(); | |
377 | sblock_vector.push_back( Sblock( 0, 0, Sblock::non_tried ) ); | |
378 | } | |
379 | else | |
380 | { | |
381 | if( !force ) | |
382 | for( unsigned j = i + 1; j < sblock_vector.size(); ++j ) | |
383 | if( sblock_vector[j].status() == Sblock::finished ) return false; | |
384 | Sblock & sb = sblock_vector[i]; | |
385 | if( sb.includes( end ) ) | |
386 | { | |
387 | if( !force && sb.status() == Sblock::finished ) return false; | |
388 | sb.size( end - sb.pos() ); | |
389 | } | |
390 | sblock_vector.erase( sblock_vector.begin() + i + 1, sblock_vector.end() ); | |
391 | } | |
392 | return true; | |
393 | } | |
394 | ||
395 | ||
396 | int Logbook::find_index( const long long pos ) const | |
397 | { | |
398 | if( index_ < 0 || index_ >= sblocks() ) index_ = sblocks() / 2; | |
399 | while( index_ + 1 < sblocks() && pos >= sblock_vector[index_].end() ) | |
400 | ++index_; | |
401 | while( index_ > 0 && pos < sblock_vector[index_].pos() ) | |
402 | --index_; | |
403 | if( !sblock_vector[index_].includes( pos ) ) index_ = -1; | |
404 | return index_; | |
405 | } | |
406 | ||
407 | ||
408 | int Logbook::find_largest_sblock( const Sblock::Status st ) const | |
409 | { | |
410 | long long size = 0; | |
411 | int index = -1; | |
412 | for( int i = 0; i < sblocks(); ++i ) | |
413 | { | |
414 | const Sblock & sb = sblock_vector[i]; | |
415 | if( sb.status() == st && sb.size() > size && domain_.includes( sb ) ) | |
416 | { size = sb.size(); index = i; } | |
417 | } | |
418 | return index; | |
419 | } | |
420 | ||
421 | ||
422 | int Logbook::find_smallest_sblock( const Sblock::Status st ) const | |
423 | { | |
424 | long long size = LLONG_MAX; | |
425 | int index = -1; | |
426 | for( int i = 0; i < sblocks(); ++i ) | |
427 | { | |
428 | const Sblock & sb = sblock_vector[i]; | |
429 | if( sb.status() == st && | |
430 | ( sb.size() < size || ( sb.size() == size && index < 0 ) ) && | |
431 | domain_.includes( sb ) ) | |
432 | { size = sb.size(); index = i; if( size <= hardbs_ ) break; } | |
433 | } | |
434 | return index; | |
435 | } | |
436 | ||
437 | ||
438 | // Find chunk from b.pos of size <= b.size and status st. | |
439 | // if not found, puts b.size to 0. | |
440 | // | |
441 | void Logbook::find_chunk( Block & b, const Sblock::Status st, | |
442 | const int alignment ) const | |
443 | { | |
444 | if( b.size() <= 0 ) return; | |
445 | if( b.pos() < sblock_vector.front().pos() ) | |
446 | b.pos( sblock_vector.front().pos() ); | |
447 | if( find_index( b.pos() ) < 0 ) { b.size( 0 ); return; } | |
448 | int i; | |
449 | for( i = index_; i < sblocks(); ++i ) | |
450 | if( sblock_vector[i].status() == st && | |
451 | domain_.includes( sblock_vector[i] ) ) | |
452 | { index_ = i; break; } | |
453 | if( i >= sblocks() ) { b.size( 0 ); return; } | |
454 | if( b.pos() < sblock_vector[index_].pos() ) | |
455 | b.pos( sblock_vector[index_].pos() ); | |
456 | b.fix_size(); | |
457 | if( !sblock_vector[index_].includes( b ) ) | |
458 | b.crop( sblock_vector[index_] ); | |
459 | if( b.end() != sblock_vector[index_].end() ) | |
460 | b.align_end( alignment ? alignment : hardbs_ ); | |
461 | } | |
462 | ||
463 | ||
464 | // Find chunk from b.end backwards of size <= b.size and status st. | |
465 | // if not found, puts b.size to 0. | |
466 | // | |
467 | void Logbook::rfind_chunk( Block & b, const Sblock::Status st, | |
468 | const int alignment ) const | |
469 | { | |
470 | if( b.size() <= 0 ) return; | |
471 | b.fix_size(); | |
472 | if( sblock_vector.back().end() < b.end() ) | |
473 | b.end( sblock_vector.back().end() ); | |
474 | find_index( b.end() - 1 ); | |
475 | for( ; index_ >= 0; --index_ ) | |
476 | if( sblock_vector[index_].status() == st && | |
477 | domain_.includes( sblock_vector[index_] ) ) | |
478 | break; | |
479 | if( index_ < 0 ) { b.size( 0 ); return; } | |
480 | if( b.end() > sblock_vector[index_].end() ) | |
481 | b.end( sblock_vector[index_].end() ); | |
482 | if( !sblock_vector[index_].includes( b ) ) | |
483 | b.crop( sblock_vector[index_] ); | |
484 | if( b.pos() != sblock_vector[index_].pos() ) | |
485 | b.align_pos( alignment ? alignment : hardbs_ ); | |
486 | } | |
487 | ||
488 | ||
489 | // Returns an adjust value (-1, 0, +1) to keep "errors" updated without | |
490 | // having to call count_errors every time. | |
491 | // - - - --> - + - return +1 | |
492 | // - - + --> - + + return 0 | |
493 | // - + - --> - - - return -1 | |
494 | // - + + --> - - + return 0 | |
495 | // + - - --> + + - return 0 | |
496 | // + - + --> + + + return -1 | |
497 | // + + - --> + - - return 0 | |
498 | // + + + --> + - + return +1 | |
499 | // | |
500 | int Logbook::change_chunk_status( const Block & b, const Sblock::Status st ) | |
501 | { | |
502 | if( b.size() <= 0 ) return 0; | |
503 | if( !domain_.includes( b ) || find_index( b.pos() ) < 0 || | |
504 | !domain_.includes( sblock_vector[index_] ) ) | |
505 | internal_error( "can't change status of chunk not in rescue domain" ); | |
506 | if( !sblock_vector[index_].includes( b ) ) | |
507 | internal_error( "can't change status of chunk spread over more than 1 block" ); | |
508 | if( sblock_vector[index_].status() == st ) return 0; | |
509 | ||
510 | const bool old_st_good = Sblock::is_good_status( sblock_vector[index_].status() ); | |
511 | const bool new_st_good = Sblock::is_good_status( st ); | |
512 | bool bl_st_good = ( index_ <= 0 || | |
513 | !domain_.includes( sblock_vector[index_-1] ) || | |
514 | Sblock::is_good_status( sblock_vector[index_-1].status() ) ); | |
515 | bool br_st_good = ( index_ + 1 >= sblocks() || | |
516 | !domain_.includes( sblock_vector[index_+1] ) || | |
517 | Sblock::is_good_status( sblock_vector[index_+1].status() ) ); | |
518 | if( sblock_vector[index_].pos() < b.pos() ) | |
519 | { | |
520 | if( sblock_vector[index_].end() == b.end() && | |
521 | index_ + 1 < sblocks() && sblock_vector[index_+1].status() == st && | |
522 | domain_.includes( sblock_vector[index_+1] ) ) | |
523 | { | |
524 | sblock_vector[index_].inc_size( -b.size() ); | |
525 | sblock_vector[index_+1].pos( b.pos() ); | |
526 | sblock_vector[index_+1].inc_size( b.size() ); | |
527 | return 0; | |
528 | } | |
529 | insert_sblock( index_, sblock_vector[index_].split( b.pos() ) ); | |
530 | ++index_; | |
531 | bl_st_good = old_st_good; | |
532 | } | |
533 | if( sblock_vector[index_].size() > b.size() ) | |
534 | { | |
535 | sblock_vector[index_].pos( b.end() ); | |
536 | sblock_vector[index_].inc_size( -b.size() ); | |
537 | br_st_good = Sblock::is_good_status( sblock_vector[index_].status() ); | |
538 | if( index_ > 0 && sblock_vector[index_-1].status() == st && | |
539 | domain_.includes( sblock_vector[index_-1] ) ) | |
540 | sblock_vector[index_-1].inc_size( b.size() ); | |
541 | else | |
542 | insert_sblock( index_, Sblock( b, st ) ); | |
543 | } | |
544 | else | |
545 | { | |
546 | sblock_vector[index_].status( st ); | |
547 | if( index_ > 0 && sblock_vector[index_-1].status() == st && | |
548 | domain_.includes( sblock_vector[index_-1] ) ) | |
549 | { | |
550 | sblock_vector[index_-1].inc_size( sblock_vector[index_].size() ); | |
551 | erase_sblock( index_ ); --index_; | |
552 | } | |
553 | if( index_ + 1 < sblocks() && sblock_vector[index_+1].status() == st && | |
554 | domain_.includes( sblock_vector[index_+1] ) ) | |
555 | { | |
556 | sblock_vector[index_].inc_size( sblock_vector[index_+1].size() ); | |
557 | erase_sblock( index_ + 1 ); | |
558 | } | |
559 | } | |
560 | int retval = 0; | |
561 | if( new_st_good != old_st_good && bl_st_good == br_st_good ) | |
562 | { if( old_st_good == bl_st_good ) retval = +1; else retval = -1; } | |
563 | return retval; | |
564 | } | |
565 | ||
566 | ||
567 | const char * Logbook::status_name( const Logbook::Status st ) | |
568 | { | |
569 | switch( st ) | |
570 | { | |
571 | case copying: return "copying"; | |
572 | case trimming: return "trimming"; | |
573 | case splitting: return "splitting"; | |
574 | case retrying: return "retrying"; | |
575 | case filling: return "filling"; | |
576 | case generating: return "generating"; | |
577 | case finished: return "finished"; | |
578 | } | |
579 | return "unknown"; // should not be reached | |
580 | } |
0 | /* GNU ddrescue - Data recovery tool | |
1 | Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, | |
2 | 2013, 2014 Antonio Diaz Diaz. | |
3 | ||
4 | This program is free software: you can redistribute it and/or modify | |
5 | it under the terms of the GNU General Public License as published by | |
6 | the Free Software Foundation, either version 3 of the License, or | |
7 | (at your option) any later version. | |
8 | ||
9 | This program is distributed in the hope that it will be useful, | |
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | GNU General Public License for more details. | |
13 | ||
14 | You should have received a copy of the GNU General Public License | |
15 | along with this program. If not, see <http://www.gnu.org/licenses/>. | |
16 | */ | |
17 | ||
18 | #define _FILE_OFFSET_BITS 64 | |
19 | ||
20 | #include <algorithm> | |
21 | #include <cctype> | |
22 | #include <cstdio> | |
23 | #include <cstdlib> | |
24 | #include <string> | |
25 | #include <vector> | |
26 | ||
27 | #include "block.h" | |
28 | ||
29 | ||
30 | namespace { | |
31 | ||
32 | int my_fgetc( FILE * const f ) | |
33 | { | |
34 | int ch = std::fgetc( f ); | |
35 | if( ch == '#' ) // comment | |
36 | { do ch = std::fgetc( f ); while( ch != '\n' && ch != EOF ); } | |
37 | return ch; | |
38 | } | |
39 | ||
40 | ||
41 | // Read a line discarding comments, leading whitespace and blank lines. | |
42 | // Returns 0 if at EOF. | |
43 | // | |
44 | const char * my_fgets( FILE * const f, int & linenum ) | |
45 | { | |
46 | const int maxlen = 127; | |
47 | static char buf[maxlen+1]; | |
48 | int ch, len = 1; | |
49 | ||
50 | while( len == 1 ) // while line is blank | |
51 | { | |
52 | do { ch = my_fgetc( f ); if( ch == '\n' ) ++linenum; } | |
53 | while( std::isspace( ch ) ); | |
54 | len = 0; | |
55 | while( true ) | |
56 | { | |
57 | if( ch == EOF ) { if( len > 0 ) ch = '\n'; else break; } | |
58 | if( len < maxlen ) buf[len++] = ch; | |
59 | if( ch == '\n' ) { ++linenum; break; } | |
60 | ch = my_fgetc( f ); | |
61 | } | |
62 | } | |
63 | if( len > 0 ) { buf[len] = 0; return buf; } | |
64 | else return 0; | |
65 | } | |
66 | ||
67 | ||
68 | void show_logfile_error( const char * const logname, const int linenum ) | |
69 | { | |
70 | char buf[80]; | |
71 | snprintf( buf, sizeof buf, "error in logfile %s, line %d.", logname, linenum ); | |
72 | show_error( buf ); | |
73 | } | |
74 | ||
75 | } // end namespace | |
76 | ||
77 | ||
78 | void Logfile::compact_sblock_vector() | |
79 | { | |
80 | std::vector< Sblock > new_vector; | |
81 | unsigned l = 0; | |
82 | while( l < sblock_vector.size() ) | |
83 | { | |
84 | Sblock run = sblock_vector[l]; | |
85 | unsigned r = l + 1; | |
86 | while( r < sblock_vector.size() && | |
87 | sblock_vector[r].status() == run.status() ) ++r; | |
88 | if( r > l + 1 ) run.size( sblock_vector[r-1].end() - run.pos() ); | |
89 | new_vector.push_back( run ); | |
90 | l = r; | |
91 | } | |
92 | sblock_vector.swap( new_vector ); | |
93 | } | |
94 | ||
95 | ||
96 | void Logfile::extend_sblock_vector( const long long isize ) | |
97 | { | |
98 | if( sblock_vector.empty() ) | |
99 | { | |
100 | const Sblock sb( 0, ( isize > 0 ) ? isize : -1, Sblock::non_tried ); | |
101 | sblock_vector.push_back( sb ); | |
102 | return; | |
103 | } | |
104 | Sblock & front = sblock_vector.front(); | |
105 | if( front.pos() > 0 ) | |
106 | sblock_vector.insert( sblock_vector.begin(), Sblock( 0, front.pos(), Sblock::non_tried ) ); | |
107 | Sblock & back = sblock_vector.back(); | |
108 | const long long end = back.end(); | |
109 | if( isize > 0 ) | |
110 | { | |
111 | if( back.pos() >= isize ) | |
112 | { | |
113 | if( back.pos() == isize && back.status() != Sblock::finished ) | |
114 | { sblock_vector.pop_back(); return; } | |
115 | show_error( "Last block in logfile begins past end of input file.\n" | |
116 | " Use '-C' if you are reading from a partial copy.", | |
117 | 0, true ); | |
118 | std::exit( 1 ); | |
119 | } | |
120 | if( end > isize ) | |
121 | { | |
122 | if( back.status() != Sblock::finished ) | |
123 | { back.size( isize - back.pos() ); return; } | |
124 | show_error( "Rescued data in logfile goes past end of input file.\n" | |
125 | " Use '-C' if you are reading from a partial copy.", | |
126 | 0, true ); | |
127 | std::exit( 1 ); | |
128 | } | |
129 | else if( end < isize ) | |
130 | sblock_vector.push_back( Sblock( end, isize - end, Sblock::non_tried ) ); | |
131 | } | |
132 | else if( end >= 0 ) | |
133 | { | |
134 | const Sblock sb( end, -1, Sblock::non_tried ); | |
135 | if( sb.size() > 0 ) sblock_vector.push_back( sb ); | |
136 | } | |
137 | } | |
138 | ||
139 | ||
140 | // Returns false only if truncation would remove finished blocks and | |
141 | // force is false. | |
142 | // | |
143 | bool Logfile::truncate_vector( const long long end, const bool force ) | |
144 | { | |
145 | unsigned i = sblock_vector.size(); | |
146 | while( i > 0 && sblock_vector[i-1].pos() >= end ) --i; | |
147 | if( !force ) | |
148 | for( unsigned j = i; j < sblock_vector.size(); ++j ) | |
149 | if( sblock_vector[j].status() == Sblock::finished ) return false; | |
150 | if( i == 0 ) | |
151 | { | |
152 | sblock_vector.clear(); | |
153 | sblock_vector.push_back( Sblock( 0, 0, Sblock::non_tried ) ); | |
154 | } | |
155 | else | |
156 | { | |
157 | Sblock & sb = sblock_vector[i-1]; | |
158 | if( sb.includes( end ) ) | |
159 | { | |
160 | if( !force && sb.status() == Sblock::finished ) return false; | |
161 | sb.size( end - sb.pos() ); | |
162 | } | |
163 | sblock_vector.erase( sblock_vector.begin() + i, sblock_vector.end() ); | |
164 | } | |
165 | return true; | |
166 | } | |
167 | ||
168 | ||
169 | // Returns true if logfile exists and is readable. | |
170 | // Fills the gaps if 'default_sblock_status' is a valid status character. | |
171 | // | |
172 | bool Logfile::read_logfile( const int default_sblock_status ) | |
173 | { | |
174 | FILE * const f = std::fopen( filename_, "r" ); | |
175 | if( !f ) return false; | |
176 | int linenum = 0; | |
177 | const bool loose = Sblock::isstatus( default_sblock_status ); | |
178 | read_only_ = false; | |
179 | sblock_vector.clear(); | |
180 | ||
181 | const char * line = my_fgets( f, linenum ); | |
182 | if( line ) // status line | |
183 | { | |
184 | char ch; | |
185 | int n = std::sscanf( line, "%lli %c\n", ¤t_pos_, &ch ); | |
186 | if( n == 2 && current_pos_ >= 0 && isstatus( ch ) ) | |
187 | current_status_ = Status( ch ); | |
188 | else | |
189 | { | |
190 | show_logfile_error( filename_, linenum ); | |
191 | show_error( "Are you using a logfile from ddrescue 1.5 or older?" ); | |
192 | std::exit( 2 ); | |
193 | } | |
194 | ||
195 | while( true ) | |
196 | { | |
197 | line = my_fgets( f, linenum ); | |
198 | if( !line ) break; | |
199 | long long pos, size; | |
200 | n = std::sscanf( line, "%lli %lli %c\n", &pos, &size, &ch ); | |
201 | if( n == 3 && pos >= 0 && Sblock::isstatus( ch ) && | |
202 | ( size > 0 || ( size == 0 && pos == 0 ) ) ) | |
203 | { | |
204 | const Sblock::Status st = Sblock::Status( ch ); | |
205 | const Sblock sb( pos, size, st ); | |
206 | const long long end = sblock_vector.size() ? | |
207 | sblock_vector.back().end() : 0; | |
208 | if( sb.pos() != end ) | |
209 | { | |
210 | if( loose && sb.pos() > end ) | |
211 | { const Sblock sb2( end, sb.pos() - end, | |
212 | Sblock::Status( default_sblock_status ) ); | |
213 | sblock_vector.push_back( sb2 ); } | |
214 | else if( end > 0 ) | |
215 | { show_logfile_error( filename_, linenum ); std::exit( 2 ); } | |
216 | } | |
217 | sblock_vector.push_back( sb ); | |
218 | } | |
219 | else | |
220 | { show_logfile_error( filename_, linenum ); std::exit( 2 ); } | |
221 | } | |
222 | } | |
223 | if( std::ferror( f ) ) | |
224 | { show_logfile_error( filename_, linenum ); std::exit( 2 ); } | |
225 | if( std::freopen( filename_, "r+", f ) ) std::fclose( f ); | |
226 | else read_only_ = true; | |
227 | return true; | |
228 | } | |
229 | ||
230 | ||
231 | int Logfile::write_logfile( FILE * f, const bool timestamp ) const | |
232 | { | |
233 | const bool f_given = ( f != 0 ); | |
234 | ||
235 | if( !f && !filename_ ) return false; | |
236 | if( !f ) { f = std::fopen( filename_, "w" ); if( !f ) return false; } | |
237 | write_logfile_header( f, "Rescue" ); | |
238 | if( timestamp ) write_timestamp( f ); | |
239 | if( current_msg.size() ) std::fprintf( f, "# %s\n", current_msg.c_str() ); | |
240 | std::fprintf( f, "# current_pos current_status\n" ); | |
241 | std::fprintf( f, "0x%08llX %c\n", current_pos_, current_status_ ); | |
242 | std::fprintf( f, "# pos size status\n" ); | |
243 | for( unsigned i = 0; i < sblock_vector.size(); ++i ) | |
244 | { | |
245 | const Sblock & sb = sblock_vector[i]; | |
246 | std::fprintf( f, "0x%08llX 0x%08llX %c\n", sb.pos(), sb.size(), sb.status() ); | |
247 | } | |
248 | return ( f_given || std::fclose( f ) == 0 ); | |
249 | } | |
250 | ||
251 | ||
252 | bool Logfile::blank() const | |
253 | { | |
254 | for( unsigned i = 0; i < sblock_vector.size(); ++i ) | |
255 | if( sblock_vector[i].status() != Sblock::non_tried ) | |
256 | return false; | |
257 | return true; | |
258 | } | |
259 | ||
260 | ||
261 | void Logfile::split_by_domain_borders( const Domain & domain ) | |
262 | { | |
263 | if( domain.blocks() == 1 ) | |
264 | { | |
265 | const Block & db = domain.block( 0 ); | |
266 | unsigned i = 0; | |
267 | while( i < sblock_vector.size() && sblock_vector[i] < db ) ++i; | |
268 | if( i < sblock_vector.size() ) try_split_sblock_by( db.pos(), i ); | |
269 | i = sblock_vector.size(); | |
270 | while( i > 0 && db < sblock_vector[i-1] ) --i; | |
271 | if( i > 0 ) try_split_sblock_by( db.end(), i - 1 ); | |
272 | } | |
273 | else | |
274 | { | |
275 | std::vector< Sblock > new_vector; | |
276 | int j = 0; | |
277 | for( unsigned i = 0; i < sblock_vector.size(); ) | |
278 | { | |
279 | Sblock & sb = sblock_vector[i]; | |
280 | while( j < domain.blocks() && domain.block( j ) < sb ) ++j; | |
281 | if( j >= domain.blocks() ) // end of domain tail copy | |
282 | { new_vector.insert( new_vector.end(), | |
283 | sblock_vector.begin() + i, sblock_vector.end() ); | |
284 | break; } | |
285 | const Block & db = domain.block( j ); | |
286 | if( sb.strictly_includes( db.pos() ) ) | |
287 | new_vector.push_back( sb.split( db.pos() ) ); | |
288 | if( sb.strictly_includes( db.end() ) ) | |
289 | new_vector.push_back( sb.split( db.end() ) ); | |
290 | if( sb.pos() < db.end() ) { new_vector.push_back( sb ); ++i; } | |
291 | } | |
292 | sblock_vector.swap( new_vector ); | |
293 | } | |
294 | } | |
295 | ||
296 | ||
297 | void Logfile::split_by_logfile_borders( const Logfile & logfile ) | |
298 | { | |
299 | std::vector< Sblock > new_vector; | |
300 | int j = 0; | |
301 | for( unsigned i = 0; i < sblock_vector.size(); ) | |
302 | { | |
303 | Sblock & sb = sblock_vector[i]; | |
304 | while( j < logfile.sblocks() && logfile.sblock( j ) < sb ) ++j; | |
305 | if( j >= logfile.sblocks() ) // end of logfile tail copy | |
306 | { new_vector.insert( new_vector.end(), | |
307 | sblock_vector.begin() + i, sblock_vector.end() ); | |
308 | break; } | |
309 | const Sblock & db = logfile.sblock( j ); | |
310 | if( sb.strictly_includes( db.pos() ) ) | |
311 | new_vector.push_back( sb.split( db.pos() ) ); | |
312 | if( sb.strictly_includes( db.end() ) ) | |
313 | new_vector.push_back( sb.split( db.end() ) ); | |
314 | if( sb.pos() < db.end() ) { new_vector.push_back( sb ); ++i; } | |
315 | } | |
316 | sblock_vector.swap( new_vector ); | |
317 | } | |
318 | ||
319 | ||
320 | int Logfile::find_index( const long long pos ) const | |
321 | { | |
322 | if( index_ < 0 || index_ >= sblocks() ) index_ = sblocks() / 2; | |
323 | while( index_ + 1 < sblocks() && pos >= sblock_vector[index_].end() ) | |
324 | ++index_; | |
325 | while( index_ > 0 && pos < sblock_vector[index_].pos() ) | |
326 | --index_; | |
327 | if( !sblock_vector[index_].includes( pos ) ) index_ = -1; | |
328 | return index_; | |
329 | } | |
330 | ||
331 | ||
332 | int Logfile::find_largest_sblock( const Sblock::Status st, | |
333 | const Domain & domain ) const | |
334 | { | |
335 | long long size = 0; | |
336 | int index = -1; | |
337 | for( int i = 0; i < sblocks(); ++i ) | |
338 | { | |
339 | const Sblock & sb = sblock_vector[i]; | |
340 | if( sb.status() == st && sb.size() > size && domain.includes( sb ) ) | |
341 | { size = sb.size(); index = i; } | |
342 | } | |
343 | return index; | |
344 | } | |
345 | ||
346 | ||
347 | int Logfile::find_smallest_sblock( const Sblock::Status st, | |
348 | const Domain & domain, const int min_size ) const | |
349 | { | |
350 | long long size = LLONG_MAX; | |
351 | int index = -1; | |
352 | for( int i = 0; i < sblocks(); ++i ) | |
353 | { | |
354 | const Sblock & sb = sblock_vector[i]; | |
355 | if( sb.status() == st && ( sb.size() < size || index < 0 ) && | |
356 | domain.includes( sb ) ) | |
357 | { size = sb.size(); index = i; if( size <= min_size ) break; } | |
358 | } | |
359 | return index; | |
360 | } | |
361 | ||
362 | ||
363 | // Find chunk from b.pos of size <= b.size and status st. | |
364 | // If not found, put b.size to 0. | |
365 | // | |
366 | void Logfile::find_chunk( Block & b, const Sblock::Status st, | |
367 | const Domain & domain, const int alignment ) const | |
368 | { | |
369 | if( b.size() <= 0 ) return; | |
370 | if( b.pos() < sblock_vector.front().pos() ) | |
371 | b.pos( sblock_vector.front().pos() ); | |
372 | if( find_index( b.pos() ) < 0 ) { b.size( 0 ); return; } | |
373 | int i; | |
374 | for( i = index_; i < sblocks(); ++i ) | |
375 | if( sblock_vector[i].status() == st && domain.includes( sblock_vector[i] ) ) | |
376 | { index_ = i; break; } | |
377 | if( i >= sblocks() ) { b.size( 0 ); return; } | |
378 | if( b.pos() < sblock_vector[index_].pos() ) | |
379 | b.pos( sblock_vector[index_].pos() ); | |
380 | if( !sblock_vector[index_].includes( b ) ) | |
381 | b.crop( sblock_vector[index_] ); | |
382 | if( b.end() != sblock_vector[index_].end() ) | |
383 | b.align_end( alignment ); | |
384 | } | |
385 | ||
386 | ||
387 | // Find chunk from b.end backwards of size <= b.size and status st. | |
388 | // If not found, put b.size to 0. | |
389 | // | |
390 | void Logfile::rfind_chunk( Block & b, const Sblock::Status st, | |
391 | const Domain & domain, const int alignment ) const | |
392 | { | |
393 | if( b.size() <= 0 ) return; | |
394 | if( sblock_vector.back().end() < b.end() ) | |
395 | b.end( sblock_vector.back().end() ); | |
396 | if( find_index( b.end() - 1 ) < 0 ) { b.size( 0 ); return; } | |
397 | int i; | |
398 | for( i = index_; i >= 0; --i ) | |
399 | if( sblock_vector[i].status() == st && domain.includes( sblock_vector[i] ) ) | |
400 | { index_ = i; break; } | |
401 | if( i < 0 ) { b.size( 0 ); return; } | |
402 | if( b.end() > sblock_vector[index_].end() ) | |
403 | b.end( sblock_vector[index_].end() ); | |
404 | if( !sblock_vector[index_].includes( b ) ) | |
405 | b.crop( sblock_vector[index_] ); | |
406 | if( b.pos() != sblock_vector[index_].pos() ) | |
407 | b.align_pos( alignment ); | |
408 | } | |
409 | ||
410 | ||
411 | // Returns an adjust value (-1, 0, +1) to keep "errors" updated without | |
412 | // having to call count_errors every time. | |
413 | // - - - --> - + - return +1 | |
414 | // - - + --> - + + return 0 | |
415 | // - + - --> - - - return -1 | |
416 | // - + + --> - - + return 0 | |
417 | // + - - --> + + - return 0 | |
418 | // + - + --> + + + return -1 | |
419 | // + + - --> + - - return 0 | |
420 | // + + + --> + - + return +1 | |
421 | // | |
422 | int Logfile::change_chunk_status( const Block & b, const Sblock::Status st, | |
423 | const Domain & domain ) | |
424 | { | |
425 | if( b.size() <= 0 ) return 0; | |
426 | if( !domain.includes( b ) || find_index( b.pos() ) < 0 || | |
427 | !domain.includes( sblock_vector[index_] ) ) | |
428 | internal_error( "can't change status of chunk not in rescue domain." ); | |
429 | if( !sblock_vector[index_].includes( b ) ) | |
430 | internal_error( "can't change status of chunk spread over more than 1 block." ); | |
431 | if( sblock_vector[index_].status() == st ) return 0; | |
432 | ||
433 | const bool old_st_good = Sblock::is_good_status( sblock_vector[index_].status() ); | |
434 | const bool new_st_good = Sblock::is_good_status( st ); | |
435 | bool bl_st_good = ( index_ <= 0 || | |
436 | !domain.includes( sblock_vector[index_-1] ) || | |
437 | Sblock::is_good_status( sblock_vector[index_-1].status() ) ); | |
438 | bool br_st_good = ( index_ + 1 >= sblocks() || | |
439 | !domain.includes( sblock_vector[index_+1] ) || | |
440 | Sblock::is_good_status( sblock_vector[index_+1].status() ) ); | |
441 | if( sblock_vector[index_].pos() < b.pos() ) | |
442 | { | |
443 | if( sblock_vector[index_].end() == b.end() && | |
444 | index_ + 1 < sblocks() && sblock_vector[index_+1].status() == st && | |
445 | domain.includes( sblock_vector[index_+1] ) ) | |
446 | { | |
447 | sblock_vector[index_].shift( sblock_vector[index_+1], b.pos() ); | |
448 | return 0; | |
449 | } | |
450 | insert_sblock( index_, sblock_vector[index_].split( b.pos() ) ); | |
451 | ++index_; | |
452 | bl_st_good = old_st_good; | |
453 | } | |
454 | if( sblock_vector[index_].size() > b.size() ) | |
455 | { | |
456 | br_st_good = Sblock::is_good_status( sblock_vector[index_].status() ); | |
457 | if( index_ > 0 && sblock_vector[index_-1].status() == st && | |
458 | domain.includes( sblock_vector[index_-1] ) ) | |
459 | sblock_vector[index_-1].shift( sblock_vector[index_], b.end() ); | |
460 | else | |
461 | insert_sblock( index_, | |
462 | Sblock( sblock_vector[index_].split( b.end() ), st ) ); | |
463 | } | |
464 | else | |
465 | { | |
466 | sblock_vector[index_].status( st ); | |
467 | if( index_ > 0 && sblock_vector[index_-1].status() == st && | |
468 | domain.includes( sblock_vector[index_-1] ) ) | |
469 | { | |
470 | sblock_vector[index_-1].join( sblock_vector[index_] ); | |
471 | erase_sblock( index_ ); --index_; | |
472 | } | |
473 | if( index_ + 1 < sblocks() && sblock_vector[index_+1].status() == st && | |
474 | domain.includes( sblock_vector[index_+1] ) ) | |
475 | { | |
476 | sblock_vector[index_].join( sblock_vector[index_+1] ); | |
477 | erase_sblock( index_ + 1 ); | |
478 | } | |
479 | } | |
480 | int retval = 0; | |
481 | if( new_st_good != old_st_good && bl_st_good == br_st_good ) | |
482 | { if( old_st_good == bl_st_good ) retval = +1; else retval = -1; } | |
483 | return retval; | |
484 | } | |
485 | ||
486 | ||
487 | const char * Logfile::status_name( const Logfile::Status st ) | |
488 | { | |
489 | switch( st ) | |
490 | { | |
491 | case copying: return "copying"; | |
492 | case trimming: return "trimming"; | |
493 | case splitting: return "splitting"; | |
494 | case retrying: return "retrying"; | |
495 | case filling: return "filling"; | |
496 | case generating: return "generating"; | |
497 | case finished: return "finished"; | |
498 | } | |
499 | return "unknown"; // should not be reached | |
500 | } |
0 | /* GNU ddrescue - Data recovery tool | |
1 | Copyright (C) 2013, 2014 Antonio Diaz Diaz. | |
2 | ||
3 | This program is free software: you can redistribute it and/or modify | |
4 | it under the terms of the GNU General Public License as published by | |
5 | the Free Software Foundation, either version 3 of the License, or | |
6 | (at your option) any later version. | |
7 | ||
8 | This program is distributed in the hope that it will be useful, | |
9 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
11 | GNU General Public License for more details. | |
12 | ||
13 | You should have received a copy of the GNU General Public License | |
14 | along with this program. If not, see <http://www.gnu.org/licenses/>. | |
15 | */ | |
16 | ||
17 | #define _FILE_OFFSET_BITS 64 | |
18 | ||
19 | #include <cstdio> | |
20 | #include <string> | |
21 | #include <vector> | |
22 | ||
23 | #include "block.h" | |
24 | #include "loggers.h" | |
25 | ||
26 | ||
27 | namespace { | |
28 | ||
29 | const char * format_time_dhms( const long t ) | |
30 | { | |
31 | static char buf[32]; | |
32 | const long s = t % 60; | |
33 | const long m = ( t / 60 ) % 60; | |
34 | const long h = ( t / 3600 ) % 24; | |
35 | const long d = t / 86400; | |
36 | ||
37 | if( d ) snprintf( buf, sizeof buf, "%lud:%02luh:%02lum:%02lus", d, h, m, s ); | |
38 | else if( h ) snprintf( buf, sizeof buf, "%luh:%02lum:%02lus", h, m, s ); | |
39 | else if( m ) snprintf( buf, sizeof buf, "%lum:%02lus", m, s ); | |
40 | else snprintf( buf, sizeof buf, "%lus", s ); | |
41 | return buf; | |
42 | } | |
43 | ||
44 | } // end namespace | |
45 | ||
46 | ||
47 | Rate_logger rate_logger; | |
48 | Read_logger read_logger; | |
49 | ||
50 | ||
51 | bool Logger::close_file() | |
52 | { | |
53 | if( f && !error && !write_final_timestamp( f ) ) error = true; | |
54 | if( f && std::fclose( f ) != 0 ) error = true; | |
55 | f = 0; | |
56 | return !error; | |
57 | } | |
58 | ||
59 | ||
60 | bool Rate_logger::open_file() | |
61 | { | |
62 | if( !filename_ ) return true; | |
63 | if( !f ) | |
64 | { | |
65 | last_time = -1; | |
66 | f = std::fopen( filename_, "w" ); | |
67 | error = !f || !write_logfile_header( f, "Rates" ) || | |
68 | std::fprintf( f, "#Time Ipos Current_rate Average_rate Errors Errsize\n" ) < 0; | |
69 | } | |
70 | return !error; | |
71 | } | |
72 | ||
73 | ||
74 | bool Rate_logger::print_line( const long time, const long long ipos, | |
75 | const long long a_rate, const long long c_rate, | |
76 | const int errors, const long long errsize ) | |
77 | { | |
78 | if( f && !error && time > last_time ) | |
79 | { | |
80 | last_time = time; | |
81 | if( std::fprintf( f, "%2lu 0x%08llX %8llu %8llu %7u %8llu\n", | |
82 | time, ipos, c_rate, a_rate, errors, errsize ) < 0 ) | |
83 | error = true; | |
84 | } | |
85 | return !error; | |
86 | } | |
87 | ||
88 | ||
89 | bool Read_logger::open_file() | |
90 | { | |
91 | if( !filename_ ) return true; | |
92 | if( !f ) | |
93 | { | |
94 | prev_is_msg = true; | |
95 | f = std::fopen( filename_, "w" ); | |
96 | error = !f || !write_logfile_header( f, "Reads" ) || | |
97 | std::fprintf( f, "# Ipos Size Copied_size Error_size\n" ) < 0; | |
98 | } | |
99 | return !error; | |
100 | } | |
101 | ||
102 | ||
103 | bool Read_logger::print_line( const long long ipos, const long long size, | |
104 | const int copied_size, const int error_size ) | |
105 | { | |
106 | if( f && !error && | |
107 | std::fprintf( f, "0x%08llX %llu %u %u\n", | |
108 | ipos, size, copied_size, error_size ) < 0 ) | |
109 | error = true; | |
110 | prev_is_msg = false; | |
111 | return !error; | |
112 | } | |
113 | ||
114 | ||
115 | bool Read_logger::print_msg( const long time, const char * const msg ) | |
116 | { | |
117 | if( f && !error && | |
118 | std::fprintf( f, "%s# %s %s\n", prev_is_msg ? "" : "\n\n", | |
119 | format_time_dhms( time ), msg ) < 0 ) | |
120 | error = true; | |
121 | prev_is_msg = true; | |
122 | return !error; | |
123 | } | |
124 | ||
125 | ||
126 | bool Read_logger::print_time( const long time ) | |
127 | { | |
128 | if( f && !error && time > 0 && | |
129 | std::fprintf( f, "# %s\n", format_time_dhms( time ) ) < 0 ) | |
130 | error = true; | |
131 | return !error; | |
132 | } |
0 | /* GNU ddrescue - Data recovery tool | |
1 | Copyright (C) 2013, 2014 Antonio Diaz Diaz. | |
2 | ||
3 | This program is free software: you can redistribute it and/or modify | |
4 | it under the terms of the GNU General Public License as published by | |
5 | the Free Software Foundation, either version 3 of the License, or | |
6 | (at your option) any later version. | |
7 | ||
8 | This program is distributed in the hope that it will be useful, | |
9 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
11 | GNU General Public License for more details. | |
12 | ||
13 | You should have received a copy of the GNU General Public License | |
14 | along with this program. If not, see <http://www.gnu.org/licenses/>. | |
15 | */ | |
16 | ||
17 | class Logger | |
18 | { | |
19 | protected: | |
20 | const char * filename_; | |
21 | FILE * f; // output stream | |
22 | bool error; | |
23 | ||
24 | public: | |
25 | Logger() : f( 0 ), error( false ) {} | |
26 | ||
27 | void set_filename( const char * const name ) { filename_ = name; } | |
28 | bool close_file(); | |
29 | }; | |
30 | ||
31 | ||
32 | class Rate_logger : public Logger | |
33 | { | |
34 | long last_time; | |
35 | public: | |
36 | Rate_logger() : last_time( -1 ) {} | |
37 | bool open_file(); | |
38 | bool print_line( const long time, const long long ipos, | |
39 | const long long a_rate, const long long c_rate, | |
40 | const int errors, const long long errsize ); | |
41 | }; | |
42 | ||
43 | extern Rate_logger rate_logger; | |
44 | ||
45 | ||
46 | class Read_logger : public Logger | |
47 | { | |
48 | bool prev_is_msg; | |
49 | public: | |
50 | Read_logger() : prev_is_msg( true ) {} | |
51 | bool open_file(); | |
52 | bool print_line( const long long ipos, const long long size, | |
53 | const int copied_size, const int error_size ); | |
54 | bool print_msg( const long time, const char * const msg ); | |
55 | bool print_time( const long time ); | |
56 | }; | |
57 | ||
58 | extern Read_logger read_logger; |
0 | 0 | /* GNU ddrescue - Data recovery tool |
1 | 1 | Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, |
2 | 2013 Antonio Diaz Diaz. | |
2 | 2013, 2014 Antonio Diaz Diaz. | |
3 | 3 | |
4 | 4 | This program is free software: you can redistribute it and/or modify |
5 | 5 | it under the terms of the GNU General Public License as published by |
23 | 23 | |
24 | 24 | #define _FILE_OFFSET_BITS 64 |
25 | 25 | |
26 | #include <algorithm> | |
26 | 27 | #include <cerrno> |
27 | 28 | #include <climits> |
28 | 29 | #include <cstdio> |
29 | 30 | #include <cstdlib> |
30 | 31 | #include <cstring> |
32 | #include <ctime> | |
31 | 33 | #include <string> |
32 | 34 | #include <vector> |
33 | 35 | #include <fcntl.h> |
39 | 41 | #include "rational.h" |
40 | 42 | #include "block.h" |
41 | 43 | #include "ddrescue.h" |
44 | #include "loggers.h" | |
45 | ||
46 | #ifndef O_BINARY | |
47 | #define O_BINARY 0 | |
48 | #endif | |
42 | 49 | |
43 | 50 | #ifndef O_DIRECT |
44 | 51 | #define O_DIRECT 0 |
54 | 61 | enum Mode { m_none, m_fill, m_generate }; |
55 | 62 | const mode_t outmode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; |
56 | 63 | |
57 | #ifdef O_BINARY | |
58 | const int o_binary = O_BINARY; | |
59 | #else | |
60 | const int o_binary = 0; | |
61 | #endif | |
62 | ||
63 | 64 | |
64 | 65 | void show_help( const int cluster, const int hardbs, const int skipbs ) |
65 | 66 | { |
66 | 67 | std::printf( "%s - Data recovery tool.\n", Program_name ); |
67 | 68 | std::printf( "Copies data from one file or block device to another,\n" |
68 | "trying hard to rescue data in case of read errors.\n" | |
69 | "trying to rescue the good parts first in case of read errors.\n" | |
69 | 70 | "\nUsage: %s [options] infile outfile [logfile]\n", invocation_name ); |
70 | 71 | std::printf( "You should use a logfile unless you know what you are doing.\n" |
71 | 72 | "If you reboot, check the device names before restarting ddrescue.\n" |
72 | "Do not use options '-F' or '-g' without reading the manual first.\n" | |
73 | "Do not use options '-F' or '-G' without reading the manual first.\n" | |
73 | 74 | "\nOptions:\n" |
74 | 75 | " -h, --help display this help and exit\n" |
75 | 76 | " -V, --version output version information and exit\n" |
85 | 86 | " -E, --max-error-rate=<bytes> maximum allowed rate of read errors per second\n" |
86 | 87 | " -f, --force overwrite output device or partition\n" |
87 | 88 | " -F, --fill-mode=<types> fill given type blocks with infile data (?*/-+)\n" |
88 | " -g, --generate-mode generate approximate logfile from partial copy\n" | |
89 | " -G, --generate-mode generate approximate logfile from partial copy\n" | |
90 | " -H, --test-mode=<file> set map of good/bad blocks from given logile\n" | |
89 | 91 | " -i, --input-position=<bytes> starting position in input file [0]\n" |
90 | 92 | " -I, --verify-input-size verify input file size with size in logfile\n" |
91 | " -K, --skip-size=<bytes> initial size to skip on read error [%sB]\n", | |
93 | " -K, --skip-size=<min>[,<max>] initial size to skip on read error [%sB]\n", | |
92 | 94 | format_num( skipbs, 9999, -1 ) ); |
93 | std::printf( " -l, --logfile-size=<entries> do not grow logfile beyond this size [1000]\n" | |
95 | std::printf( " -l, --logfile-size=<entries> do not grow logfile beyond this size [10000]\n" | |
96 | " -L, --loose-domain accept an incomplete domain logfile\n" | |
94 | 97 | " -m, --domain-logfile=<file> restrict domain to finished blocks in file\n" |
95 | 98 | " -M, --retrim mark all failed blocks as non-trimmed\n" |
96 | " -n, --no-split do not try to split or retry failed blocks\n" | |
99 | " -n, --no-split skip the splitting phase\n" | |
100 | " -N, --no-trim skip the trimming phase\n" | |
97 | 101 | " -o, --output-position=<bytes> starting position in output file [ipos]\n" |
102 | " -O, --reopen-on-error reopen input file after every read error\n" | |
98 | 103 | " -p, --preallocate preallocate space on disc for output file\n" |
99 | 104 | " -q, --quiet suppress all messages\n" |
100 | " -r, --retries=<n> exit after given retries (-1=infinity) [0]\n" | |
101 | " -R, --reverse reverse direction of copy operations\n" | |
105 | " -r, --retry-passes=<n> exit after <n> retry passes (-1=infinity) [0]\n" | |
106 | " -R, --reverse reverse direction of some copy operations\n" | |
102 | 107 | " -s, --size=<bytes> maximum size of input data to be copied\n" |
103 | 108 | " -S, --sparse use sparse writes for output file\n" |
104 | 109 | " -t, --truncate truncate output file to zero size\n" |
106 | 111 | " -v, --verbose be verbose (a 2nd -v gives more)\n" |
107 | 112 | " -w, --ignore-write-errors make fill mode ignore write errors\n" |
108 | 113 | " -x, --extend-outfile=<bytes> extend outfile size to be at least this long\n" |
109 | "Numbers may be followed by a multiplier: s = sectors, k = kB = 10^3 = 1000,\n" | |
110 | "Ki = KiB = 2^10 = 1024, M = 10^6, Mi = 2^20, G = 10^9, Gi = 2^30, etc...\n" | |
114 | " -1, --log-rates=<file> log rates and error sizes in file\n" | |
115 | " -2, --log-reads=<file> log all read operations in file\n" | |
116 | "Numbers may be in decimal, hexadecimal or octal, and may be followed by a\n" | |
117 | "multiplier: s = sectors, k = 1000, Ki = 1024, M = 10^6, Mi = 2^20, etc...\n" | |
111 | 118 | "Time intervals have the format 1[.5][smhd] or 1/2[smhd].\n" |
112 | 119 | "\nExit status: 0 for a normal exit, 1 for environmental problems (file\n" |
113 | 120 | "not found, invalid flags, I/O errors, etc), 2 to indicate a corrupt or\n" |
233 | 240 | |
234 | 241 | Fillbook fillbook( offset, domain, logname, cluster, hardbs, |
235 | 242 | ignore_write_errors, synchronous ); |
236 | if( fillbook.domain().size() == 0 ) | |
237 | { show_error( "Nothing to do." ); return 0; } | |
238 | ||
239 | const int ides = open( iname, O_RDONLY | o_binary ); | |
243 | if( !fillbook.logfile_exists() ) return not_readable( logname ); | |
244 | if( fillbook.domain().empty() ) return empty_domain(); | |
245 | if( fillbook.read_only() ) return not_writable( logname ); | |
246 | ||
247 | const int ides = open( iname, O_RDONLY | O_BINARY ); | |
240 | 248 | if( ides < 0 ) |
241 | 249 | { show_error( "Can't open input file", errno ); return 1; } |
242 | 250 | if( !fillbook.read_buffer( ides ) ) |
243 | 251 | { show_error( "Error reading fill data from input file." ); return 1; } |
244 | 252 | |
245 | const int odes = open( oname, O_CREAT | O_WRONLY | o_binary, outmode ); | |
253 | const int odes = open( oname, O_CREAT | O_WRONLY | O_BINARY, outmode ); | |
246 | 254 | if( odes < 0 ) |
247 | 255 | { show_error( "Can't open output file", errno ); return 1; } |
248 | 256 | if( lseek( odes, 0, SEEK_SET ) ) |
256 | 264 | iname, oname, filltypes.c_str() ); |
257 | 265 | std::printf( " Maximum size to fill: %sBytes\n", |
258 | 266 | format_num( fillbook.domain().in_size() ) ); |
259 | std::printf( " Starting positions: infile = %sB", | |
260 | format_num( fillbook.domain().pos() ) ); | |
261 | std::printf( ", outfile = %sB\n", | |
267 | std::printf( " Starting positions: infile = %sB, outfile = %sB\n", | |
268 | format_num( fillbook.domain().pos() ), | |
262 | 269 | format_num( fillbook.domain().pos() + fillbook.offset() ) ); |
263 | 270 | std::printf( " Copy block size: %3d sectors\n", cluster ); |
264 | 271 | std::printf( "Sector size: %sBytes\n\n", format_num( hardbs, 99999 ) ); |
279 | 286 | return 1; |
280 | 287 | } |
281 | 288 | |
282 | const int ides = open( iname, O_RDONLY | o_binary ); | |
289 | const int ides = open( iname, O_RDONLY | O_BINARY ); | |
283 | 290 | if( ides < 0 ) |
284 | 291 | { show_error( "Can't open input file", errno ); return 1; } |
285 | 292 | const long long isize = lseek( ides, 0, SEEK_END ); |
287 | 294 | { show_error( "Input file is not seekable." ); return 1; } |
288 | 295 | |
289 | 296 | Genbook genbook( offset, isize, domain, logname, cluster, hardbs ); |
290 | if( genbook.domain().size() == 0 ) | |
291 | { show_error( "Nothing to do." ); return 0; } | |
297 | if( genbook.domain().empty() ) return empty_domain(); | |
292 | 298 | if( !genbook.blank() && genbook.current_status() != Logbook::generating ) |
293 | 299 | { |
294 | 300 | show_error( "Logfile alredy exists and is non-empty.", 0, true ); |
295 | 301 | return 1; |
296 | 302 | } |
297 | ||
298 | const int odes = open( oname, O_RDONLY | o_binary ); | |
303 | if( genbook.read_only() ) return not_writable( logname ); | |
304 | ||
305 | const int odes = open( oname, O_RDONLY | O_BINARY ); | |
299 | 306 | if( odes < 0 ) |
300 | 307 | { show_error( "Can't open output file", errno ); return 1; } |
301 | 308 | if( lseek( odes, 0, SEEK_SET ) ) |
307 | 314 | { |
308 | 315 | std::printf( "About to generate an approximate logfile for %s and %s\n", |
309 | 316 | iname, oname ); |
310 | std::printf( " Starting positions: infile = %sB", | |
311 | format_num( genbook.domain().pos() ) ); | |
312 | std::printf( ", outfile = %sB\n", | |
317 | std::printf( " Starting positions: infile = %sB, outfile = %sB\n", | |
318 | format_num( genbook.domain().pos() ), | |
313 | 319 | format_num( genbook.domain().pos() + genbook.offset() ) ); |
314 | 320 | std::printf( " Copy block size: %3d sectors\n", cluster ); |
315 | 321 | std::printf( "Sector size: %sBytes\n\n", format_num( hardbs, 99999 ) ); |
319 | 325 | |
320 | 326 | |
321 | 327 | int do_rescue( const long long offset, Domain & domain, |
322 | const Rb_options & rb_opts, | |
328 | const Domain * const test_domain, const Rb_options & rb_opts, | |
323 | 329 | const char * const iname, const char * const oname, |
324 | 330 | const char * const logname, const int cluster, |
325 | const int hardbs, const int o_direct, const int o_trunc, | |
326 | const bool preallocate, const bool reverse, | |
327 | const bool synchronous, const bool verify_input_size ) | |
328 | { | |
329 | const int ides = open( iname, O_RDONLY | o_direct | o_binary ); | |
331 | const int hardbs, const int o_trunc, | |
332 | const bool preallocate, const bool synchronous, | |
333 | const bool verify_input_size ) | |
334 | { | |
335 | const int ides = open( iname, O_RDONLY | rb_opts.o_direct | O_BINARY ); | |
330 | 336 | if( ides < 0 ) |
331 | 337 | { show_error( "Can't open input file", errno ); return 1; } |
332 | const long long isize = lseek( ides, 0, SEEK_END ); | |
338 | long long isize = lseek( ides, 0, SEEK_END ); | |
333 | 339 | if( isize < 0 ) |
334 | 340 | { show_error( "Input file is not seekable." ); return 1; } |
335 | ||
336 | Rescuebook rescuebook( offset, isize, domain, rb_opts, iname, logname, | |
337 | cluster, hardbs, synchronous ); | |
341 | if( test_domain ) | |
342 | { const long long size = test_domain->end(); | |
343 | if( isize <= 0 || isize > size ) isize = size; } | |
344 | ||
345 | Rescuebook rescuebook( offset, isize, domain, test_domain, rb_opts, iname, | |
346 | logname, cluster, hardbs, synchronous ); | |
338 | 347 | |
339 | 348 | if( verify_input_size ) |
340 | 349 | { |
352 | 361 | return 1; |
353 | 362 | } |
354 | 363 | } |
355 | if( rescuebook.domain().size() == 0 ) | |
356 | { | |
357 | if( rb_opts.complete_only ) | |
364 | if( rescuebook.domain().empty() ) | |
365 | { | |
366 | if( rescuebook.complete_only && !rescuebook.logfile_exists() ) | |
358 | 367 | { show_error( "Nothing to complete; logfile is missing or empty.", 0, true ); |
359 | 368 | return 1; } |
360 | show_error( "Nothing to do." ); return 0; | |
369 | return empty_domain(); | |
361 | 370 | } |
362 | 371 | if( o_trunc && !rescuebook.blank() ) |
363 | 372 | { |
364 | 373 | show_error( "Outfile truncation and logfile input are incompatible.", 0, true ); |
365 | 374 | return 1; |
366 | 375 | } |
367 | ||
368 | const int odes = open( oname, O_CREAT | O_WRONLY | o_trunc | o_binary, | |
376 | if( rescuebook.read_only() ) return not_writable( logname ); | |
377 | ||
378 | const int odes = open( oname, O_CREAT | O_WRONLY | o_trunc | O_BINARY, | |
369 | 379 | outmode ); |
370 | 380 | if( odes < 0 ) |
371 | 381 | { show_error( "Can't open output file", errno ); return 1; } |
372 | 382 | if( lseek( odes, 0, SEEK_SET ) ) |
373 | 383 | { show_error( "Output file is not seekable." ); return 1; } |
374 | while( preallocate ) | |
384 | if( preallocate ) | |
375 | 385 | { |
376 | 386 | #if defined _POSIX_ADVISORY_INFO && _POSIX_ADVISORY_INFO > 0 |
377 | 387 | if( posix_fallocate( odes, rescuebook.domain().pos() + rescuebook.offset(), |
378 | rescuebook.domain().size() ) == 0 ) break; | |
379 | if( errno != EINTR ) | |
388 | rescuebook.domain().size() ) != 0 ) | |
380 | 389 | { show_error( "Can't preallocate output file", errno ); return 1; } |
381 | 390 | #else |
382 | show_error( "warning: Preallocation not available." ); break; | |
391 | show_error( "warning: Preallocation not available." ); | |
383 | 392 | #endif |
384 | 393 | } |
385 | ||
386 | if( !rescuebook.update_logfile( -1, true ) ) return 1; | |
394 | if( !rate_logger.open_file() ) | |
395 | { show_error( "Can't open file for logging rates", errno ); return 1; } | |
396 | if( !read_logger.open_file() ) | |
397 | { show_error( "Can't open file for logging reads", errno ); return 1; } | |
387 | 398 | |
388 | 399 | if( verbosity >= 0 ) |
389 | 400 | std::printf( "\n\n%s %s\n", Program_name, PROGVERSION ); |
390 | 401 | if( verbosity >= 1 ) |
391 | 402 | { |
392 | std::printf( "About to copy %sBytes from %s to %s\n", | |
393 | format_num( rescuebook.domain().in_size() ), iname, oname ); | |
394 | std::printf( " Starting positions: infile = %sB", | |
395 | format_num( rescuebook.domain().pos() ) ); | |
396 | std::printf( ", outfile = %sB\n", | |
403 | if( rescuebook.domain().full() ) | |
404 | std::printf( "About to copy an unknown number of bytes from %s to %s\n", | |
405 | iname, oname ); | |
406 | else | |
407 | std::printf( "About to copy %sBytes from %s to %s\n", | |
408 | format_num( rescuebook.domain().in_size() ), iname, oname ); | |
409 | std::printf( " Starting positions: infile = %sB, outfile = %sB\n", | |
410 | format_num( rescuebook.domain().pos() ), | |
397 | 411 | format_num( rescuebook.domain().pos() + rescuebook.offset() ) ); |
398 | 412 | std::printf( " Copy block size: %3d sectors", cluster ); |
399 | std::printf( " Initial skip size: %d sectors\n", | |
400 | rb_opts.skipbs / hardbs ); | |
413 | if( rescuebook.skipbs > 0 ) | |
414 | std::printf( " Initial skip size: %d sectors\n", | |
415 | rescuebook.skipbs / hardbs ); | |
416 | else | |
417 | std::printf( " Skipping disabled\n" ); | |
401 | 418 | std::printf( "Sector size: %sBytes\n", format_num( hardbs, 99999 ) ); |
402 | 419 | if( verbosity >= 2 ) |
403 | 420 | { |
404 | 421 | bool nl = false; |
405 | if( rb_opts.max_error_rate >= 0 ) | |
406 | { nl = true; std::printf( "Max error rate: %8sB/s ", | |
407 | format_num( rb_opts.max_error_rate, 99999 ) ); } | |
408 | if( rb_opts.max_errors >= 0 ) | |
422 | if( rescuebook.max_error_rate >= 0 ) | |
423 | { nl = true; std::printf( "Max error rate: %6sB/s ", | |
424 | format_num( rescuebook.max_error_rate, 99999 ) ); } | |
425 | if( rescuebook.max_errors >= 0 ) | |
409 | 426 | { |
410 | 427 | nl = true; |
411 | std::printf( "Max %serrors: %d ", | |
412 | rb_opts.new_errors_only ? "new " : "", rb_opts.max_errors ); | |
428 | std::printf( "Max %serrors: %d ", rescuebook.new_errors_only ? | |
429 | "new " : "", rescuebook.max_errors ); | |
413 | 430 | } |
414 | if( rb_opts.max_retries >= 0 ) | |
415 | { nl = true; std::printf( "Max retries: %d ", rb_opts.max_retries ); } | |
431 | if( rescuebook.max_retries >= 0 ) | |
432 | { nl = true; | |
433 | std::printf( "Max retry passes: %d ", rescuebook.max_retries ); } | |
416 | 434 | if( nl ) { nl = false; std::printf( "\n" ); } |
417 | 435 | |
418 | if( rb_opts.min_read_rate >= 0 ) | |
419 | { nl = true; std::printf( "Min read rate: %8sB/s ", | |
420 | format_num( rb_opts.min_read_rate, 99999 ) ); } | |
421 | if( rb_opts.timeout >= 0 ) | |
436 | if( rescuebook.min_read_rate == 0 ) | |
437 | { nl = true; std::printf( "Min read rate: auto " ); } | |
438 | else if( rescuebook.min_read_rate > 0 ) | |
439 | { nl = true; std::printf( "Min read rate: %6sB/s ", | |
440 | format_num( rescuebook.min_read_rate, 99999 ) ); } | |
441 | if( rescuebook.timeout >= 0 ) | |
422 | 442 | { nl = true; std::printf( "Max time since last successful read: %s", |
423 | format_time( rb_opts.timeout ) ); } | |
443 | format_time( rescuebook.timeout ) ); } | |
424 | 444 | if( nl ) { nl = false; std::printf( "\n" ); } |
425 | 445 | |
426 | std::printf( "Direct: %s ", o_direct ? "yes" : "no" ); | |
427 | std::printf( "Sparse: %s ", rb_opts.sparse ? "yes" : "no" ); | |
428 | std::printf( "Split: %s ", !rb_opts.nosplit ? "yes" : "no" ); | |
446 | std::printf( "Direct: %s ", rescuebook.o_direct ? "yes" : "no" ); | |
447 | std::printf( "Sparse: %s ", rescuebook.sparse ? "yes" : "no" ); | |
448 | std::printf( "Split: %s ", !rescuebook.nosplit ? "yes" : "no" ); | |
449 | std::printf( "Trim: %s ", !rescuebook.notrim ? "yes" : "no" ); | |
429 | 450 | std::printf( "Truncate: %s ", o_trunc ? "yes" : "no" ); |
430 | if( rb_opts.complete_only ) std::printf( "Complete only" ); | |
431 | 451 | std::printf( "\n" ); |
432 | if( reverse ) std::printf( "Reverse mode\n" ); | |
452 | if( rescuebook.complete_only ) | |
453 | { nl = true; std::printf( "Complete only " ); } | |
454 | if( rescuebook.reverse ) { nl = true; std::printf( "Reverse mode" ); } | |
455 | if( nl ) { nl = false; std::printf( "\n" ); } | |
433 | 456 | } |
434 | 457 | std::printf( "\n" ); |
435 | 458 | } |
436 | return rescuebook.do_rescue( ides, odes, reverse ); | |
459 | return rescuebook.do_rescue( ides, odes ); | |
437 | 460 | } |
438 | 461 | |
439 | 462 | } // end namespace |
440 | 463 | |
441 | 464 | |
442 | 465 | #include "main_common.cc" |
466 | ||
467 | ||
468 | namespace { | |
469 | ||
470 | void parse_skipbs( const char * const arg, Rb_options & rb_opts, | |
471 | const int hardbs ) | |
472 | { | |
473 | const char * const arg2 = std::strchr( arg, ',' ); | |
474 | ||
475 | if( !arg2 || arg2 != arg ) | |
476 | rb_opts.skipbs = getnum( arg, hardbs, 0, Rb_options::max_max_skipbs, true ); | |
477 | if( arg2 ) | |
478 | rb_opts.max_skipbs = getnum( arg2 + 1, hardbs, Rb_options::default_skipbs, | |
479 | Rb_options::max_max_skipbs ); | |
480 | if( rb_opts.skipbs > 0 && rb_opts.skipbs < Rb_options::default_skipbs ) | |
481 | { | |
482 | show_error( "Minimum initial skip size is 64KiB." ); | |
483 | std::exit( 1 ); | |
484 | } | |
485 | if( rb_opts.skipbs > rb_opts.max_skipbs ) | |
486 | { | |
487 | show_error( "'initial skip size' is larger than 'max skip size'." ); | |
488 | std::exit( 1 ); | |
489 | } | |
490 | } | |
491 | ||
492 | } // end namespace | |
493 | ||
494 | ||
495 | bool Rescuebook::reopen_infile() | |
496 | { | |
497 | if( ides_ >= 0 ) close( ides_ ); | |
498 | ides_ = open( iname_, O_RDONLY | o_direct | O_BINARY ); | |
499 | if( ides_ < 0 ) | |
500 | { final_msg( "Can't reopen input file", errno ); return false; } | |
501 | const long long isize = lseek( ides_, 0, SEEK_END ); | |
502 | if( isize < 0 ) | |
503 | { final_msg( "Input file has become not seekable", errno ); return false; } | |
504 | return true; | |
505 | } | |
443 | 506 | |
444 | 507 | |
445 | 508 | int main( const int argc, const char * const argv[] ) |
448 | 511 | long long opos = -1; |
449 | 512 | long long max_size = -1; |
450 | 513 | const char * domain_logfile_name = 0; |
514 | const char * test_mode_logfile_name = 0; | |
451 | 515 | const int cluster_bytes = 65536; |
452 | 516 | const int default_hardbs = 512; |
453 | const int max_hardbs = Rb_options::max_skipbs; | |
517 | const int max_hardbs = Rb_options::max_max_skipbs; | |
454 | 518 | int cluster = 0; |
455 | 519 | int hardbs = default_hardbs; |
456 | int o_direct = 0; | |
457 | 520 | int o_trunc = 0; |
458 | 521 | Mode program_mode = m_none; |
459 | struct Rb_options rb_opts; | |
522 | Rb_options rb_opts; | |
460 | 523 | bool force = false; |
461 | 524 | bool ignore_write_errors = false; |
525 | bool loose = false; | |
462 | 526 | bool preallocate = false; |
463 | bool reverse = false; | |
464 | 527 | bool synchronous = false; |
465 | 528 | bool verify_input_size = false; |
466 | 529 | std::string filltypes; |
468 | 531 | command_line = argv[0]; |
469 | 532 | for( int i = 1; i < argc; ++i ) |
470 | 533 | { command_line += ' '; command_line += argv[i]; } |
534 | initial_time_ = std::time( 0 ); | |
471 | 535 | |
472 | 536 | const Arg_parser::Option options[] = |
473 | 537 | { |
538 | { '1', "log-rates", Arg_parser::yes }, | |
539 | { '2', "log-reads", Arg_parser::yes }, | |
474 | 540 | { 'a', "min-read-rate", Arg_parser::yes }, |
475 | 541 | { 'A', "try-again", Arg_parser::no }, |
476 | 542 | { 'b', "block-size", Arg_parser::yes }, |
484 | 550 | { 'E', "max-error-rate", Arg_parser::yes }, |
485 | 551 | { 'f', "force", Arg_parser::no }, |
486 | 552 | { 'F', "fill-mode", Arg_parser::yes }, |
487 | { 'g', "generate-mode", Arg_parser::no }, | |
553 | { 'G', "generate-mode", Arg_parser::no }, | |
488 | 554 | { 'h', "help", Arg_parser::no }, |
555 | { 'H', "test-mode", Arg_parser::yes }, | |
489 | 556 | { 'i', "input-position", Arg_parser::yes }, |
490 | 557 | { 'I', "verify-input-size", Arg_parser::no }, |
491 | 558 | { 'K', "skip-size", Arg_parser::yes }, |
492 | 559 | { 'l', "logfile-size", Arg_parser::yes }, |
560 | { 'L', "loose-domain", Arg_parser::no }, | |
493 | 561 | { 'm', "domain-logfile", Arg_parser::yes }, |
494 | 562 | { 'M', "retrim", Arg_parser::no }, |
495 | 563 | { 'n', "no-split", Arg_parser::no }, |
564 | { 'N', "no-trim", Arg_parser::no }, | |
496 | 565 | { 'o', "output-position", Arg_parser::yes }, |
566 | { 'O', "reopen-on-error", Arg_parser::no }, | |
497 | 567 | { 'p', "preallocate", Arg_parser::no }, |
498 | 568 | { 'q', "quiet", Arg_parser::no }, |
499 | { 'r', "retries", Arg_parser::yes }, | |
500 | { 'r', "max-retries", Arg_parser::yes }, | |
569 | { 'r', "retry-passes", Arg_parser::yes }, | |
501 | 570 | { 'R', "reverse", Arg_parser::no }, |
502 | 571 | { 's', "size", Arg_parser::yes }, |
503 | 572 | { 's', "max-size", Arg_parser::yes }, |
522 | 591 | const char * const arg = parser.argument( argind ).c_str(); |
523 | 592 | switch( code ) |
524 | 593 | { |
594 | case '1': rate_logger.set_filename( arg ); break; | |
595 | case '2': read_logger.set_filename( arg ); break; | |
525 | 596 | case 'a': rb_opts.min_read_rate = getnum( arg, hardbs, 0 ); break; |
526 | 597 | case 'A': rb_opts.try_again = true; break; |
527 | 598 | case 'b': hardbs = getnum( arg, 0, 1, max_hardbs ); break; |
528 | 599 | case 'B': format_num( 0, 0, -1 ); break; // set binary prefixes |
529 | 600 | case 'c': cluster = getnum( arg, 0, 1, INT_MAX ); break; |
530 | 601 | case 'C': rb_opts.complete_only = true; break; |
531 | case 'd': o_direct = O_DIRECT; | |
532 | if( !o_direct ) | |
602 | case 'd': rb_opts.o_direct = O_DIRECT; | |
603 | if( rb_opts.o_direct == 0 ) | |
533 | 604 | { show_error( "Direct disc access not available." ); return 1; } |
534 | 605 | break; |
535 | 606 | case 'D': synchronous = true; break; |
539 | 610 | case 'f': force = true; break; |
540 | 611 | case 'F': set_mode( program_mode, m_fill ); filltypes = arg; |
541 | 612 | check_types( filltypes, "fill-mode" ); break; |
542 | case 'g': set_mode( program_mode, m_generate ); break; | |
613 | case 'G': set_mode( program_mode, m_generate ); break; | |
543 | 614 | case 'h': show_help( cluster_bytes / default_hardbs, default_hardbs, |
544 | 615 | Rb_options::default_skipbs ); |
545 | 616 | return 0; |
617 | case 'H': set_name( &test_mode_logfile_name, arg, code ); break; | |
546 | 618 | case 'i': ipos = getnum( arg, hardbs, 0 ); break; |
547 | 619 | case 'I': verify_input_size = true; break; |
548 | case 'K': rb_opts.skipbs = getnum( arg, hardbs, Rb_options::default_skipbs, | |
549 | Rb_options::max_skipbs ); break; | |
620 | case 'K': parse_skipbs( arg, rb_opts, hardbs ); break; | |
550 | 621 | case 'l': rb_opts.max_logfile_size = getnum( arg, 0, 1, INT_MAX ); break; |
551 | case 'm': set_name( &domain_logfile_name, arg ); break; | |
622 | case 'L': loose = true; break; | |
623 | case 'm': set_name( &domain_logfile_name, arg, code ); break; | |
552 | 624 | case 'M': rb_opts.retrim = true; break; |
553 | 625 | case 'n': rb_opts.nosplit = true; break; |
626 | case 'N': rb_opts.notrim = true; break; | |
554 | 627 | case 'o': opos = getnum( arg, hardbs, 0 ); break; |
628 | case 'O': rb_opts.reopen_on_error = true; break; | |
555 | 629 | case 'p': preallocate = true; break; |
556 | 630 | case 'q': verbosity = -1; break; |
557 | 631 | case 'r': rb_opts.max_retries = getnum( arg, 0, -1, INT_MAX ); break; |
558 | case 'R': reverse = true; break; | |
632 | case 'R': rb_opts.reverse = true; break; | |
559 | 633 | case 's': max_size = getnum( arg, hardbs, -1 ); break; |
560 | 634 | case 'S': rb_opts.sparse = true; break; |
561 | 635 | case 't': o_trunc = O_TRUNC; break; |
564 | 638 | case 'V': show_version(); return 0; |
565 | 639 | case 'w': ignore_write_errors = true; break; |
566 | 640 | case 'x': rb_opts.min_outfile_size = getnum( arg, hardbs, 1 ); break; |
567 | default : internal_error( "uncaught option" ); | |
641 | default : internal_error( "uncaught option." ); | |
568 | 642 | } |
569 | 643 | } // end process options |
570 | 644 | |
573 | 647 | if( cluster >= INT_MAX / hardbs ) cluster = ( INT_MAX / hardbs ) - 1; |
574 | 648 | if( cluster < 1 ) cluster = cluster_bytes / hardbs; |
575 | 649 | if( cluster < 1 ) cluster = 1; |
576 | if( rb_opts.skipbs < hardbs ) | |
577 | rb_opts.skipbs = hardbs; | |
578 | else // make multiple of hardbs | |
579 | rb_opts.skipbs = round_up( rb_opts.skipbs, hardbs ); | |
580 | 650 | |
581 | 651 | const char *iname = 0, *oname = 0, *logname = 0; |
582 | 652 | if( argind < parser.arguments() ) iname = parser.argument( argind++ ).c_str(); |
591 | 661 | program_mode == m_generate, preallocate, rb_opts.sparse ) ) |
592 | 662 | return 1; |
593 | 663 | |
594 | Domain domain( ipos, max_size, domain_logfile_name ); | |
664 | Domain domain( ipos, max_size, domain_logfile_name, loose ); | |
595 | 665 | |
596 | 666 | switch( program_mode ) |
597 | 667 | { |
598 | 668 | case m_fill: |
599 | if( rb_opts != Rb_options() || o_direct || | |
600 | verify_input_size || preallocate || reverse || o_trunc ) | |
601 | show_error( "warning: Options -aCdeEIMnOprRStTx are ignored in fill mode." ); | |
669 | if( rb_opts != Rb_options() || test_mode_logfile_name || | |
670 | verify_input_size || preallocate || o_trunc ) | |
671 | show_error( "warning: Options -aACdeEHIKlMnOprRStTx are ignored in fill mode." ); | |
602 | 672 | return do_fill( opos - ipos, domain, iname, oname, logname, cluster, |
603 | 673 | hardbs, filltypes, ignore_write_errors, synchronous ); |
604 | 674 | case m_generate: |
605 | if( rb_opts != Rb_options() || o_direct || synchronous || | |
606 | verify_input_size || preallocate || reverse || o_trunc || | |
607 | ignore_write_errors ) | |
608 | show_error( "warning: Options -aCdDeEIMnOprRStTwx are ignored in generate mode." ); | |
675 | if( rb_opts != Rb_options() || synchronous || test_mode_logfile_name || | |
676 | verify_input_size || preallocate || o_trunc || ignore_write_errors ) | |
677 | show_error( "warning: Options -aACdDeEHIKlMnOprRStTwx are ignored in generate mode." ); | |
609 | 678 | return do_generate( opos - ipos, domain, iname, oname, logname, |
610 | 679 | cluster, hardbs ); |
611 | 680 | case m_none: |
681 | { | |
612 | 682 | if( ignore_write_errors ) |
613 | 683 | { show_error( "Option '-w' is incompatible with rescue mode.", 0, true ); |
614 | 684 | return 1; } |
615 | return do_rescue( opos - ipos, domain, rb_opts, iname, oname, logname, | |
616 | cluster, hardbs, o_direct, o_trunc, preallocate, | |
617 | reverse, synchronous, verify_input_size ); | |
618 | } | |
619 | } | |
685 | const Domain * const test_domain = test_mode_logfile_name ? | |
686 | new Domain( 0, -1, test_mode_logfile_name, loose ) : 0; | |
687 | int tmp = do_rescue( opos - ipos, domain, test_domain, rb_opts, iname, | |
688 | oname, logname, cluster, hardbs, o_trunc, | |
689 | preallocate, synchronous, verify_input_size ); | |
690 | if( test_domain ) delete test_domain; | |
691 | return tmp; | |
692 | } | |
693 | } | |
694 | } |
0 | 0 | /* GNU ddrescue - Data recovery tool |
1 | 1 | Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, |
2 | 2013 Antonio Diaz Diaz. | |
2 | 2013, 2014 Antonio Diaz Diaz. | |
3 | 3 | |
4 | 4 | This program is free software: you can redistribute it and/or modify |
5 | 5 | it under the terms of the GNU General Public License as published by |
17 | 17 | |
18 | 18 | namespace { |
19 | 19 | |
20 | const char * const program_year = "2013"; | |
20 | const char * const program_year = "2014"; | |
21 | 21 | std::string command_line; |
22 | long initial_time_; | |
22 | 23 | |
23 | 24 | |
24 | 25 | void show_version() |
25 | 26 | { |
26 | std::printf( "%s %s\n", Program_name, PROGVERSION ); | |
27 | std::printf( "GNU %s %s\n", program_name, PROGVERSION ); | |
27 | 28 | std::printf( "Copyright (C) %s Antonio Diaz Diaz.\n", program_year ); |
28 | 29 | std::printf( "License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n" |
29 | 30 | "This is free software: you are free to change and redistribute it.\n" |
33 | 34 | |
34 | 35 | long long getnum( const char * const ptr, const int hardbs, |
35 | 36 | const long long min = LLONG_MIN + 1, |
36 | const long long max = LLONG_MAX ) | |
37 | const long long max = LLONG_MAX, const bool comma = false ) | |
37 | 38 | { |
38 | 39 | errno = 0; |
39 | char *tail; | |
40 | char * tail; | |
40 | 41 | long long result = strtoll( ptr, &tail, 0 ); |
41 | 42 | if( tail == ptr ) |
42 | 43 | { |
52 | 53 | switch( tail[0] ) |
53 | 54 | { |
54 | 55 | case ' ': break; |
56 | case ',': if( !comma ) { bad_multiplier = true; } break; | |
55 | 57 | case 'b': |
56 | 58 | case 's': if( hardbs > 0 ) { factor = hardbs; exponent = 1; } |
57 | 59 | else bad_multiplier = true; |
117 | 119 | } |
118 | 120 | |
119 | 121 | |
120 | void set_name( const char ** domain_logfile_name, const char * new_name ) | |
121 | { | |
122 | if( *domain_logfile_name ) | |
123 | { | |
124 | show_error( "Only one domain logfile can be specified.", 0, true ); | |
125 | std::exit( 1 ); | |
126 | } | |
127 | *domain_logfile_name = new_name; | |
122 | void set_name( const char ** name, const char * new_name, const char opt ) | |
123 | { | |
124 | if( *name ) | |
125 | { | |
126 | std::string msg( "Option '- ' can be specified only once." ); | |
127 | msg[9] = opt; | |
128 | show_error( msg.c_str(), 0, true ); | |
129 | std::exit( 1 ); | |
130 | } | |
131 | *name = new_name; | |
132 | } | |
133 | ||
134 | ||
135 | const char * get_timestamp( const long t = 0 ) | |
136 | { | |
137 | static char buf[80]; | |
138 | const time_t tt = t ? t : std::time( 0 ); | |
139 | const struct tm * const tm = std::localtime( &tt ); | |
140 | if( !tm || std::strftime( buf, sizeof buf, "%Y-%m-%d %H:%M:%S", tm ) == 0 ) | |
141 | buf[0] = 0; | |
142 | return buf; | |
128 | 143 | } |
129 | 144 | |
130 | 145 | } // end namespace |
154 | 169 | void internal_error( const char * const msg ) |
155 | 170 | { |
156 | 171 | if( verbosity >= 0 ) |
157 | std::fprintf( stderr, "%s: internal error: %s.\n", program_name, msg ); | |
172 | std::fprintf( stderr, "%s: internal error: %s\n", program_name, msg ); | |
158 | 173 | std::exit( 3 ); |
159 | 174 | } |
160 | 175 | |
161 | 176 | |
162 | void write_logfile_header( FILE * const f ) | |
163 | { | |
164 | std::fprintf( f, "# Rescue Logfile. Created by %s version %s\n", | |
165 | Program_name, PROGVERSION ); | |
166 | std::fprintf( f, "# Command line: %s\n", command_line.c_str() ); | |
177 | int empty_domain() { show_error( "Empty domain." ); return 0; } | |
178 | ||
179 | ||
180 | int not_readable( const char * const logname ) | |
181 | { | |
182 | char buf[80]; | |
183 | snprintf( buf, sizeof buf, | |
184 | "Logfile '%s' does not exist or is not readable.", logname ); | |
185 | show_error( buf ); | |
186 | return 1; | |
187 | } | |
188 | ||
189 | ||
190 | int not_writable( const char * const logname ) | |
191 | { | |
192 | char buf[80]; | |
193 | snprintf( buf, sizeof buf, "Logfile '%s' is not writable.", logname ); | |
194 | show_error( buf ); | |
195 | return 1; | |
196 | } | |
197 | ||
198 | ||
199 | long initial_time() { return initial_time_; } | |
200 | ||
201 | ||
202 | bool write_logfile_header( FILE * const f, const char * const logtype ) | |
203 | { | |
204 | static std::string timestamp; | |
205 | ||
206 | if( timestamp.empty() ) timestamp = get_timestamp( initial_time_ ); | |
207 | return ( std::fprintf( f, "# %s Logfile. Created by %s version %s\n" | |
208 | "# Command line: %s\n" | |
209 | "# Start time: %s\n", | |
210 | logtype, Program_name, PROGVERSION, command_line.c_str(), | |
211 | timestamp.c_str() ) >= 0 ); | |
212 | } | |
213 | ||
214 | ||
215 | bool write_timestamp( FILE * const f ) | |
216 | { | |
217 | const char * const timestamp = get_timestamp(); | |
218 | ||
219 | return ( !timestamp || !timestamp[0] || | |
220 | std::fprintf( f, "# Current time: %s\n", timestamp ) >= 0 ); | |
221 | } | |
222 | ||
223 | ||
224 | bool write_final_timestamp( FILE * const f ) | |
225 | { | |
226 | static std::string timestamp; | |
227 | ||
228 | if( timestamp.empty() ) timestamp = get_timestamp(); | |
229 | return ( std::fprintf( f, "# End time: %s\n", timestamp.c_str() ) >= 0 ); | |
167 | 230 | } |
168 | 231 | |
169 | 232 | |
174 | 237 | { "k", "M", "G", "T", "P", "E", "Z", "Y" }; |
175 | 238 | const char * const binary_prefix[8] = |
176 | 239 | { "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi", "Yi" }; |
240 | enum { buffers = 8, bufsize = 16 }; | |
241 | static char buffer[buffers][bufsize]; // circle of static buffers for printf | |
242 | static int current = 0; | |
177 | 243 | static bool si = true; |
178 | static char buf[16]; | |
179 | 244 | |
180 | 245 | if( set_prefix ) si = ( set_prefix > 0 ); |
181 | const int factor = ( si ? 1000 : 1024 ); | |
182 | const char * const * prefix = ( si ? si_prefix : binary_prefix ); | |
246 | const int factor = si ? 1000 : 1024; | |
247 | char * const buf = buffer[current++]; current %= buffers; | |
248 | const char * const * prefix = si ? si_prefix : binary_prefix; | |
183 | 249 | const char * p = ""; |
184 | 250 | limit = std::max( 999LL, std::min( 999999LL, limit ) ); |
185 | 251 | |
186 | 252 | for( int i = 0; i < 8 && llabs( num ) > limit; ++i ) |
187 | 253 | { num /= factor; p = prefix[i]; } |
188 | snprintf( buf, sizeof buf, "%lld %s", num, p ); | |
254 | snprintf( buf, bufsize, "%lld %s", num, p ); | |
189 | 255 | return buf; |
190 | 256 | } |
0 | 0 | /* Rational - Rational number class with overflow detection. |
1 | Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 | |
2 | Antonio Diaz Diaz. | |
1 | Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, | |
2 | 2014 Antonio Diaz Diaz. | |
3 | 3 | |
4 | 4 | This library is free software: you have unlimited permission to |
5 | 5 | copy, distribute and modify it. |
0 | 0 | /* Rational - Rational number class with overflow detection. |
1 | Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 | |
2 | Antonio Diaz Diaz. | |
1 | Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, | |
2 | 2014 Antonio Diaz Diaz. | |
3 | 3 | |
4 | 4 | This library is free software: you have unlimited permission to |
5 | 5 | copy, distribute and modify it. |
0 | 0 | /* GNU ddrescue - Data recovery tool |
1 | 1 | Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, |
2 | 2013 Antonio Diaz Diaz. | |
2 | 2013, 2014 Antonio Diaz Diaz. | |
3 | 3 | |
4 | 4 | This program is free software: you can redistribute it and/or modify |
5 | 5 | it under the terms of the GNU General Public License as published by |
17 | 17 | |
18 | 18 | #define _FILE_OFFSET_BITS 64 |
19 | 19 | |
20 | #include <algorithm> | |
20 | 21 | #include <cerrno> |
21 | 22 | #include <climits> |
22 | 23 | #include <cstdio> |
23 | 24 | #include <cstring> |
25 | #include <ctime> | |
24 | 26 | #include <string> |
25 | 27 | #include <vector> |
26 | 28 | #include <stdint.h> |
28 | 30 | |
29 | 31 | #include "block.h" |
30 | 32 | #include "ddrescue.h" |
31 | ||
32 | ||
33 | namespace { | |
34 | ||
35 | int calculate_max_skip_size( const long long isize, | |
36 | const int hardbs, const int skipbs ) | |
37 | { | |
38 | int skip; | |
39 | ||
40 | if( isize > 0 && isize / 100 < Rb_options::max_skipbs ) skip = isize / 100; | |
41 | else skip = Rb_options::max_skipbs; | |
42 | if( skip < hardbs || skip < skipbs ) skip = skipbs; | |
43 | return round_up( skip, hardbs ); | |
44 | } | |
45 | ||
46 | } // end namespace | |
33 | #include "loggers.h" | |
47 | 34 | |
48 | 35 | |
49 | 36 | void Rescuebook::count_errors() |
68 | 55 | } |
69 | 56 | |
70 | 57 | |
58 | // Return values: 1 error, 0 OK. | |
59 | // | |
60 | int Rescuebook::update( const Block & b, const Sblock::Status st, | |
61 | const int copied_size, const int error_size ) | |
62 | { | |
63 | int retval = 0; | |
64 | if( copied_size + error_size < b.size() ) // EOF | |
65 | { | |
66 | if( complete_only ) truncate_domain( b.pos() + copied_size + error_size ); | |
67 | else if( !truncate_vector( b.pos() + copied_size + error_size ) ) | |
68 | { final_msg( "EOF found before end of logfile" ); retval = 1; } | |
69 | } | |
70 | if( copied_size > 0 ) | |
71 | { | |
72 | errors += change_chunk_status( Block( b.pos(), copied_size ), | |
73 | Sblock::finished, domain() ); | |
74 | recsize += copied_size; | |
75 | } | |
76 | if( error_size > 0 ) | |
77 | { | |
78 | errors += change_chunk_status( Block( b.pos() + copied_size, error_size ), | |
79 | ( error_size > hardbs() ) ? st : Sblock::bad_sector, domain() ); | |
80 | if( access_works && access( iname_, F_OK ) != 0 ) | |
81 | { final_msg( "Input file disappeared", errno ); retval = 1; } | |
82 | } | |
83 | return retval; | |
84 | } | |
85 | ||
86 | ||
71 | 87 | // Return values: 1 I/O error, 0 OK, -1 interrupted. |
72 | 88 | // |
73 | int Rescuebook::copy_and_update( const Block & b, const Sblock::Status st, | |
74 | int & copied_size, int & error_size, | |
75 | const char * const msg, bool & first_post, | |
76 | const bool forward ) | |
89 | int Rescuebook::copy_and_update( const Block & b, int & error_size, | |
90 | const char * const msg, const bool forward ) | |
77 | 91 | { |
78 | 92 | current_pos( forward ? b.pos() : b.end() ); |
79 | show_status( current_pos(), msg, first_post ); | |
80 | first_post = false; | |
93 | show_status( b.pos(), msg ); | |
81 | 94 | if( errors_or_timeout() ) return 1; |
82 | 95 | if( interrupted() ) return -1; |
96 | int copied_size = 0; | |
83 | 97 | int retval = copy_block( b, copied_size, error_size ); |
84 | if( !retval ) | |
85 | { | |
86 | if( copied_size + error_size < b.size() ) // EOF | |
87 | { | |
88 | if( complete_only ) truncate_domain( b.pos() + copied_size + error_size ); | |
89 | else if( !truncate_vector( b.pos() + copied_size + error_size ) ) | |
90 | { | |
91 | final_msg( "EOF found before end of logfile" ); | |
92 | retval = 1; | |
93 | } | |
94 | } | |
95 | if( copied_size > 0 ) | |
96 | { | |
97 | errors += change_chunk_status( Block( b.pos(), copied_size ), Sblock::finished ); | |
98 | recsize += copied_size; | |
99 | } | |
100 | if( error_size > 0 ) | |
101 | { | |
102 | errors += change_chunk_status( Block( b.pos() + copied_size, error_size ), | |
103 | ( error_size > hardbs() ) ? st : Sblock::bad_sector ); | |
104 | if( iname_ && access( iname_, F_OK ) != 0 ) | |
105 | { | |
106 | final_msg( "input file disappeared" ); final_errno( errno ); | |
107 | retval = 1; | |
108 | } | |
109 | } | |
110 | } | |
98 | if( retval == 0 ) | |
99 | { | |
100 | if( copied_size > 0 ) errsize -= copied_size; | |
101 | retval = update( b, Sblock::bad_sector, copied_size, error_size ); | |
102 | } | |
103 | return retval; | |
104 | } | |
105 | ||
106 | ||
107 | // Return values: 1 I/O error, 0 OK, -1 interrupted. | |
108 | // | |
109 | int Rescuebook::copy_and_update2( const Block & b, int & copied_size, | |
110 | int & error_size, const char * const msg, | |
111 | const bool forward, const bool small_try ) | |
112 | { | |
113 | if( first_post ) read_logger.print_msg( t1 - t0, msg ); | |
114 | current_pos( forward ? b.pos() : b.end() ); | |
115 | current_status( copying, msg ); | |
116 | show_status( b.pos(), msg ); | |
117 | if( errors_or_timeout() ) return 1; | |
118 | if( interrupted() ) return -1; | |
119 | if( small_try && b.size() > hardbs() ) // try one sector first | |
120 | { | |
121 | const Block b1( b.pos(), hardbs() ); | |
122 | const Block b2( b1.end(), b.size() - b1.size() ); | |
123 | int retval = copy_block( b1, copied_size, error_size ); | |
124 | if( retval ) return retval; | |
125 | if( copied_size != b1.size() ) // mark full block on error | |
126 | { error_size += b2.size(); | |
127 | return update( b, Sblock::non_trimmed, copied_size, error_size ); } | |
128 | retval = copy_block( b2, copied_size, error_size ); | |
129 | if( retval ) return update( b1, Sblock::bad_sector, b1.size(), 0 ); | |
130 | copied_size += b1.size(); | |
131 | return update( b, Sblock::non_trimmed, copied_size, error_size ); | |
132 | } | |
133 | int retval = copy_block( b, copied_size, error_size ); | |
134 | if( retval == 0 ) | |
135 | retval = update( b, Sblock::non_trimmed, copied_size, error_size ); | |
111 | 136 | return retval; |
112 | 137 | } |
113 | 138 | |
117 | 142 | // |
118 | 143 | int Rescuebook::copy_non_tried() |
119 | 144 | { |
120 | bool first_post = true; | |
121 | ||
122 | for( bool first_pass = true; ; first_pass = false ) | |
123 | { | |
124 | long long pos = 0; | |
125 | int skip_size = 0; // size to skip on error | |
126 | bool block_found = false; | |
127 | ||
128 | if( first_pass && current_status() == copying && | |
129 | domain().includes( current_pos() ) ) | |
130 | { | |
131 | Block b( current_pos(), 1 ); | |
132 | find_chunk( b, Sblock::non_tried ); | |
133 | if( b.size() > 0 ) pos = b.pos(); // resume | |
134 | } | |
135 | ||
136 | while( pos >= 0 ) | |
137 | { | |
138 | const int alignment = ( skip_size ? hardbs() : softbs() ); | |
139 | Block b( pos, alignment ); | |
140 | find_chunk( b, Sblock::non_tried, alignment ); | |
141 | if( b.size() <= 0 ) break; | |
142 | if( pos != b.pos() ) skip_size = 0; // reset size on block change | |
143 | pos = b.end(); | |
144 | current_status( copying ); | |
145 | block_found = true; | |
146 | const Sblock::Status st = | |
147 | ( ( b.size() > hardbs() ) ? Sblock::non_trimmed : Sblock::bad_sector ); | |
148 | int copied_size = 0, error_size = 0; | |
149 | const int retval = copy_and_update( b, st, copied_size, error_size, | |
150 | "Copying non-tried blocks...", | |
151 | first_post, true ); | |
152 | if( error_size > 0 ) errsize += error_size; | |
153 | else if( skip_size > 0 && copied_size > 0 ) | |
154 | { skip_size -= copied_size; if( skip_size < 0 ) skip_size = 0; } | |
155 | if( retval ) return retval; | |
156 | if( error_size > 0 ) error_rate += error_size; | |
157 | update_rates(); | |
158 | if( error_size > 0 || slow_read() ) | |
145 | char msgbuf[80] = "Copying non-tried blocks... Pass "; | |
146 | const int msglen = std::strlen( msgbuf ); | |
147 | bool forward = !reverse; | |
148 | ||
149 | for( int pass = 1; ; ++pass ) | |
150 | { | |
151 | first_post = true; | |
152 | snprintf( msgbuf + msglen, ( sizeof msgbuf ) - msglen, "%d %s", | |
153 | pass, forward ? "(forwards)" : "(backwards)" ); | |
154 | int retval = forward ? fcopy_non_tried( msgbuf, pass ) : | |
155 | rcopy_non_tried( msgbuf, pass ); | |
156 | if( retval != -3 ) return retval; | |
157 | reduce_min_read_rate(); | |
158 | forward = !forward; | |
159 | } | |
160 | } | |
161 | ||
162 | ||
163 | // Return values: 1 I/O error, 0 OK, -1 interrupted, -2 logfile error. | |
164 | // Read forwards the non-damaged part of the domain, skipping over the | |
165 | // damaged areas. | |
166 | // | |
167 | int Rescuebook::fcopy_non_tried( const char * const msg, const int pass ) | |
168 | { | |
169 | long long pos = 0; | |
170 | int skip_size = 0; // size to skip on error if skipbs > 0 | |
171 | bool block_found = false; | |
172 | ||
173 | if( pass == 1 && current_status() == copying && | |
174 | domain().includes( current_pos() ) ) | |
175 | { | |
176 | Block b( current_pos(), 1 ); | |
177 | find_chunk( b, Sblock::non_tried, domain(), hardbs() ); | |
178 | if( b.size() > 0 ) pos = b.pos(); // resume | |
179 | } | |
180 | ||
181 | while( pos >= 0 ) | |
182 | { | |
183 | Block b( pos, softbs() ); | |
184 | find_chunk( b, Sblock::non_tried, domain(), softbs() ); | |
185 | if( b.size() <= 0 ) break; | |
186 | if( pos != b.pos() ) skip_size = 0; // reset size on block change | |
187 | pos = b.end(); | |
188 | block_found = true; | |
189 | int copied_size = 0, error_size = 0; | |
190 | const int retval = copy_and_update2( b, copied_size, error_size, | |
191 | msg, true, skip_size > 0 ); | |
192 | if( error_size > 0 ) { errsize += error_size; error_rate += error_size; } | |
193 | if( retval ) return retval; | |
194 | update_rates(); | |
195 | if( ( error_size > 0 || slow_read() ) && pos >= 0 ) | |
196 | { | |
197 | if( skip_size > 0 ) // do not skip until 2nd error | |
159 | 198 | { |
160 | if( pos >= 0 && skip_size > 0 ) | |
199 | if( reopen_on_error && !reopen_infile() ) return 1; | |
200 | if( skipbs > 0 && pass <= 2 ) // do not skip if skipbs == 0 | |
161 | 201 | { |
162 | b.assign( pos, skip_size ); b.fix_size(); | |
163 | find_chunk( b, Sblock::non_tried ); | |
202 | b.assign( pos, skip_size ); | |
203 | find_chunk( b, Sblock::non_tried, domain(), hardbs() ); | |
164 | 204 | if( pos == b.pos() && b.size() > 0 ) |
165 | 205 | { |
166 | if( error_size > 0 ) | |
167 | { errors += change_chunk_status( b, Sblock::non_trimmed ); | |
206 | if( error_size > 0 && b.size() <= softbs() && b.size() <= skip_size ) | |
207 | { errors += change_chunk_status( b, Sblock::non_trimmed, domain() ); | |
168 | 208 | errsize += b.size(); } |
169 | 209 | pos = b.end(); |
170 | 210 | } |
171 | 211 | } |
172 | if( skip_size < skipbs ) skip_size = skipbs; | |
173 | else if( skip_size <= max_skip_size / 2 ) skip_size *= 2; | |
174 | else skip_size = max_skip_size; | |
175 | 212 | } |
176 | if( !update_logfile( odes_ ) ) return -2; | |
177 | } | |
178 | if( !block_found ) break; | |
179 | reduce_min_read_rate(); | |
180 | } | |
181 | return 0; | |
213 | if( skip_size < skipbs ) skip_size = skipbs; | |
214 | else if( skip_size <= max_skipbs / 2 ) skip_size *= 2; | |
215 | else skip_size = max_skipbs; | |
216 | } | |
217 | else if( skip_size > 0 && copied_size > 0 ) // reset in two steps | |
218 | { if( skip_size > skipbs ) skip_size = skipbs; else skip_size = 0; } | |
219 | if( !update_logfile( odes_ ) ) return -2; | |
220 | } | |
221 | if( !block_found ) return 0; | |
222 | return -3; | |
182 | 223 | } |
183 | 224 | |
184 | 225 | |
185 | 226 | // Return values: 1 I/O error, 0 OK, -1 interrupted, -2 logfile error. |
186 | // Read the non-damaged part of the domain in reverse mode, skipping | |
187 | // over the damaged areas. | |
188 | // | |
189 | int Rescuebook::rcopy_non_tried() | |
190 | { | |
191 | bool first_post = true; | |
192 | ||
193 | for( bool first_pass = true; ; first_pass = false ) | |
194 | { | |
195 | long long end = LLONG_MAX; | |
196 | int skip_size = 0; // size to skip on error | |
197 | bool block_found = false; | |
198 | ||
199 | if( first_pass && current_status() == copying && | |
200 | domain().includes( current_pos() - 1 ) ) | |
201 | { | |
202 | Block b( current_pos() - 1, 1 ); | |
203 | rfind_chunk( b, Sblock::non_tried ); | |
204 | if( b.size() > 0 ) end = b.end(); // resume | |
205 | } | |
206 | ||
207 | while( end > 0 ) | |
208 | { | |
209 | const int alignment = ( skip_size ? hardbs() : softbs() ); | |
210 | long long pos = end - alignment; | |
211 | if( pos < 0 ) pos = 0; | |
212 | Block b( pos, end - pos ); | |
213 | rfind_chunk( b, Sblock::non_tried, alignment ); | |
214 | if( b.size() <= 0 ) break; | |
215 | if( pos != b.pos() ) skip_size = 0; // reset size on block change | |
216 | end = b.pos(); | |
217 | current_status( copying ); | |
218 | block_found = true; | |
219 | const Sblock::Status st = | |
220 | ( ( b.size() > hardbs() ) ? Sblock::non_trimmed : Sblock::bad_sector ); | |
221 | int copied_size = 0, error_size = 0; | |
222 | const int retval = copy_and_update( b, st, copied_size, error_size, | |
223 | "Copying non-tried blocks...", | |
224 | first_post, false ); | |
225 | if( error_size > 0 ) errsize += error_size; | |
226 | else if( skip_size > 0 && copied_size > 0 ) | |
227 | { skip_size -= copied_size; if( skip_size < 0 ) skip_size = 0; } | |
228 | if( retval ) return retval; | |
229 | if( error_size > 0 ) error_rate += error_size; | |
230 | update_rates(); | |
231 | if( error_size > 0 || slow_read() ) | |
227 | // Read backwards the non-damaged part of the domain, skipping over the | |
228 | // damaged areas. | |
229 | // | |
230 | int Rescuebook::rcopy_non_tried( const char * const msg, const int pass ) | |
231 | { | |
232 | long long end = LLONG_MAX; | |
233 | int skip_size = 0; // size to skip on error if skipbs > 0 | |
234 | bool block_found = false; | |
235 | ||
236 | if( pass == 1 && current_status() == copying && | |
237 | domain().includes( current_pos() - 1 ) ) | |
238 | { | |
239 | Block b( current_pos() - 1, 1 ); | |
240 | rfind_chunk( b, Sblock::non_tried, domain(), hardbs() ); | |
241 | if( b.size() > 0 ) end = b.end(); // resume | |
242 | } | |
243 | ||
244 | while( end > 0 ) | |
245 | { | |
246 | Block b( end - softbs(), softbs() ); | |
247 | rfind_chunk( b, Sblock::non_tried, domain(), softbs() ); | |
248 | if( b.size() <= 0 ) break; | |
249 | if( end != b.end() ) skip_size = 0; // reset size on block change | |
250 | end = b.pos(); | |
251 | block_found = true; | |
252 | int copied_size = 0, error_size = 0; | |
253 | const int retval = copy_and_update2( b, copied_size, error_size, | |
254 | msg, false, skip_size > 0 ); | |
255 | if( error_size > 0 ) { errsize += error_size; error_rate += error_size; } | |
256 | if( retval ) return retval; | |
257 | update_rates(); | |
258 | if( ( error_size > 0 || slow_read() ) && end > 0 ) | |
259 | { | |
260 | if( skip_size > 0 ) // do not skip until 2nd error | |
232 | 261 | { |
233 | if( end > 0 && skip_size > 0 ) | |
262 | if( reopen_on_error && !reopen_infile() ) return 1; | |
263 | if( skipbs > 0 && pass <= 2 ) // do not skip if skipbs == 0 | |
234 | 264 | { |
235 | b.size( skip_size ); b.end( end ); pos = b.pos(); | |
236 | rfind_chunk( b, Sblock::non_tried ); | |
237 | if( pos == b.pos() && b.size() > 0 ) | |
265 | b.assign( end - skip_size, skip_size ); | |
266 | rfind_chunk( b, Sblock::non_tried, domain(), hardbs() ); | |
267 | if( end == b.end() && b.size() > 0 ) | |
238 | 268 | { |
239 | if( error_size > 0 ) | |
240 | { errors += change_chunk_status( b, Sblock::non_trimmed ); | |
269 | if( error_size > 0 && b.size() <= softbs() && b.size() <= skip_size ) | |
270 | { errors += change_chunk_status( b, Sblock::non_trimmed, domain() ); | |
241 | 271 | errsize += b.size(); } |
242 | 272 | end = b.pos(); |
243 | 273 | } |
244 | 274 | } |
245 | if( skip_size < skipbs ) skip_size = skipbs; | |
246 | else if( skip_size <= max_skip_size / 2 ) skip_size *= 2; | |
247 | else skip_size = max_skip_size; | |
248 | 275 | } |
249 | if( !update_logfile( odes_ ) ) return -2; | |
250 | } | |
251 | if( !block_found ) break; | |
252 | reduce_min_read_rate(); | |
253 | } | |
254 | return 0; | |
276 | if( skip_size < skipbs ) skip_size = skipbs; | |
277 | else if( skip_size <= max_skipbs / 2 ) skip_size *= 2; | |
278 | else skip_size = max_skipbs; | |
279 | } | |
280 | else if( skip_size > 0 && copied_size > 0 ) // reset in two steps | |
281 | { if( skip_size > skipbs ) skip_size = skipbs; else skip_size = 0; } | |
282 | if( !update_logfile( odes_ ) ) return -2; | |
283 | } | |
284 | if( !block_found ) return 0; | |
285 | return -3; | |
255 | 286 | } |
256 | 287 | |
257 | 288 | |
258 | 289 | // Return values: 1 I/O error, 0 OK, -1 interrupted, -2 logfile error. |
259 | // Trim the damaged areas (largest first) from both edges. | |
290 | // Trim the damaged areas (smallest first) from both edges. | |
260 | 291 | // |
261 | 292 | int Rescuebook::trim_errors() |
262 | 293 | { |
263 | 294 | const char * const msg = "Trimming failed blocks..."; |
264 | bool first_post = true; | |
295 | first_post = true; | |
296 | read_logger.print_msg( t1 - t0, msg ); | |
265 | 297 | |
266 | 298 | while( true ) |
267 | 299 | { |
268 | const int index = find_largest_sblock( Sblock::non_trimmed ); | |
300 | const int index = | |
301 | find_smallest_sblock( Sblock::non_trimmed, domain(), softbs() ); | |
269 | 302 | if( index < 0 ) break; // no more blocks |
270 | 303 | const Block block = sblock( index ); |
271 | 304 | long long pos = block.pos(); |
272 | 305 | while( pos >= 0 ) |
273 | 306 | { |
274 | 307 | Block b( pos, hardbs() ); |
275 | find_chunk( b, Sblock::non_trimmed ); | |
308 | find_chunk( b, Sblock::non_trimmed, domain(), hardbs() ); | |
276 | 309 | if( pos != b.pos() || b.size() <= 0 ) break; // block change |
277 | 310 | pos = b.end(); |
278 | current_status( trimming ); | |
279 | int copied_size = 0, error_size = 0; | |
280 | const int retval = copy_and_update( b, Sblock::bad_sector, copied_size, | |
281 | error_size, msg, first_post, true ); | |
282 | if( copied_size > 0 ) errsize -= copied_size; | |
311 | current_status( trimming, msg ); | |
312 | int error_size = 0; | |
313 | const int retval = copy_and_update( b, error_size, msg, true ); | |
283 | 314 | if( retval ) return retval; |
284 | 315 | if( error_size > 0 ) { error_rate += error_size; pos = -1; } |
285 | 316 | update_rates(); |
288 | 319 | long long end = block.end(); |
289 | 320 | while( end > 0 ) |
290 | 321 | { |
291 | pos = end - hardbs(); | |
292 | if( pos < 0 ) pos = 0; | |
293 | Block b( pos, end - pos ); | |
294 | rfind_chunk( b, Sblock::non_trimmed ); | |
295 | if( pos != b.pos() || b.size() <= 0 ) break; // block change | |
322 | Block b( end - hardbs(), hardbs() ); | |
323 | rfind_chunk( b, Sblock::non_trimmed, domain(), hardbs() ); | |
324 | if( end != b.end() || b.size() <= 0 ) break; // block change | |
296 | 325 | end = b.pos(); |
297 | current_status( trimming ); | |
298 | int copied_size = 0, error_size = 0; | |
299 | const int retval = copy_and_update( b, Sblock::bad_sector, copied_size, | |
300 | error_size, msg, first_post, false ); | |
301 | if( copied_size > 0 ) errsize -= copied_size; | |
326 | current_status( trimming, msg ); | |
327 | int error_size = 0; | |
328 | const int retval = copy_and_update( b, error_size, msg, false ); | |
302 | 329 | if( retval ) return retval; |
303 | 330 | if( error_size > 0 ) error_rate += error_size; |
304 | 331 | if( error_size > 0 && end > 0 ) |
306 | 333 | const int index = find_index( end - 1 ); |
307 | 334 | if( index >= 0 && domain().includes( sblock( index ) ) && |
308 | 335 | sblock( index ).status() == Sblock::non_trimmed ) |
309 | errors += change_chunk_status( sblock( index ), Sblock::non_split ); | |
336 | errors += change_chunk_status( sblock( index ), Sblock::non_split, | |
337 | domain() ); | |
310 | 338 | end = -1; |
311 | 339 | } |
312 | 340 | update_rates(); |
319 | 347 | |
320 | 348 | // Return values: 1 I/O error, 0 OK, -1 interrupted, -2 logfile error. |
321 | 349 | // Split the damaged areas (largest first). |
322 | // Then read the remaining small areas sequentially. | |
323 | // | |
324 | int Rescuebook::split_errors( const bool reverse ) | |
350 | // Then read the remaining small areas (less than 7 sectors) sequentially. | |
351 | // | |
352 | int Rescuebook::split_errors() | |
325 | 353 | { |
326 | 354 | const char * const msg = "Splitting failed blocks..."; |
327 | bool first_post = true; | |
355 | first_post = true; | |
356 | read_logger.print_msg( t1 - t0, msg ); | |
328 | 357 | |
329 | 358 | while( true ) |
330 | 359 | { |
360 | const int index = find_largest_sblock( Sblock::non_split, domain() ); | |
361 | if( index < 0 ) break; // no more blocks | |
362 | const Block block = sblock( index ); | |
363 | if( block.size() / hardbs() < 7 ) break; // no more large blocks | |
331 | 364 | if( sblocks() < max_logfile_size ) // split largest block |
332 | 365 | { |
333 | const int index = find_largest_sblock( Sblock::non_split ); | |
334 | if( index < 0 ) break; // no more blocks | |
335 | const Block block = sblock( index ); | |
336 | if( block.size() / hardbs() < 5 ) break; // no more large blocks | |
337 | const long long midpos = | |
366 | long long midpos = | |
338 | 367 | block.pos() + ( ( block.size() / ( 2 * hardbs() ) ) * hardbs() ); |
339 | 368 | long long pos = midpos; |
340 | 369 | while( pos >= 0 ) |
341 | 370 | { |
342 | 371 | Block b( pos, hardbs() ); |
343 | find_chunk( b, Sblock::non_split ); | |
372 | find_chunk( b, Sblock::non_split, domain(), hardbs() ); | |
344 | 373 | if( pos != b.pos() || b.size() <= 0 ) break; // block change |
345 | 374 | pos = b.end(); |
346 | current_status( splitting ); | |
347 | int copied_size = 0, error_size = 0; | |
348 | const int retval = copy_and_update( b, Sblock::bad_sector, copied_size, | |
349 | error_size, msg, first_post, true ); | |
350 | if( copied_size > 0 ) errsize -= copied_size; | |
375 | current_status( splitting, msg ); | |
376 | int error_size = 0; | |
377 | const int retval = copy_and_update( b, error_size, msg, true ); | |
351 | 378 | if( retval ) return retval; |
352 | if( error_size > 0 ) { error_rate += error_size; pos = -1; } | |
379 | if( error_size > 0 ) | |
380 | { error_rate += error_size; pos = -1; | |
381 | if( b.pos() == midpos ) midpos = -1; } // skip backwards reads | |
353 | 382 | update_rates(); |
354 | 383 | if( !update_logfile( odes_ ) ) return -2; |
355 | 384 | } |
356 | 385 | long long end = midpos; |
357 | 386 | while( end > 0 ) |
358 | 387 | { |
359 | pos = end - hardbs(); | |
360 | if( pos < 0 ) pos = 0; | |
361 | Block b( pos, end - pos ); | |
362 | rfind_chunk( b, Sblock::non_split ); | |
363 | if( pos != b.pos() || b.size() <= 0 ) break; // block change | |
388 | Block b( end - hardbs(), hardbs() ); | |
389 | rfind_chunk( b, Sblock::non_split, domain(), hardbs() ); | |
390 | if( end != b.end() || b.size() <= 0 ) break; // block change | |
364 | 391 | end = b.pos(); |
365 | current_status( splitting ); | |
366 | int copied_size = 0, error_size = 0; | |
367 | const int retval = copy_and_update( b, Sblock::bad_sector, copied_size, | |
368 | error_size, msg, first_post, true ); | |
369 | if( copied_size > 0 ) errsize -= copied_size; | |
392 | current_status( splitting, msg ); | |
393 | int error_size = 0; | |
394 | const int retval = copy_and_update( b, error_size, msg, true ); | |
370 | 395 | if( retval ) return retval; |
371 | 396 | if( error_size > 0 ) { error_rate += error_size; end = -1; } |
372 | 397 | update_rates(); |
373 | 398 | if( !update_logfile( odes_ ) ) return -2; |
374 | 399 | } |
375 | 400 | } |
376 | else // logfile is full; read smallest block | |
377 | { | |
378 | const int index = find_smallest_sblock( Sblock::non_split ); | |
379 | if( index < 0 ) break; // no more blocks | |
380 | const Block block = sblock( index ); | |
401 | else // logfile is full; read largest block | |
402 | { | |
381 | 403 | long long pos = block.pos(); |
382 | 404 | while( pos >= 0 ) |
383 | 405 | { |
384 | 406 | Block b( pos, hardbs() ); |
385 | find_chunk( b, Sblock::non_split ); | |
407 | find_chunk( b, Sblock::non_split, domain(), hardbs() ); | |
386 | 408 | if( pos != b.pos() || b.size() <= 0 ) break; // block change |
387 | 409 | pos = b.end(); |
388 | current_status( splitting ); | |
389 | int copied_size = 0, error_size = 0; | |
390 | const int retval = copy_and_update( b, Sblock::bad_sector, copied_size, | |
391 | error_size, msg, first_post, true ); | |
392 | if( copied_size > 0 ) errsize -= copied_size; | |
410 | current_status( splitting, msg ); | |
411 | int error_size = 0; | |
412 | const int retval = copy_and_update( b, error_size, msg, true ); | |
393 | 413 | if( retval ) return retval; |
394 | 414 | if( error_size > 0 ) error_rate += error_size; |
395 | 415 | update_rates(); |
403 | 423 | while( pos >= 0 ) |
404 | 424 | { |
405 | 425 | Block b( pos, hardbs() ); |
406 | find_chunk( b, Sblock::non_split ); | |
426 | find_chunk( b, Sblock::non_split, domain(), hardbs() ); | |
407 | 427 | if( b.size() <= 0 ) break; // no more blocks |
408 | 428 | pos = b.end(); |
409 | current_status( splitting ); | |
410 | int copied_size = 0, error_size = 0; | |
411 | const int retval = copy_and_update( b, Sblock::bad_sector, copied_size, | |
412 | error_size, msg, first_post, true ); | |
413 | if( copied_size > 0 ) errsize -= copied_size; | |
429 | current_status( splitting, msg ); | |
430 | int error_size = 0; | |
431 | const int retval = copy_and_update( b, error_size, msg, true ); | |
414 | 432 | if( retval ) return retval; |
415 | 433 | if( error_size > 0 ) error_rate += error_size; |
416 | 434 | update_rates(); |
422 | 440 | long long end = LLONG_MAX; |
423 | 441 | while( end > 0 ) |
424 | 442 | { |
425 | long long pos = end - hardbs(); | |
426 | if( pos < 0 ) pos = 0; | |
427 | Block b( pos, end - pos ); | |
428 | rfind_chunk( b, Sblock::non_split ); | |
443 | Block b( end - hardbs(), hardbs() ); | |
444 | rfind_chunk( b, Sblock::non_split, domain(), hardbs() ); | |
429 | 445 | if( b.size() <= 0 ) break; // no more blocks |
430 | 446 | end = b.pos(); |
431 | current_status( splitting ); | |
432 | int copied_size = 0, error_size = 0; | |
433 | const int retval = copy_and_update( b, Sblock::bad_sector, copied_size, | |
434 | error_size, msg, first_post, true ); | |
435 | if( copied_size > 0 ) errsize -= copied_size; | |
447 | current_status( splitting, msg ); | |
448 | int error_size = 0; | |
449 | const int retval = copy_and_update( b, error_size, msg, true ); | |
436 | 450 | if( retval ) return retval; |
437 | 451 | if( error_size > 0 ) error_rate += error_size; |
438 | 452 | update_rates(); |
448 | 462 | // |
449 | 463 | int Rescuebook::copy_errors() |
450 | 464 | { |
451 | char msgbuf[80] = "Retrying bad sectors... Retry "; | |
465 | char msgbuf[80] = "Retrying bad sectors (forwards)... Retry "; | |
452 | 466 | const int msglen = std::strlen( msgbuf ); |
453 | 467 | |
454 | 468 | for( int retry = 1; max_retries < 0 || retry <= max_retries; ++retry ) |
455 | 469 | { |
456 | 470 | long long pos = 0; |
457 | bool first_post = true, block_found = false; | |
471 | bool block_found = false; | |
472 | first_post = true; | |
458 | 473 | snprintf( msgbuf + msglen, ( sizeof msgbuf ) - msglen, "%d", retry ); |
474 | read_logger.print_msg( t1 - t0, msgbuf ); | |
459 | 475 | |
460 | 476 | if( retry == 1 && current_status() == retrying && |
461 | 477 | domain().includes( current_pos() ) ) |
462 | 478 | { |
463 | 479 | Block b( current_pos(), 1 ); |
464 | find_chunk( b, Sblock::bad_sector ); | |
480 | find_chunk( b, Sblock::bad_sector, domain(), hardbs() ); | |
465 | 481 | if( b.size() > 0 ) pos = b.pos(); // resume |
466 | 482 | } |
467 | 483 | |
468 | 484 | while( pos >= 0 ) |
469 | 485 | { |
470 | 486 | Block b( pos, hardbs() ); |
471 | find_chunk( b, Sblock::bad_sector ); | |
472 | if( b.size() <= 0 ) break; | |
487 | find_chunk( b, Sblock::bad_sector, domain(), hardbs() ); | |
488 | if( b.size() <= 0 ) break; // no more blocks | |
473 | 489 | pos = b.end(); |
474 | current_status( retrying ); | |
490 | current_status( retrying, msgbuf ); | |
475 | 491 | block_found = true; |
476 | int copied_size = 0, error_size = 0; | |
477 | const int retval = copy_and_update( b, Sblock::bad_sector, copied_size, | |
478 | error_size, msgbuf, first_post, true ); | |
479 | if( copied_size > 0 ) errsize -= copied_size; | |
492 | int error_size = 0; | |
493 | const int retval = copy_and_update( b, error_size, msgbuf, true ); | |
480 | 494 | if( retval ) return retval; |
481 | 495 | if( error_size > 0 ) error_rate += error_size; |
482 | 496 | update_rates(); |
493 | 507 | // |
494 | 508 | int Rescuebook::rcopy_errors() |
495 | 509 | { |
496 | char msgbuf[80] = "Retrying bad sectors... Retry "; | |
510 | char msgbuf[80] = "Retrying bad sectors (backwards)... Retry "; | |
497 | 511 | const int msglen = std::strlen( msgbuf ); |
498 | 512 | |
499 | 513 | for( int retry = 1; max_retries < 0 || retry <= max_retries; ++retry ) |
500 | 514 | { |
501 | 515 | long long end = LLONG_MAX; |
502 | bool first_post = true, block_found = false; | |
516 | bool block_found = false; | |
517 | first_post = true; | |
503 | 518 | snprintf( msgbuf + msglen, ( sizeof msgbuf ) - msglen, "%d", retry ); |
519 | read_logger.print_msg( t1 - t0, msgbuf ); | |
504 | 520 | |
505 | 521 | if( retry == 1 && current_status() == retrying && |
506 | 522 | domain().includes( current_pos() - 1 ) ) |
507 | 523 | { |
508 | 524 | Block b( current_pos() - 1, 1 ); |
509 | rfind_chunk( b, Sblock::bad_sector ); | |
525 | rfind_chunk( b, Sblock::bad_sector, domain(), hardbs() ); | |
510 | 526 | if( b.size() > 0 ) end = b.end(); // resume |
511 | 527 | } |
512 | 528 | |
513 | 529 | while( end > 0 ) |
514 | 530 | { |
515 | long long pos = end - hardbs(); | |
516 | if( pos < 0 ) pos = 0; | |
517 | Block b( pos, end - pos ); | |
518 | rfind_chunk( b, Sblock::bad_sector ); | |
519 | if( b.size() <= 0 ) break; | |
531 | Block b( end - hardbs(), hardbs() ); | |
532 | rfind_chunk( b, Sblock::bad_sector, domain(), hardbs() ); | |
533 | if( b.size() <= 0 ) break; // no more blocks | |
520 | 534 | end = b.pos(); |
521 | current_status( retrying ); | |
535 | current_status( retrying, msgbuf ); | |
522 | 536 | block_found = true; |
523 | int copied_size = 0, error_size = 0; | |
524 | const int retval = copy_and_update( b, Sblock::bad_sector, copied_size, | |
525 | error_size, msgbuf, first_post, false ); | |
526 | if( copied_size > 0 ) errsize -= copied_size; | |
537 | int error_size = 0; | |
538 | const int retval = copy_and_update( b, error_size, msgbuf, false ); | |
527 | 539 | if( retval ) return retval; |
528 | 540 | if( error_size > 0 ) error_rate += error_size; |
529 | 541 | update_rates(); |
535 | 547 | } |
536 | 548 | |
537 | 549 | |
550 | void Rescuebook::update_rates( const bool force ) | |
551 | { | |
552 | if( t0 == 0 ) | |
553 | { | |
554 | t0 = t1 = ts = initial_time(); | |
555 | first_size = last_size = recsize; | |
556 | rates_updated = true; | |
557 | if( verbosity >= 0 ) std::printf( "\n\n\n" ); | |
558 | } | |
559 | ||
560 | long t2 = std::time( 0 ); | |
561 | if( force && t2 <= t1 ) t2 = t1 + 1; // force update of e_code | |
562 | if( t2 > t1 ) | |
563 | { | |
564 | a_rate = ( recsize - first_size ) / ( t2 - t0 ); | |
565 | c_rate = ( recsize - last_size ) / ( t2 - t1 ); | |
566 | if( !( e_code & 4 ) ) | |
567 | { | |
568 | if( recsize != last_size ) { last_size = recsize; ts = t2; } | |
569 | else if( timeout >= 0 && t2 - ts > timeout ) e_code |= 4; | |
570 | } | |
571 | if( max_error_rate >= 0 && !( e_code & 1 ) ) | |
572 | { | |
573 | error_rate /= ( t2 - t1 ); | |
574 | if( error_rate > max_error_rate ) e_code |= 1; | |
575 | else error_rate = 0; | |
576 | } | |
577 | t1 = t2; | |
578 | rates_updated = true; | |
579 | } | |
580 | else if( t2 < t1 ) // clock jumped back | |
581 | { | |
582 | const long delta = std::min( t0, t1 - t2 ); | |
583 | t0 -= delta; | |
584 | ts -= delta; | |
585 | t1 = t2; | |
586 | } | |
587 | } | |
588 | ||
589 | ||
590 | void Rescuebook::show_status( const long long ipos, const char * const msg, | |
591 | const bool force ) | |
592 | { | |
593 | const char * const up = "\x1b[A"; | |
594 | ||
595 | if( ipos >= 0 ) last_ipos = ipos; | |
596 | if( rates_updated || force || first_post ) | |
597 | { | |
598 | if( verbosity >= 0 ) | |
599 | { | |
600 | std::printf( "\r%s%s%s", up, up, up ); | |
601 | std::printf( "rescued: %10sB, errsize:%9sB, current rate: %9sB/s\n", | |
602 | format_num( recsize ), format_num( errsize, 99999 ), | |
603 | format_num( c_rate, 99999 ) ); | |
604 | std::printf( " ipos: %10sB, errors: %7u, average rate: %9sB/s\n", | |
605 | format_num( last_ipos ), errors, | |
606 | format_num( a_rate, 99999 ) ); | |
607 | std::printf( " opos: %10sB, run time: %9s, successful read: %9s ago\n", | |
608 | format_num( last_ipos + offset() ), | |
609 | format_time( t1 - t0 ), format_time( t1 - ts ) ); | |
610 | if( msg && msg[0] && !errors_or_timeout() ) | |
611 | { | |
612 | const int len = std::strlen( msg ); std::printf( "\r%s", msg ); | |
613 | for( int i = len; i < oldlen; ++i ) std::fputc( ' ', stdout ); | |
614 | oldlen = len; | |
615 | } | |
616 | std::fflush( stdout ); | |
617 | } | |
618 | rate_logger.print_line( t1 - t0, last_ipos, a_rate, c_rate, errors, errsize ); | |
619 | if( !force && !first_post ) read_logger.print_time( t1 - t0 ); | |
620 | rates_updated = false; | |
621 | first_post = false; | |
622 | } | |
623 | } | |
624 | ||
625 | ||
538 | 626 | Rescuebook::Rescuebook( const long long offset, const long long isize, |
539 | Domain & dom, const Rb_options & rb_opts, | |
540 | const char * const iname, const char * const logname, | |
541 | const int cluster, const int hardbs, | |
542 | const bool synchronous ) | |
627 | Domain & dom, const Domain * const test_dom, | |
628 | const Rb_options & rb_opts, const char * const iname, | |
629 | const char * const logname, const int cluster, | |
630 | const int hardbs, const bool synchronous ) | |
543 | 631 | : Logbook( offset, isize, dom, logname, cluster, hardbs, rb_opts.complete_only ), |
544 | 632 | Rb_options( rb_opts ), |
545 | 633 | error_rate( 0 ), |
546 | 634 | sparse_size( sparse ? 0 : -1 ), |
547 | 635 | recsize( 0 ), |
548 | 636 | errsize( 0 ), |
549 | iname_( ( access( iname, F_OK ) == 0 ) ? iname : 0 ), | |
550 | max_skip_size( calculate_max_skip_size( isize, hardbs, skipbs ) ), | |
637 | test_domain( test_dom ), | |
638 | iname_( iname ), | |
551 | 639 | e_code( 0 ), |
640 | access_works( access( iname, F_OK ) == 0 ), | |
552 | 641 | synchronous_( synchronous ), |
553 | 642 | a_rate( 0 ), c_rate( 0 ), first_size( 0 ), last_size( 0 ), |
554 | 643 | last_ipos( 0 ), t0( 0 ), t1( 0 ), ts( 0 ), oldlen( 0 ), |
555 | rates_updated( false ) | |
556 | { | |
644 | rates_updated( false ), first_post( false ) | |
645 | { | |
646 | const long long csize = isize / 100; | |
647 | if( isize > 0 && skipbs > 0 && max_skipbs == Rb_options::max_max_skipbs && | |
648 | csize < max_skipbs ) | |
649 | max_skipbs = std::max( (long long)skipbs, csize ); | |
650 | skipbs = round_up( skipbs, hardbs ); // make multiple of hardbs | |
651 | max_skipbs = round_up( max_skipbs, hardbs ); | |
652 | ||
557 | 653 | if( retrim ) |
558 | 654 | for( int index = 0; index < sblocks(); ++index ) |
559 | 655 | { |
581 | 677 | |
582 | 678 | // Return values: 1 I/O error, 0 OK. |
583 | 679 | // |
584 | int Rescuebook::do_rescue( const int ides, const int odes, const bool reverse ) | |
680 | int Rescuebook::do_rescue( const int ides, const int odes ) | |
585 | 681 | { |
586 | 682 | bool copy_pending = false, trim_pending = false, split_pending = false; |
587 | 683 | ides_ = ides; odes_ = odes; |
607 | 703 | if( logfile_exists() ) |
608 | 704 | { |
609 | 705 | std::printf( "Initial status (read from logfile)\n" ); |
610 | std::printf( "rescued: %10sB,", format_num( recsize ) ); | |
611 | std::printf( " errsize:%9sB,", format_num( errsize, 99999 ) ); | |
612 | std::printf( " errors: %7u\n", errors ); | |
613 | 706 | if( verbosity >= 2 ) |
614 | 707 | { |
615 | std::printf( "current position: %10sB,", format_num( current_pos() ) ); | |
616 | std::printf( " current sector: %7lld\n", current_pos() / hardbs() ); | |
708 | std::printf( "current position: %10sB, current sector: %7lld\n", | |
709 | format_num( current_pos() ), current_pos() / hardbs() ); | |
617 | 710 | if( sblocks() ) |
618 | std::printf( "last block size: %10sB\n", | |
711 | std::printf( " last block size: %10sB\n", | |
619 | 712 | format_num( sblock( sblocks() - 1 ).size() ) ); |
620 | std::printf( "\n" ); | |
621 | 713 | } |
622 | std::printf( "Current status\n" ); | |
714 | if( domain().pos() > 0 || domain().end() < logfile_isize() ) | |
715 | std::printf( "(sizes below are limited to the domain %sB to %sB)\n", | |
716 | format_num( domain().pos() ), format_num( domain().end() ) ); | |
717 | std::printf( "rescued: %10sB, errsize:%9sB, errors: %7u\n", | |
718 | format_num( recsize ), format_num( errsize, 99999 ), errors ); | |
719 | std::printf( "\nCurrent status\n" ); | |
623 | 720 | } |
624 | 721 | } |
625 | 722 | int retval = 0; |
626 | 723 | update_rates(); // first call |
627 | 724 | if( copy_pending && !errors_or_timeout() ) |
628 | retval = ( reverse ? rcopy_non_tried() : copy_non_tried() ); | |
629 | if( !retval && trim_pending && !errors_or_timeout() ) | |
725 | retval = copy_non_tried(); | |
726 | if( retval == 0 && trim_pending && !notrim && !errors_or_timeout() ) | |
630 | 727 | retval = trim_errors(); |
631 | if( !retval && split_pending && !nosplit && !errors_or_timeout() ) | |
632 | retval = split_errors( reverse ); | |
633 | if( !retval && max_retries != 0 && !errors_or_timeout() ) | |
634 | retval = ( reverse ? rcopy_errors() : copy_errors() ); | |
728 | if( retval == 0 && split_pending && !nosplit && !errors_or_timeout() ) | |
729 | retval = split_errors(); | |
730 | if( retval == 0 && max_retries != 0 && !errors_or_timeout() ) | |
731 | retval = reverse ? rcopy_errors() : copy_errors(); | |
635 | 732 | if( !rates_updated ) update_rates( true ); // force update of e_code |
636 | show_status( -1, (retval ? 0 : "Finished"), true ); | |
637 | if( !retval && errors_or_timeout() ) retval = 1; | |
733 | show_status( -1, retval ? 0 : "Finished", true ); | |
734 | ||
735 | const bool signaled = ( retval == -1 ); | |
736 | if( signaled ) retval = 0; | |
737 | if( retval == 0 && errors_or_timeout() ) retval = 1; | |
638 | 738 | if( verbosity >= 0 ) |
639 | 739 | { |
640 | 740 | if( retval == -2 ) std::printf( "\nLogfile error" ); |
641 | else if( retval < 0 ) std::printf( "\nInterrupted by user" ); | |
741 | else if( retval == 0 && signaled ) std::printf( "\nInterrupted by user" ); | |
642 | 742 | else |
643 | 743 | { |
644 | 744 | if( e_code & 1 ) |
652 | 752 | if( retval == -2 ) retval = 1; // logfile error |
653 | 753 | else |
654 | 754 | { |
655 | if( retval == 0 ) current_status( finished ); | |
656 | else if( retval < 0 ) retval = 0; // interrupted by user | |
755 | if( retval == 0 && !signaled ) current_status( finished ); | |
657 | 756 | if( !extend_outfile_size() ) // sparse or -x option |
658 | 757 | { |
659 | 758 | show_error( "Error extending output file size." ); |
662 | 761 | compact_sblock_vector(); |
663 | 762 | if( !update_logfile( odes_, true ) && retval == 0 ) retval = 1; |
664 | 763 | } |
764 | if( close( odes_ ) != 0 ) | |
765 | { show_error( "Can't close outfile", errno ); | |
766 | if( retval == 0 ) retval = 1; } | |
767 | if( !rate_logger.close_file() ) | |
768 | show_error( "warning: Error closing the rates logging file." ); | |
769 | if( !read_logger.close_file() ) | |
770 | show_error( "warning: Error closing the reads logging file." ); | |
665 | 771 | if( final_msg() ) show_error( final_msg(), final_errno() ); |
666 | return retval; | |
667 | } | |
772 | if( retval ) return retval; // errors have priority over signals | |
773 | if( signaled ) return signaled_exit(); | |
774 | return 0; | |
775 | } |
0 | 0 | #! /bin/sh |
1 | 1 | # check script for GNU ddrescue - Data recovery tool |
2 | # Copyright (C) 2009, 2010, 2011, 2012, 2013 Antonio Diaz Diaz. | |
2 | # Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014 Antonio Diaz Diaz. | |
3 | 3 | # |
4 | 4 | # This script is free software: you have unlimited permission |
5 | 5 | # to copy, distribute and modify it. |
12 | 12 | DDRESCUELOG="${objdir}"/ddrescuelog |
13 | 13 | framework_failure() { echo "failure in testing framework" ; exit 1 ; } |
14 | 14 | |
15 | if [ ! -x "${DDRESCUE}" ] ; then | |
15 | if [ ! -f "${DDRESCUE}" ] || [ ! -x "${DDRESCUE}" ] ; then | |
16 | 16 | echo "${DDRESCUE}: cannot execute" |
17 | 17 | exit 1 |
18 | 18 | fi |
24 | 24 | in="${testdir}"/test.txt |
25 | 25 | in1="${testdir}"/test1.txt |
26 | 26 | in2="${testdir}"/test2.txt |
27 | blank="${testdir}"/logfile_blank | |
27 | 28 | logfile1="${testdir}"/logfile1 |
28 | 29 | logfile2="${testdir}"/logfile2 |
30 | logfile2i="${testdir}"/logfile2i | |
31 | logfile3="${testdir}"/logfile3 | |
32 | logfile4="${testdir}"/logfile4 | |
33 | logfile5="${testdir}"/logfile5 | |
29 | 34 | fail=0 |
30 | 35 | |
31 | 36 | printf "testing ddrescue-%s..." "$2" |
32 | 37 | |
33 | 38 | "${DDRESCUE}" -q ${in} |
34 | if [ $? = 0 ] ; then fail=1 ; printf - ; else printf . ; fi | |
39 | if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi | |
35 | 40 | "${DDRESCUE}" -q ${in} out logfile extra |
36 | if [ $? = 0 ] ; then fail=1 ; printf - ; else printf . ; fi | |
41 | if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi | |
37 | 42 | "${DDRESCUE}" -q ${in} ${in} logfile |
38 | if [ $? = 0 ] ; then fail=1 ; printf - ; else printf . ; fi | |
43 | if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi | |
39 | 44 | "${DDRESCUE}" -q ${in} out ${in} |
40 | if [ $? = 0 ] ; then fail=1 ; printf - ; else printf . ; fi | |
45 | if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi | |
41 | 46 | "${DDRESCUE}" -q ${in} out out |
42 | if [ $? = 0 ] ; then fail=1 ; printf - ; else printf . ; fi | |
43 | "${DDRESCUE}" -q -g ${in} out | |
44 | if [ $? = 0 ] ; then fail=1 ; printf - ; else printf . ; fi | |
47 | if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi | |
45 | 48 | "${DDRESCUE}" -q -F- ${in} out |
46 | if [ $? = 0 ] ; then fail=1 ; printf - ; else printf . ; fi | |
49 | if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi | |
47 | 50 | "${DDRESCUE}" -q -F ${in} out logfile |
48 | if [ $? = 0 ] ; then fail=1 ; printf - ; else printf . ; fi | |
49 | "${DDRESCUE}" -q -F- -g ${in} out logfile | |
50 | if [ $? = 0 ] ; then fail=1 ; printf - ; else printf . ; fi | |
51 | if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi | |
52 | "${DDRESCUE}" -q -G ${in} out | |
53 | if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi | |
54 | "${DDRESCUE}" -q -F- -G ${in} out logfile | |
55 | if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi | |
56 | "${DDRESCUE}" -q -H ${logfile2i} ${in} out logfile | |
57 | if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi | |
58 | "${DDRESCUE}" -q -K0, ${in} out | |
59 | if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi | |
60 | "${DDRESCUE}" -q -K0,65535 ${in} out | |
61 | if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi | |
62 | "${DDRESCUE}" -q -i 0, ${in} out | |
63 | if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi | |
64 | "${DDRESCUE}" -q -i -1 ${in} out | |
65 | if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi | |
51 | 66 | "${DDRESCUE}" -q -m ${logfile1} -m ${logfile1} ${in} out logfile |
52 | if [ $? = 0 ] ; then fail=1 ; printf - ; else printf . ; fi | |
67 | if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi | |
68 | "${DDRESCUE}" -q -m ${logfile2i} ${in} out logfile | |
69 | if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi | |
53 | 70 | "${DDRESCUE}" -q -w ${in} out logfile |
54 | if [ $? = 0 ] ; then fail=1 ; printf - ; else printf . ; fi | |
55 | ||
56 | if [ -r logfile ] ; then rm logfile || framework_failure ; fi | |
57 | "${DDRESCUE}" -t -pq -i15000 ${in} out logfile || fail=1 | |
58 | "${DDRESCUE}" -D -fnq -s15000 ${in} out logfile || fail=1 | |
59 | cmp ${in} out || fail=1 | |
60 | printf . | |
61 | ||
62 | rm out || framework_failure | |
63 | rm logfile || framework_failure | |
64 | "${DDRESCUE}" -qR -i15000 ${in} out logfile || fail=1 | |
65 | "${DDRESCUE}" -qR -s15000 ${in} out logfile || fail=1 | |
66 | cmp ${in} out || fail=1 | |
67 | printf . | |
68 | ||
69 | rm out || framework_failure | |
70 | "${DDRESCUE}" -qF+ -o15000 ${in} out2 logfile || fail=1 | |
71 | "${DDRESCUE}" -qRS -i15000 -o0 out2 out || fail=1 | |
71 | if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi | |
72 | ||
73 | rm -f logfile | |
74 | "${DDRESCUE}" -q -t -p -i15000 ${in} out logfile || fail=1 | |
75 | "${DDRESCUE}" -q -D -f -n -s15000 ${in} out logfile || fail=1 | |
76 | cmp ${in} out || fail=1 | |
77 | printf . | |
78 | ||
79 | rm -f out | |
80 | rm -f logfile | |
81 | "${DDRESCUE}" -q -R -i15000 ${in} out logfile || fail=1 | |
82 | "${DDRESCUE}" -q -R -s15000 ${in} out logfile || fail=1 | |
83 | cmp ${in} out || fail=1 | |
84 | printf . | |
85 | ||
86 | rm -f out | |
87 | "${DDRESCUE}" -q -F+ -o15000 ${in} out2 logfile || fail=1 | |
88 | "${DDRESCUE}" -q -R -S -i15000 -o0 out2 out || fail=1 | |
72 | 89 | cmp ${in} out || fail=1 |
73 | 90 | printf . |
74 | 91 | |
75 | 92 | printf "garbage" >> out || framework_failure |
76 | "${DDRESCUE}" -qRt -i15000 -o0 out2 out || fail=1 | |
77 | cmp ${in} out || fail=1 | |
78 | printf . | |
79 | ||
80 | rm out || framework_failure | |
81 | "${DDRESCUE}" -q -m ${logfile1} ${in} out || fail=1 | |
93 | "${DDRESCUE}" -q -R -t -i15000 -o0 out2 out || fail=1 | |
94 | cmp ${in} out || fail=1 | |
95 | printf . | |
96 | ||
97 | rm -f out | |
98 | "${DDRESCUE}" -q -O -H ${logfile1} ${in} out || fail=1 | |
82 | 99 | cmp ${in1} out || fail=1 |
83 | 100 | printf . |
84 | "${DDRESCUE}" -q -m ${logfile2} ${in} out || fail=1 | |
85 | cmp ${in} out || fail=1 | |
86 | printf . | |
87 | ||
88 | rm out || framework_failure | |
89 | "${DDRESCUE}" -qRm ${logfile2} ${in} out || fail=1 | |
101 | "${DDRESCUE}" -q -O -L -K0 -H ${logfile2i} ${in} out || fail=1 | |
102 | cmp ${in} out || fail=1 | |
103 | printf . | |
104 | ||
105 | rm -f out | |
106 | "${DDRESCUE}" -q -O -m ${logfile1} ${in} out || fail=1 | |
107 | cmp ${in1} out || fail=1 | |
108 | printf . | |
109 | "${DDRESCUE}" -q -O -L -m ${logfile2i} ${in} out || fail=1 | |
110 | cmp ${in} out || fail=1 | |
111 | printf . | |
112 | ||
113 | rm -f out | |
114 | "${DDRESCUE}" -q -R -m ${logfile2} ${in} out || fail=1 | |
90 | 115 | cmp ${in2} out || fail=1 |
91 | 116 | printf . |
92 | "${DDRESCUE}" -qRm ${logfile1} ${in} out || fail=1 | |
93 | cmp ${in} out || fail=1 | |
94 | printf . | |
95 | ||
96 | rm out || framework_failure | |
117 | "${DDRESCUE}" -q -R -K,64KiB -m ${logfile1} ${in} out || fail=1 | |
118 | cmp ${in} out || fail=1 | |
119 | printf . | |
120 | ||
121 | rm -f out | |
97 | 122 | cat ${logfile1} > logfile || framework_failure |
98 | "${DDRESCUE}" -qI ${in} out logfile || fail=1 | |
123 | "${DDRESCUE}" -q -I ${in} out logfile || fail=1 | |
99 | 124 | cat ${logfile2} > logfile || framework_failure |
100 | "${DDRESCUE}" -qI ${in} out logfile || fail=1 | |
101 | cmp ${in} out || fail=1 | |
102 | printf . | |
103 | ||
104 | rm out || framework_failure | |
125 | "${DDRESCUE}" -q -I ${in} out logfile || fail=1 | |
126 | cmp ${in} out || fail=1 | |
127 | printf . | |
128 | ||
129 | rm -f out | |
105 | 130 | cat ${logfile1} > logfile || framework_failure |
106 | "${DDRESCUE}" -qR ${in} out logfile || fail=1 | |
131 | "${DDRESCUE}" -q -R ${in} out logfile || fail=1 | |
107 | 132 | cat ${logfile2} > logfile || framework_failure |
108 | "${DDRESCUE}" -qR ${in} out logfile || fail=1 | |
109 | cmp ${in} out || fail=1 | |
110 | printf . | |
111 | ||
133 | "${DDRESCUE}" -q -R ${in} out logfile || fail=1 | |
134 | cmp ${in} out || fail=1 | |
135 | printf . | |
136 | ||
137 | rm -f out | |
138 | fail2=0 | |
139 | for i in 0 8000 16000 24000 32000 ; do | |
140 | "${DDRESCUE}" -q -i${i} -s4000 -m ${logfile1} ${in} out || fail2=1 | |
141 | done | |
142 | cmp -s ${in} out && fail2=1 | |
143 | for i in 4000 12000 20000 28000 36000 ; do | |
144 | "${DDRESCUE}" -q -i${i} -s4000 -m ${logfile1} ${in} out || fail2=1 | |
145 | done | |
146 | cmp ${in1} out || fail2=1 | |
147 | for i in 0 8000 16000 24000 32000 ; do | |
148 | "${DDRESCUE}" -q -i${i} -s4000 -m ${logfile2} ${in2} out || fail2=1 | |
149 | done | |
150 | cmp -s ${in} out && fail2=1 | |
151 | for i in 4000 12000 20000 28000 36000 ; do | |
152 | "${DDRESCUE}" -q -i${i} -s4000 -m ${logfile2} ${in2} out || fail2=1 | |
153 | done | |
154 | cmp ${in} out || fail2=1 | |
155 | if [ ${fail2} = 0 ] ; then printf . ; else printf - ; fail=1 ; fi | |
156 | ||
157 | rm -f logfile | |
112 | 158 | cat ${in1} > out || framework_failure |
113 | rm logfile || framework_failure | |
114 | "${DDRESCUE}" -qg ${in} out logfile || fail=1 | |
159 | "${DDRESCUE}" -q -G ${in} out logfile || fail=1 | |
115 | 160 | "${DDRESCUE}" -q ${in2} out logfile || fail=1 |
116 | 161 | cmp ${in} out || fail=1 |
117 | 162 | printf . |
118 | 163 | |
164 | rm -f logfile | |
119 | 165 | cat ${in} > copy || framework_failure |
120 | 166 | printf "garbage" >> copy || framework_failure |
121 | 167 | cat ${in2} > out || framework_failure |
122 | rm logfile || framework_failure | |
123 | "${DDRESCUE}" -qt -x 35744 ${in1} copy || fail=1 | |
124 | "${DDRESCUE}" -qg ${in} out logfile || fail=1 | |
125 | "${DDRESCUE}" -qR -T1.5d copy out logfile || fail=1 | |
168 | "${DDRESCUE}" -q -t -x 35744 ${in1} copy || fail=1 | |
169 | "${DDRESCUE}" -q -G ${in} out logfile || fail=1 | |
170 | "${DDRESCUE}" -q -R -T1.5d copy out logfile || fail=1 | |
126 | 171 | cmp ${in} out || fail=1 |
127 | 172 | printf . |
128 | 173 | |
129 | 174 | printf "\ntesting ddrescuelog-%s..." "$2" |
130 | 175 | |
131 | 176 | "${DDRESCUELOG}" -q logfile |
132 | if [ $? = 0 ] ; then fail=1 ; printf - ; else printf . ; fi | |
177 | if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi | |
133 | 178 | "${DDRESCUELOG}" -q -d |
134 | if [ $? = 0 ] ; then fail=1 ; printf - ; else printf . ; fi | |
179 | if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi | |
135 | 180 | "${DDRESCUELOG}" -q -t -d logfile |
136 | if [ $? = 0 ] ; then fail=1 ; printf - ; else printf . ; fi | |
181 | if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi | |
182 | "${DDRESCUELOG}" -q -m ${logfile2i} -t logfile | |
183 | if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi | |
184 | ||
185 | "${DDRESCUELOG}" -a '?,+' -i3072 ${logfile1} > logfile | |
186 | "${DDRESCUELOG}" -D logfile | |
187 | if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi | |
188 | "${DDRESCUELOG}" -a '?,+' -i2048 -s1024 logfile > logfile2 | |
189 | "${DDRESCUELOG}" -d logfile2 | |
190 | if [ $? = 0 ] ; then printf . ; else printf - ; fail=1 ; fi | |
137 | 191 | |
138 | 192 | "${DDRESCUELOG}" -b2048 -l+ ${logfile1} > out || fail=1 |
139 | cat out | "${DDRESCUELOG}" -b2048 -fc logfile || fail=1 | |
193 | "${DDRESCUELOG}" -b2048 -f -c logfile < out || fail=1 | |
140 | 194 | "${DDRESCUELOG}" -b2048 -l+ logfile > copy || fail=1 |
141 | 195 | cmp out copy || fail=1 |
142 | 196 | printf . |
143 | cat out | "${DDRESCUELOG}" -b2048 -s35744 -fc?+ logfile || fail=1 | |
197 | "${DDRESCUELOG}" -q -p ${logfile1} logfile | |
198 | if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi | |
199 | "${DDRESCUELOG}" -P ${logfile1} logfile || fail=1 | |
200 | printf . | |
201 | "${DDRESCUELOG}" -b2048 -s35744 -f -c?+ logfile < out || fail=1 | |
144 | 202 | "${DDRESCUELOG}" -p ${logfile2} logfile || fail=1 |
145 | 203 | printf . |
146 | cat out | "${DDRESCUELOG}" -b2048 -fc?+ logfile || fail=1 | |
204 | "${DDRESCUELOG}" -b2048 -f -c?+ logfile < out || fail=1 | |
147 | 205 | "${DDRESCUELOG}" -s35744 -p ${logfile2} logfile || fail=1 |
148 | 206 | printf . |
149 | "${DDRESCUELOG}" -s35745 -q -p ${logfile2} logfile | |
150 | if [ $? = 0 ] ; then fail=1 ; printf - ; else printf . ; fi | |
207 | "${DDRESCUELOG}" -q -s35745 -p ${logfile2} logfile | |
208 | if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi | |
209 | ||
210 | printf "10\n12\n14\n16\n" | "${DDRESCUELOG}" -b2048 -f -c+? logfile || fail=1 | |
211 | "${DDRESCUELOG}" -q -p logfile ${logfile1} | |
212 | if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi | |
213 | "${DDRESCUELOG}" -q -i0x5000 -p logfile ${logfile1} | |
214 | if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi | |
215 | "${DDRESCUELOG}" -i0x5000 -s0x3800 -p logfile ${logfile1} || fail=1 | |
216 | printf . | |
217 | ||
218 | "${DDRESCUELOG}" -C ${logfile2i} > logfile || fail=1 | |
219 | "${DDRESCUELOG}" -p ${logfile2} logfile || fail=1 | |
220 | printf . | |
151 | 221 | |
152 | 222 | cat ${logfile1} > logfile || framework_failure |
153 | 223 | "${DDRESCUELOG}" -i1024 -s2048 -d logfile |
154 | if [ $? = 0 ] ; then fail=1 ; printf - ; else printf . ; fi | |
224 | if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi | |
155 | 225 | "${DDRESCUELOG}" -i1024 -s1024 -d logfile || fail=1 |
156 | 226 | printf . |
157 | "${DDRESCUELOG}" -i1024 -s1024 -d -q logfile | |
158 | if [ $? = 0 ] ; then fail=1 ; printf - ; else printf . ; fi | |
227 | "${DDRESCUELOG}" -q -i1024 -s1024 -d logfile | |
228 | if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi | |
159 | 229 | |
160 | 230 | cat ${logfile2} > logfile || framework_failure |
161 | 231 | "${DDRESCUELOG}" -m ${logfile1} -D logfile |
162 | if [ $? = 0 ] ; then fail=1 ; printf - ; else printf . ; fi | |
163 | "${DDRESCUELOG}" -m ${logfile2} -D logfile || fail=1 | |
232 | if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi | |
233 | "${DDRESCUELOG}" -L -m ${logfile2i} -D logfile || fail=1 | |
164 | 234 | printf . |
165 | 235 | "${DDRESCUELOG}" -i1024 -s2048 -d logfile |
166 | if [ $? = 0 ] ; then fail=1 ; printf - ; else printf . ; fi | |
236 | if [ $? = 1 ] ; then printf . ; else printf - ; fail=1 ; fi | |
167 | 237 | "${DDRESCUELOG}" -i2048 -s2048 -d logfile || fail=1 |
168 | 238 | printf . |
169 | 239 | |
170 | cat ${logfile1} > logfile || framework_failure | |
171 | "${DDRESCUELOG}" -b2048 -l+ logfile > out || fail=1 | |
240 | "${DDRESCUELOG}" -b2048 -l+ ${logfile1} > out || fail=1 | |
172 | 241 | printf "0\n2\n4\n6\n8\n10\n12\n14\n16\n" > copy || framework_failure |
173 | 242 | cmp out copy || fail=1 |
174 | 243 | printf . |
175 | "${DDRESCUELOG}" -b2048 -l?- logfile > out || fail=1 | |
244 | "${DDRESCUELOG}" -b2048 -l?- ${logfile1} > out || fail=1 | |
176 | 245 | printf "1\n3\n5\n7\n9\n11\n13\n15\n17\n" > copy || framework_failure |
177 | 246 | cmp out copy || fail=1 |
178 | 247 | printf . |
179 | "${DDRESCUELOG}" -b2048 -l+ -i6KiB -o0 -s16KiB logfile > out || fail=1 | |
248 | "${DDRESCUELOG}" -b2048 -l+ -i0x1800 -o0 -s0x4000 ${logfile1} > out || fail=1 | |
180 | 249 | printf "1\n3\n5\n7\n" > copy || framework_failure |
181 | 250 | cmp out copy || fail=1 |
182 | 251 | printf . |
190 | 259 | printf "1\n3\n5\n7\n9\n11\n13\n15\n17\n" > copy || framework_failure |
191 | 260 | cmp out copy || fail=1 |
192 | 261 | printf . |
193 | "${DDRESCUELOG}" -b2048 -l+ -i2048 -o0 -s16KiB logfile > out || fail=1 | |
262 | "${DDRESCUELOG}" -b2048 -l+ -i2048 -o0 -s0x4000 logfile > out || fail=1 | |
194 | 263 | printf "1\n3\n5\n7\n" > copy || framework_failure |
195 | 264 | cmp out copy || fail=1 |
196 | 265 | printf . |
197 | 266 | |
198 | "${DDRESCUELOG}" -b2048 -l+ ${logfile1} > out || fail=1 | |
199 | "${DDRESCUELOG}" -x ${logfile1} ${logfile1} > logfile || fail=1 | |
200 | "${DDRESCUELOG}" -b2048 -l- logfile > copy || fail=1 | |
201 | cmp out copy || fail=1 | |
202 | printf . | |
203 | "${DDRESCUELOG}" -y ${logfile2} ${logfile1} > logfile || fail=1 | |
204 | "${DDRESCUELOG}" -b2048 -l- logfile > copy || fail=1 | |
205 | cmp out copy || fail=1 | |
206 | printf . | |
207 | "${DDRESCUELOG}" -z ${logfile1} ${logfile2} > logfile || fail=1 | |
208 | "${DDRESCUELOG}" -d logfile || fail=1 | |
209 | printf . | |
267 | "${DDRESCUELOG}" -q -P ${logfile2i} ${logfile2} | |
268 | if [ $? = 2 ] ; then printf . ; else printf - ; fail=1 ; fi | |
269 | "${DDRESCUELOG}" -L -P ${logfile2i} ${logfile2} || fail=1 | |
270 | printf . | |
271 | ||
272 | fail2=0 # test XOR | |
273 | for i in ${logfile1} ${logfile2} ${logfile3} ${logfile4} ${logfile5} ; do | |
274 | for j in ${logfile1} ${logfile2} ${logfile3} ${logfile4} ${logfile5} ; do | |
275 | "${DDRESCUELOG}" -x ${j} ${i} > out || fail2=1 | |
276 | "${DDRESCUELOG}" -x ${i} ${j} > copy || fail2=1 | |
277 | "${DDRESCUELOG}" -P out copy || fail2=1 | |
278 | "${DDRESCUELOG}" -x ${j} out > copy || fail2=1 | |
279 | "${DDRESCUELOG}" -P ${i} copy || fail2=1 | |
280 | done | |
281 | done | |
282 | if [ ${fail2} = 0 ] ; then printf . ; else printf - ; fail=1 ; fi | |
283 | ||
284 | fail2=0 | |
285 | "${DDRESCUELOG}" -x ${logfile1} ${logfile2} > out || fail2=1 | |
286 | "${DDRESCUELOG}" -x ${logfile2} ${logfile1} > copy || fail2=1 | |
287 | "${DDRESCUELOG}" -p out copy || fail2=1 | |
288 | "${DDRESCUELOG}" -d out || fail2=1 | |
289 | "${DDRESCUELOG}" -d copy || fail2=1 | |
290 | "${DDRESCUELOG}" -x ${logfile1} ${blank} > out || fail2=1 | |
291 | "${DDRESCUELOG}" -x ${blank} ${logfile1} > copy || fail2=1 | |
292 | "${DDRESCUELOG}" -p out copy || fail2=1 | |
293 | "${DDRESCUELOG}" -p out ${logfile1} || fail2=1 | |
294 | "${DDRESCUELOG}" -p ${logfile1} copy || fail2=1 | |
295 | "${DDRESCUELOG}" -x ${logfile2} ${logfile2} > logfile || fail2=1 | |
296 | "${DDRESCUELOG}" -P ${blank} logfile || fail2=1 | |
297 | "${DDRESCUELOG}" -x ${logfile1} ${logfile1} > logfile || fail2=1 | |
298 | "${DDRESCUELOG}" -P ${blank} logfile || fail2=1 | |
299 | "${DDRESCUELOG}" -b2048 -l+ ${logfile1} > out || fail2=1 | |
300 | "${DDRESCUELOG}" -b2048 -l- logfile > copy || fail2=1 | |
301 | cmp out copy || fail2=1 | |
302 | "${DDRESCUELOG}" -b2048 -i0x2000 -s0x2800 -l+ ${logfile1} > out || fail2=1 | |
303 | "${DDRESCUELOG}" -i0x1800 -s0x3800 -x ${logfile1} ${logfile1} > logfile || fail2=1 | |
304 | "${DDRESCUELOG}" -b2048 -l- logfile > copy || fail2=1 | |
305 | cmp out copy || fail2=1 | |
306 | if [ ${fail2} = 0 ] ; then printf . ; else printf - ; fail=1 ; fi | |
307 | ||
308 | fail2=0 | |
309 | "${DDRESCUELOG}" -x ${logfile3} ${logfile4} > out || fail2=1 | |
310 | "${DDRESCUELOG}" -x ${logfile4} ${logfile3} > copy || fail2=1 | |
311 | "${DDRESCUELOG}" -p out copy || fail2=1 | |
312 | "${DDRESCUELOG}" -x ${logfile3} ${logfile5} > out || fail2=1 | |
313 | "${DDRESCUELOG}" -x ${logfile5} ${logfile3} > copy || fail2=1 | |
314 | "${DDRESCUELOG}" -p out copy || fail2=1 | |
315 | "${DDRESCUELOG}" -x ${logfile4} ${logfile5} > out || fail2=1 | |
316 | "${DDRESCUELOG}" -x ${logfile5} ${logfile4} > copy || fail2=1 | |
317 | "${DDRESCUELOG}" -p out copy || fail2=1 | |
318 | if [ ${fail2} = 0 ] ; then printf . ; else printf - ; fail=1 ; fi | |
319 | ||
320 | fail2=0 | |
321 | "${DDRESCUELOG}" -x ${logfile3} ${logfile4} > out || fail2=1 | |
322 | "${DDRESCUELOG}" -D out && fail2=1 | |
323 | "${DDRESCUELOG}" -x out ${logfile5} > logfile || fail2=1 | |
324 | "${DDRESCUELOG}" -d logfile || fail2=1 | |
325 | ||
326 | "${DDRESCUELOG}" -x ${logfile3} ${logfile5} > out || fail2=1 | |
327 | "${DDRESCUELOG}" -D out && fail2=1 | |
328 | "${DDRESCUELOG}" -x out ${logfile4} > logfile || fail2=1 | |
329 | "${DDRESCUELOG}" -d logfile || fail2=1 | |
330 | ||
331 | "${DDRESCUELOG}" -x ${logfile4} ${logfile3} > out || fail2=1 | |
332 | "${DDRESCUELOG}" -D out && fail2=1 | |
333 | "${DDRESCUELOG}" -x out ${logfile5} > logfile || fail2=1 | |
334 | "${DDRESCUELOG}" -d logfile || fail2=1 | |
335 | ||
336 | "${DDRESCUELOG}" -x ${logfile4} ${logfile5} > out || fail2=1 | |
337 | "${DDRESCUELOG}" -D out && fail2=1 | |
338 | "${DDRESCUELOG}" -x out ${logfile3} > logfile || fail2=1 | |
339 | "${DDRESCUELOG}" -d logfile || fail2=1 | |
340 | ||
341 | "${DDRESCUELOG}" -x ${logfile5} ${logfile3} > out || fail2=1 | |
342 | "${DDRESCUELOG}" -D out && fail2=1 | |
343 | "${DDRESCUELOG}" -x out ${logfile4} > logfile || fail2=1 | |
344 | "${DDRESCUELOG}" -d logfile || fail2=1 | |
345 | ||
346 | "${DDRESCUELOG}" -x ${logfile5} ${logfile4} > out || fail2=1 | |
347 | "${DDRESCUELOG}" -D out && fail2=1 | |
348 | "${DDRESCUELOG}" -x out ${logfile3} > logfile || fail2=1 | |
349 | "${DDRESCUELOG}" -d logfile || fail2=1 | |
350 | if [ ${fail2} = 0 ] ; then printf . ; else printf - ; fail=1 ; fi | |
351 | ||
352 | fail2=0 # test AND | |
353 | for i in ${logfile1} ${logfile2} ${logfile3} ${logfile4} ${logfile5} ; do | |
354 | for j in ${logfile1} ${logfile2} ${logfile3} ${logfile4} ${logfile5} ; do | |
355 | "${DDRESCUELOG}" -y ${j} ${i} > out || fail2=1 | |
356 | "${DDRESCUELOG}" -y ${i} ${j} > copy || fail2=1 | |
357 | "${DDRESCUELOG}" -P out copy || fail2=1 | |
358 | done | |
359 | done | |
360 | if [ ${fail2} = 0 ] ; then printf . ; else printf - ; fail=1 ; fi | |
361 | ||
362 | fail2=0 | |
363 | "${DDRESCUELOG}" -b2048 -l+ ${logfile1} > out || fail2=1 | |
364 | "${DDRESCUELOG}" -y ${logfile1} ${logfile2} > logfile || fail2=1 | |
365 | "${DDRESCUELOG}" -P ${blank} logfile || fail2=1 | |
366 | "${DDRESCUELOG}" -b2048 -l? logfile > copy || fail2=1 | |
367 | cmp out copy || fail2=1 | |
368 | "${DDRESCUELOG}" -y ${logfile2} ${logfile1} > logfile || fail2=1 | |
369 | "${DDRESCUELOG}" -P ${blank} logfile || fail2=1 | |
370 | "${DDRESCUELOG}" -b2048 -l- logfile > copy || fail2=1 | |
371 | cmp out copy || fail2=1 | |
372 | "${DDRESCUELOG}" -b2048 -i0x2000 -s0x2800 -l+ ${logfile1} > out || fail2=1 | |
373 | "${DDRESCUELOG}" -i0x1800 -s0x3800 -y ${logfile2} ${logfile1} > logfile || fail2=1 | |
374 | "${DDRESCUELOG}" -b2048 -l- logfile > copy || fail2=1 | |
375 | cmp out copy || fail2=1 | |
376 | if [ ${fail2} = 0 ] ; then printf . ; else printf - ; fail=1 ; fi | |
377 | ||
378 | fail2=0 | |
379 | "${DDRESCUELOG}" -y ${logfile3} ${logfile4} > out || fail2=1 | |
380 | "${DDRESCUELOG}" -P ${blank} out || fail2=1 | |
381 | "${DDRESCUELOG}" -y ${logfile3} ${logfile5} > out || fail2=1 | |
382 | "${DDRESCUELOG}" -P ${blank} out || fail2=1 | |
383 | "${DDRESCUELOG}" -y ${logfile4} ${logfile5} > out || fail2=1 | |
384 | "${DDRESCUELOG}" -P ${blank} out || fail2=1 | |
385 | if [ ${fail2} = 0 ] ; then printf . ; else printf - ; fail=1 ; fi | |
386 | ||
387 | fail2=0 | |
388 | "${DDRESCUELOG}" -i0x2000 -s0x2800 -z ${logfile2} ${logfile1} > logfile || fail2=1 | |
389 | "${DDRESCUELOG}" -D logfile | |
390 | if [ $? != 1 ] ; then fail2=1 ; fi | |
391 | "${DDRESCUELOG}" -i0x1C00 -s0x2C00 -D logfile | |
392 | if [ $? != 1 ] ; then fail2=1 ; fi | |
393 | "${DDRESCUELOG}" -i0x2000 -s0x2C00 -D logfile | |
394 | if [ $? != 1 ] ; then fail2=1 ; fi | |
395 | "${DDRESCUELOG}" -i0x2000 -s0x2800 -d logfile | |
396 | if [ $? != 0 ] ; then fail2=1 ; fi | |
397 | if [ ${fail2} = 0 ] ; then printf . ; else printf - ; fail=1 ; fi | |
398 | ||
399 | fail2=0 # test OR | |
400 | for i in ${logfile1} ${logfile2} ${logfile3} ${logfile4} ${logfile5} ; do | |
401 | for j in ${logfile1} ${logfile2} ${logfile3} ${logfile4} ${logfile5} ; do | |
402 | "${DDRESCUELOG}" -z ${j} ${i} > out || fail2=1 | |
403 | "${DDRESCUELOG}" -z ${i} ${j} > copy || fail2=1 | |
404 | "${DDRESCUELOG}" -P out copy || fail2=1 | |
405 | done | |
406 | done | |
407 | if [ ${fail2} = 0 ] ; then printf . ; else printf - ; fail=1 ; fi | |
408 | ||
409 | fail2=0 | |
410 | "${DDRESCUELOG}" -z ${logfile1} ${logfile2} > out || fail2=1 | |
411 | "${DDRESCUELOG}" -z ${logfile2} ${logfile1} > copy || fail2=1 | |
412 | "${DDRESCUELOG}" -p out copy || fail2=1 | |
413 | "${DDRESCUELOG}" -d out || fail2=1 | |
414 | "${DDRESCUELOG}" -d copy || fail2=1 | |
415 | "${DDRESCUELOG}" -z ${logfile1} ${blank} > out || fail2=1 | |
416 | "${DDRESCUELOG}" -z ${blank} ${logfile1} > copy || fail2=1 | |
417 | "${DDRESCUELOG}" -p out copy || fail2=1 | |
418 | "${DDRESCUELOG}" -p out ${logfile1} || fail2=1 | |
419 | "${DDRESCUELOG}" -p ${logfile1} copy || fail2=1 | |
420 | "${DDRESCUELOG}" -z ${logfile3} ${logfile4} > out || fail2=1 | |
421 | "${DDRESCUELOG}" -z ${logfile4} ${logfile3} > copy || fail2=1 | |
422 | "${DDRESCUELOG}" -p out copy || fail2=1 | |
423 | "${DDRESCUELOG}" -z ${logfile3} ${logfile5} > out || fail2=1 | |
424 | "${DDRESCUELOG}" -z ${logfile5} ${logfile3} > copy || fail2=1 | |
425 | "${DDRESCUELOG}" -p out copy || fail2=1 | |
426 | "${DDRESCUELOG}" -z ${logfile4} ${logfile5} > out || fail2=1 | |
427 | "${DDRESCUELOG}" -z ${logfile5} ${logfile4} > copy || fail2=1 | |
428 | "${DDRESCUELOG}" -p out copy || fail2=1 | |
429 | if [ ${fail2} = 0 ] ; then printf . ; else printf - ; fail=1 ; fi | |
430 | ||
431 | fail2=0 | |
432 | "${DDRESCUELOG}" -z ${logfile3} ${logfile4} > out || fail2=1 | |
433 | "${DDRESCUELOG}" -D out && fail2=1 | |
434 | "${DDRESCUELOG}" -z out ${logfile5} > logfile || fail2=1 | |
435 | "${DDRESCUELOG}" -d logfile || fail2=1 | |
436 | ||
437 | "${DDRESCUELOG}" -z ${logfile3} ${logfile5} > out || fail2=1 | |
438 | "${DDRESCUELOG}" -D out && fail2=1 | |
439 | "${DDRESCUELOG}" -z out ${logfile4} > logfile || fail2=1 | |
440 | "${DDRESCUELOG}" -d logfile || fail2=1 | |
441 | ||
442 | "${DDRESCUELOG}" -z ${logfile4} ${logfile3} > out || fail2=1 | |
443 | "${DDRESCUELOG}" -D out && fail2=1 | |
444 | "${DDRESCUELOG}" -z out ${logfile5} > logfile || fail2=1 | |
445 | "${DDRESCUELOG}" -d logfile || fail2=1 | |
446 | ||
447 | "${DDRESCUELOG}" -z ${logfile4} ${logfile5} > out || fail2=1 | |
448 | "${DDRESCUELOG}" -D out && fail2=1 | |
449 | "${DDRESCUELOG}" -z out ${logfile3} > logfile || fail2=1 | |
450 | "${DDRESCUELOG}" -d logfile || fail2=1 | |
451 | ||
452 | "${DDRESCUELOG}" -z ${logfile5} ${logfile3} > out || fail2=1 | |
453 | "${DDRESCUELOG}" -D out && fail2=1 | |
454 | "${DDRESCUELOG}" -z out ${logfile4} > logfile || fail2=1 | |
455 | "${DDRESCUELOG}" -d logfile || fail2=1 | |
456 | ||
457 | "${DDRESCUELOG}" -z ${logfile5} ${logfile4} > out || fail2=1 | |
458 | "${DDRESCUELOG}" -D out && fail2=1 | |
459 | "${DDRESCUELOG}" -z out ${logfile3} > logfile || fail2=1 | |
460 | "${DDRESCUELOG}" -d logfile || fail2=1 | |
461 | if [ ${fail2} = 0 ] ; then printf . ; else printf - ; fail=1 ; fi | |
462 | ||
463 | fail2=0 # test ( a && b ) == !( !a || !b ) | |
464 | for i in ${logfile1} ${logfile2} ${logfile3} ${logfile4} ${logfile5} ; do | |
465 | for j in ${logfile1} ${logfile2} ${logfile3} ${logfile4} ${logfile5} ; do | |
466 | "${DDRESCUELOG}" -n ${i} > na || fail2=1 | |
467 | "${DDRESCUELOG}" -n ${j} > nb || fail2=1 | |
468 | "${DDRESCUELOG}" -z nb na > out || fail2=1 | |
469 | "${DDRESCUELOG}" -n out > copy || fail2=1 | |
470 | "${DDRESCUELOG}" -y ${j} ${i} > out || fail2=1 | |
471 | "${DDRESCUELOG}" -P out copy || fail2=1 | |
472 | done | |
473 | done | |
474 | if [ ${fail2} = 0 ] ; then printf . ; else printf - ; fail=1 ; fi | |
210 | 475 | |
211 | 476 | echo |
212 | 477 | if [ ${fail} = 0 ] ; then |
0 | # Rescue Logfile. Created by GNU ddrescue version 1.11 | |
1 | # current_pos current_status | |
2 | 0x00000000 + | |
3 | # pos size status | |
4 | 0x00000800 0x00000800 + | |
5 | 0x00001800 0x00000800 + | |
6 | 0x00002800 0x00000800 + | |
7 | 0x00003800 0x00000800 + | |
8 | 0x00004800 0x00000800 + | |
9 | 0x00005800 0x00000800 + | |
10 | 0x00006800 0x00000800 + | |
11 | 0x00007800 0x00000800 + | |
12 | 0x00008800 0x000003A0 + |
0 | # Rescue Logfile. Created by GNU ddrescue version 1.11 | |
1 | # current_pos current_status | |
2 | 0x00000000 + | |
3 | # pos size status | |
4 | 0x00000000 0x00000800 + | |
5 | 0x00000800 0x00001000 ? | |
6 | 0x00001800 0x00000800 + | |
7 | 0x00002000 0x00001000 ? | |
8 | 0x00003000 0x00000800 + | |
9 | 0x00003800 0x00001000 ? | |
10 | 0x00004800 0x00000800 + | |
11 | 0x00005000 0x00001000 ? | |
12 | 0x00006000 0x00000800 + | |
13 | 0x00006800 0x00001000 ? | |
14 | 0x00007800 0x00000800 + | |
15 | 0x00008000 0x00000BA0 ? |
0 | # Rescue Logfile. Created by GNU ddrescue version 1.11 | |
1 | # current_pos current_status | |
2 | 0x00000000 + | |
3 | # pos size status | |
4 | 0x00000000 0x00000800 ? | |
5 | 0x00000800 0x00000800 + | |
6 | 0x00001000 0x00001000 ? | |
7 | 0x00002000 0x00000800 + | |
8 | 0x00002800 0x00001000 ? | |
9 | 0x00003800 0x00000800 + | |
10 | 0x00004000 0x00001000 ? | |
11 | 0x00005000 0x00000800 + | |
12 | 0x00005800 0x00001000 ? | |
13 | 0x00006800 0x00000800 + | |
14 | 0x00007000 0x00001000 ? | |
15 | 0x00008000 0x00000800 + | |
16 | 0x00008800 0x000003A0 ? |
0 | # Rescue Logfile. Created by GNU ddrescue version 1.11 | |
1 | # current_pos current_status | |
2 | 0x00000000 + | |
3 | # pos size status | |
4 | 0x00000000 0x00001000 ? | |
5 | 0x00001000 0x00000800 + | |
6 | 0x00001800 0x00001000 ? | |
7 | 0x00002800 0x00000800 + | |
8 | 0x00003000 0x00001000 ? | |
9 | 0x00004000 0x00000800 + | |
10 | 0x00004800 0x00001000 ? | |
11 | 0x00005800 0x00000800 + | |
12 | 0x00006000 0x00001000 ? | |
13 | 0x00007000 0x00000800 + | |
14 | 0x00007800 0x00001000 ? | |
15 | 0x00008800 0x000003A0 + |