Are there any plans to support other output formats than raw images?
I recently did some tests with AFF format
(https://github.com/sshock/AFFLIBv3) to provide it for Cygwin as an
enhancement to sleuthkit.
AFF is a segmented format which supports compression, encryption and
signing. Using it instead of raw images could significantly reduce the
size of output images if the disk contents is compressible. AFF
Development packages are available in all major distributions. The
format is also supported by forensic tools like sleuthkit.
To check whether this also works with the seek+write patterns of
ddrescue, I tested with the attached quick proof-of-concept patch. It
only supports basic ddrescue operation without special options. The
extra code I also added to fake random read errors is not included.
It should be straightforward to optionally add alternative output
formats by introducing an abstract output layer with
open/close/seek/write/allocate/flush member functions.
OT: Recently I had a very interesting real word testcase for ddrescue:
A 4TB Seagate ST4000NM0265 with massive seek problems could be
completely recovered by running ddrescue (Cygwin) for about two weeks
24x7, except for one single sector. Required changing ddrescue
parameters several times and reduction of disk's own ERC time (with
smartctl).
--
Regards,
Christian
diff --git a/Makefile.in b/Makefile.in
index a789fcf..fbe80af 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -21,7 +21,7 @@ logobjs = arg_parser.o block.o mapfile.o ddrescuelog.o
all : $(progname) ddrescuelog
$(progname) : $(objs)
- $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $(objs)
+ $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $(objs) -lafflib
ddrescuelog : $(logobjs)
$(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $(logobjs)
diff --git a/command_mode.cc b/command_mode.cc
index 40b065f..168b647 100644
--- a/command_mode.cc
+++ b/command_mode.cc
@@ -98,6 +98,7 @@ int Rescuebook::status_command( const char * const command )
const
//
int Rescuebook::do_commands( const int ides, const int odes )
{
+#if 0
ides_ = ides; odes_ = odes;
// set_signals(); // ignore signals
@@ -148,4 +149,8 @@ int Rescuebook::do_commands( const int ides, const int odes
)
{ show_file_error( oname_, "Error closing outfile", errno );
if( retval == 0 ) retval = 1; }
return retval;
+#else
+ (void)ides; (void)odes;
+ return 1;
+#endif
}
diff --git a/main.cc b/main.cc
index 916746d..8fe0a5c 100644
--- a/main.cc
+++ b/main.cc
@@ -520,18 +520,18 @@ int do_rescue( const long long offset, Domain & domain,
if( ask && !user_agrees_ids( rescuebook, iname, oname, insize, ides ) )
return 1;
- const int odes =
- open( oname, ( rescuebook.compare_before_write ? O_RDWR : O_WRONLY ) |
- O_CREAT | o_direct_out | o_trunc | O_BINARY, outmode );
- if( odes < 0 )
+ AFFILE * aff =
+ af_open( oname, O_RDWR | O_CREAT, outmode );
+ if( !aff )
{ show_file_error( oname, "Can't open output file", errno ); return 1; }
+#if 0
if( lseek( odes, 0, SEEK_SET ) )
{ show_file_error( oname, "Output file is not seekable." ); return 1; }
if( preallocate && lseek( odes, 0, SEEK_END ) - rescuebook.offset() <
rescuebook.domain().end() )
{
#if defined _POSIX_ADVISORY_INFO && _POSIX_ADVISORY_INFO > 0
- if( posix_fallocate( odes, rescuebook.domain().pos() + rescuebook.offset(),
+ if( posix_fallocate( aff, rescuebook.domain().pos() + rescuebook.offset(),
rescuebook.domain().size() ) != 0 )
{ show_file_error( oname, "Can't preallocate output file", errno );
return 1; }
@@ -539,12 +539,15 @@ int do_rescue( const long long offset, Domain & domain,
show_file_error( oname, "warning: preallocation not available." );
#endif
}
+#endif
if( rescuebook.filename() && !rescuebook.mapfile_exists() &&
!rescuebook.write_mapfile( 0, true ) )
{ show_file_error( mapname, "Can't create mapfile", errno ); return 1; }
+#if 0
if( command_mode ) return rescuebook.do_commands( ides, odes );
+#endif
if( !event_logger.open_file() )
{ show_file_error( event_logger.filename(),
@@ -622,7 +625,7 @@ int do_rescue( const long long offset, Domain & domain,
}
std::fputc( '\n', stdout );
}
- return rescuebook.do_rescue( ides, odes );
+ return rescuebook.do_rescue( ides, aff );
}
diff --git a/rescuebook.cc b/rescuebook.cc
index 7186c77..cbf9654 100644
--- a/rescuebook.cc
+++ b/rescuebook.cc
@@ -51,6 +51,22 @@ long long round_up( long long size, const int hardbs )
return size;
}
+int writeblockp( AFFILE * aff, const uint8_t * const buf, const int size,
+ const long long pos )
+ {
+ int sz = 0;
+ errno = 0;
+ af_seek( aff, pos, SEEK_SET ); // never fails
+ while( sz < size )
+ {
+ errno = 0;
+ const int n = af_write( aff, const_cast<uint8_t *>(buf) + sz, size - sz
);
+ if( n > 0 ) sz += n;
+ else if( n < 0 && errno != EINTR ) break;
+ }
+ return sz;
+ }
+
} // end namespace
@@ -90,19 +106,23 @@ bool Rescuebook::extend_outfile_size()
if( min_outfile_size > 0 || sparse_size > 0 )
{
const long long min_size = std::max( min_outfile_size, sparse_size );
- const long long size = lseek( odes_, 0, SEEK_END );
+ const long long size = af_seek( oaff_, 0, SEEK_END );
if( size < 0 ) return false;
if( min_size > size )
{
+#if 0
int ret;
do ret = ftruncate( odes_, min_size );
while( ret != 0 && errno == EINTR );
- if( ret != 0 || lseek( odes_, 0, SEEK_END ) != min_size )
+ if( ret != 0 || lseek( oaff_, 0, SEEK_END ) != min_size )
+#endif
{
const uint8_t zero = 0; // if ftruncate fails, write a
zero
- if( writeblockp( odes_, &zero, 1, min_size - 1 ) != 1 ) return false;
+ if( writeblockp( oaff_, &zero, 1, min_size - 1 ) != 1 ) return false;
}
+#if 0
fsync( odes_ );
+#endif
}
}
return true;
@@ -163,11 +183,15 @@ int Rescuebook::copy_block( const Block & b, int &
copied_size, int & error_size
if( end > sparse_size ) sparse_size = end;
}
else
+#if 0
if( !compare_before_write ||
readblockp( odes_, iobuf2(), copied_size, pos ) != copied_size ||
std::memcmp( iobuf(), iobuf2(), copied_size ) != 0 )
- if( writeblockp( odes_, iobuf(), copied_size, pos ) != copied_size ||
- ( synchronous_ && fsync( odes_ ) != 0 && errno != EINVAL ) )
+#endif
+ if( writeblockp( oaff_, iobuf(), copied_size, pos ) != copied_size )
+#if 0
+ || ( synchronous_ && fsync( oaff_ ) != 0 && errno != EINVAL ) )
+#endif
{ final_msg( oname_, "Write error", errno ); return 1; }
}
else iobuf_ipos = -1;
@@ -379,7 +403,7 @@ int Rescuebook::fcopy_non_tried( const char * const msg,
const int pass,
}
}
else if( error_size == 0 && copied_size > 0 ) eskip_size = skipbs; // reset
- if( !update_mapfile( odes_ ) ) return -2;
+ if( !update_mapfile( -1 ) ) return -2;
}
if( !block_found ) return 0;
if( block_processed ) show_status( -1, msg, true ); // update at end of pass
@@ -453,7 +477,7 @@ int Rescuebook::rcopy_non_tried( const char * const msg,
const int pass,
}
}
else if( error_size == 0 && copied_size > 0 ) eskip_size = skipbs; // reset
- if( !update_mapfile( odes_ ) ) return -2;
+ if( !update_mapfile( -1 ) ) return -2;
}
if( !block_found ) return 0;
if( block_processed ) show_status( -1, msg, true ); // update at end of pass
@@ -501,7 +525,7 @@ int Rescuebook::trim_errors()
update_rates();
if( error_size > 0 )
{ error_found = true; if( pause_on_error > 0 ) do_pause_on_error(); }
- if( !update_mapfile( odes_ ) ) return -2;
+ if( !update_mapfile( -1 ) ) return -2;
}
error_found = rbad;
while( pos < end && !error_found ) // trim trailing edge
@@ -517,7 +541,7 @@ int Rescuebook::trim_errors()
update_rates();
if( error_size > 0 )
{ error_found = true; if( pause_on_error > 0 ) do_pause_on_error(); }
- if( !update_mapfile( odes_ ) ) return -2;
+ if( !update_mapfile( -1 ) ) return -2;
}
if( pos < end )
{
@@ -562,7 +586,7 @@ int Rescuebook::scrape_errors()
if( retval ) return retval;
update_rates();
if( error_size > 0 && pause_on_error > 0 ) do_pause_on_error();
- if( !update_mapfile( odes_ ) ) return -2;
+ if( !update_mapfile( -1 ) ) return -2;
}
}
show_status( -1, msg, true ); // update at end of pass
@@ -629,7 +653,7 @@ int Rescuebook::fcopy_errors( const char * const msg, const
int pass,
if( retval ) return retval;
update_rates();
if( error_size > 0 && pause_on_error > 0 ) do_pause_on_error();
- if( !update_mapfile( odes_ ) ) return -2;
+ if( !update_mapfile( -1 ) ) return -2;
}
if( !block_found ) return 0;
show_status( -1, msg, true ); // update at end of pass
@@ -666,7 +690,7 @@ int Rescuebook::rcopy_errors( const char * const msg, const
int pass,
if( retval ) return retval;
update_rates();
if( error_size > 0 && pause_on_error > 0 ) do_pause_on_error();
- if( !update_mapfile( odes_ ) ) return -2;
+ if( !update_mapfile( -1 ) ) return -2;
}
if( !block_found ) return 0;
show_status( -1, msg, true ); // update at end of pass
@@ -894,9 +918,9 @@ Rescuebook::Rescuebook( const long long offset, const long
long insize,
// Return values: 0 OK, != 0 error.
//
-int Rescuebook::do_rescue( const int ides, const int odes )
+int Rescuebook::do_rescue( const int ides, AFFILE * const oaff )
{
- ides_ = ides; odes_ = odes;
+ ides_ = ides; oaff_ = oaff;
set_signals();
if( verbosity >= 0 )
{
@@ -933,7 +957,9 @@ int Rescuebook::do_rescue( const int ides, const int odes )
retval = scrape_errors();
if( retval == 0 && bad_size && max_retries != 0 && !errors_or_timeout() )
retval = copy_errors();
+#if 0
fsync( odes_ ); // prevent early exit if kernel caches writes
+#endif
if( !rates_updated ) update_rates( true ); // force update of e_code
show_status( -1, retval ? 0 : "\nFinished", true );
@@ -972,10 +998,10 @@ int Rescuebook::do_rescue( const int ides, const int odes
)
if( retval == 0 ) retval = 1;
}
compact_sblock_vector();
- if( !update_mapfile( odes_, true ) && retval == 0 ) retval = 1;
+ if( !update_mapfile( -1, true ) && retval == 0 ) retval = 1;
}
if( final_msg().size() ) show_error( final_msg().c_str(), final_errno() );
- if( close( odes_ ) != 0 )
+ if( af_close( oaff_ ) != 0 )
{ show_file_error( oname_, "Error closing outfile", errno );
if( retval == 0 ) retval = 1; }
event_logger.print_eor( t1 - t0, percent_rescued(), current_pos(),
diff --git a/rescuebook.h b/rescuebook.h
index 576a2d8..65d6edf 100644
--- a/rescuebook.h
+++ b/rescuebook.h
@@ -15,6 +15,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <afflib/afflib.h>
+
class Sliding_average // Calculates the average of the last N terms
{
unsigned index; // either index or data.size() contain N
@@ -139,7 +141,8 @@ class Rescuebook : public Mapbook, public Rb_options
const char * const iname_, * const oname_;
unsigned long bad_areas; // bad areas found so far
unsigned long read_errors, slow_reads;
- int ides_, odes_; // input and output file descriptors
+ int ides_; // input descriptor
+ AFFILE * oaff_; // AFF file
int e_code; // error code for errors_or_timeout
// 1 rate, 2 bad_areas, 4 timeout,
// 8 other (explained in final_msg),
@@ -201,5 +204,5 @@ public:
~Rescuebook() { delete[] voe_buf; }
int do_commands( const int ides, const int odes );
- int do_rescue( const int ides, const int odes );
+ int do_rescue( const int ides, AFFILE * const oaff );
};