/* GNU ddrescue - Data recovery tool Copyright (C) 2004, 2005, 2006, 2007 Antonio Diaz Diaz. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #define _FILE_OFFSET_BITS 64 #include #include #include #include #include #include #include #include #include #include #include #include "ddrescue.h" namespace { bool volatile interrupted = false; // user pressed Ctrl-C void sighandler( int ) throw() { interrupted = true; } // Returns the number of bytes really read. // If (returned value < size) and (errno == 0), means EOF was reached. // int readblock( const int fd, char * buf, const int size, const long long pos ) throw() { int rest = size; errno = 0; if( lseek( fd, pos, SEEK_SET ) >= 0 ) while( rest > 0 ) { errno = 0; const int n = read( fd, buf + size - rest, rest ); if( n > 0 ) rest -= n; else if( n == 0 ) break; else if( errno != EINTR && errno != EAGAIN ) break; } return ( rest > 0 ) ? size - rest : size; } // Returns the number of bytes really written. // If (returned value < size), it is always an error. // int writeblock( const int fd, const char * buf, const int size, const long long pos ) throw() { int rest = size; errno = 0; if( lseek( fd, pos, SEEK_SET ) >= 0 ) while( rest > 0 ) { errno = 0; const int n = write( fd, buf + size - rest, rest ); if( n > 0 ) rest -= n; else if( errno && errno != EINTR && errno != EAGAIN ) break; } return ( rest > 0 ) ? size - rest : size; } bool block_is_zero( const char * buf, const int size ) throw() { for( int i = 0; i < size; ++i ) if( buf[i] != 0 ) return false; return true; } } // end namespace // Return values: 1 write error, 0 OK, -1 interrupted. // int Fillbook::fill_block( const Block & b ) throw() { current_pos( b.pos() ); if( interrupted ) return -1; if( b.size() <= 0 ) internal_error( "bad size filling a Block" ); const int size = b.size(); if( writeblock( _odes, iobuf(), size, b.pos() ) != size ) { final_msg( "write error" ); final_errno( errno ); return 1; } filled_size += size; remaining_size -= size; return 0; } void Fillbook::show_status( const long long opos, bool force ) throw() { static const char * const up = "\x1b[A"; static long long a_rate = 0, c_rate = 0, first_size = 0, last_size = 0; static long long last_opos = 0; static time_t t0 = 0, t1 = 0; if( t0 == 0 ) { t0 = t1 = std::time( 0 ); first_size = last_size = filled_size; force = true; std::printf( "\n\n\n" ); } if( opos >= 0 ) last_opos = opos; time_t t2 = std::time( 0 ); if( t2 > t1 || force ) { if( t2 > t1 ) { a_rate = ( filled_size - first_size ) / ( t2 - t0 ); c_rate = ( filled_size - last_size ) / ( t2 - t1 ); t1 = t2; last_size = filled_size; } std::printf( "\r%s%s%s", up, up, up ); std::printf( "filled size: %10sB,", format_num( filled_size ) ); std::printf( " filled areas: %6u,", filled_areas ); std::printf( " current rate: %9sB/s\n", format_num( c_rate, 99999 ) ); std::printf( "remain size: %10sB,", format_num( remaining_size ) ); std::printf( " remain areas: %6u,", remaining_areas ); std::printf( " average rate: %9sB/s\n", format_num( a_rate, 99999 ) ); std::printf( "current pos: %10sB\n", format_num( last_opos ) ); std::fflush( stdout ); } } bool Fillbook::read_buffer( const long long ipos, const int ides ) throw() { const int rd = readblock( ides, iobuf(), softbs(), ipos ); if( rd <= 0 ) return false; for( int i = rd; i < softbs(); i *= 2 ) { const int size = std::min( i, softbs() - i ); std::memcpy( iobuf() + i, iobuf(), size ); } return true; } bool Rescuebook::sync_sparse_file() throw() { bool done = true; if( _sparse && sparse_size > 0 ) { const long long size = lseek( _odes, 0, SEEK_END ); if( size < 0 ) done = false; if( sparse_size > size ) { const char zero = 0; if( writeblock( _odes, &zero, 1, sparse_size - 1 ) != 1 ) done = false; fsync( _odes ); } } return done; } int Rescuebook::write_block_or_move( const int fd, const char * buf, const int size, const long long pos ) throw() { if( _sparse && block_is_zero( buf, size ) && lseek( fd, pos + size, SEEK_SET ) >= 0 ) { if( pos + size > sparse_size ) sparse_size = pos + size; return size; } return writeblock( fd, buf, size, pos ); } // Return values: 1 write error, 0 OK, -1 interrupted. // If !OK, copied_size and error_size are set to 0. // If OK && copied_size + error_size < b.size(), it means EOF has been reached. // int Rescuebook::copy_block( const Block & b, int & copied_size, int & error_size ) throw() { current_pos( b.pos() ); copied_size = 0; error_size = 0; if( interrupted ) return -1; if( b.size() <= 0 ) internal_error( "bad size copying a Block" ); const int size = b.size(); if( size > hardbs() ) { copied_size = readblock( _ides, iobuf(), hardbs(), b.pos() ); if( copied_size == hardbs() ) copied_size += readblock( _ides, iobuf() + copied_size, size - copied_size, b.pos() + copied_size ); } else copied_size = readblock( _ides, iobuf(), size, b.pos() ); if( errno ) error_size = size - copied_size; if( copied_size > 0 && write_block_or_move( _odes, iobuf(), copied_size, b.pos() + offset ) != copied_size ) { copied_size = 0; error_size = 0; final_msg( "write error" ); final_errno( errno ); return 1; } return 0; } void Rescuebook::show_status( const long long ipos, const char * msg, bool force ) throw() { static const char * const up = "\x1b[A"; static long long a_rate = 0, c_rate = 0, first_size = 0, last_size = 0; static long long last_ipos = 0; static time_t t0 = 0, t1 = 0; static int oldlen = 0; if( t0 == 0 ) { t0 = t1 = std::time( 0 ); first_size = last_size = recsize; force = true; std::printf( "\n\n\n" ); } if( ipos >= 0 ) last_ipos = ipos; time_t t2 = std::time( 0 ); if( t2 > t1 || force ) { if( t2 > t1 ) { a_rate = ( recsize - first_size ) / ( t2 - t0 ); c_rate = ( recsize - last_size ) / ( t2 - t1 ); t1 = t2; last_size = recsize; } std::printf( "\r%s%s%s", up, up, up ); std::printf( "rescued: %10sB,", format_num( recsize ) ); std::printf( " errsize:%9sB,", format_num( errsize, 99999 ) ); std::printf( " current rate: %9sB/s\n", format_num( c_rate, 99999 ) ); std::printf( " ipos: %10sB, errors: %7u, ", format_num( last_ipos ), errors ); std::printf( " average rate: %9sB/s\n", format_num( a_rate, 99999 ) ); std::printf( " opos: %10sB\n", format_num( last_ipos + offset ) ); int len = oldlen; if( msg ) { len = std::strlen( msg ); if( len ) std::printf( msg ); } for( int i = len; i < oldlen; ++i ) std::fputc( ' ', stdout ); if( len || oldlen ) std::fputc( '\r', stdout ); oldlen = len; std::fflush( stdout ); } } const char * format_num( long long num, long long max, const int set_prefix ) throw() { static const char * const si_prefix[8] = { "k", "M", "G", "T", "P", "E", "Z", "Y" }; static const char * const binary_prefix[8] = { "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi", "Yi" }; static bool si = true; static char buf[16]; if( set_prefix ) si = ( set_prefix > 0 ); const int factor = ( si ) ? 1000 : 1024; const char * const *prefix = ( si ) ? si_prefix : binary_prefix; const char *p = ""; max = std::max( 999LL, std::min( 999999LL, max ) ); for( int i = 0; i < 8 && llabs( num ) > llabs( max ); ++i ) { num /= factor; p = prefix[i]; } snprintf( buf, sizeof( buf ), "%lld %s", num, p ); return buf; } void set_handler() throw() { interrupted = false; signal( SIGINT, sighandler ); signal( SIGHUP, sighandler ); signal( SIGTERM, sighandler ); }