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 );
   };

Reply via email to