1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
|
/* GNU ddrescue - Data recovery tool
Copyright (C) 2004-2026 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 2 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 <http://www.gnu.org/licenses/>.
*/
#define _FILE_OFFSET_BITS 64
#include <cctype>
#include <cerrno>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <stdint.h>
#include <termios.h>
#include <unistd.h>
#include "mapfile.h"
#include "mapbook.h"
namespace {
void input_pos_error( const long long pos, const long long insize )
{
char buf[160];
const char * const p = format_num3( pos );
const int len = std::strlen( p );
snprintf( buf, sizeof buf,
"Can't start reading at pos %s.\n"
" Size of input file is only %*s bytes.",
p, len, format_num3( insize ) );
show_error( buf );
}
} // end namespace
bool safe_fflush( FILE * const f )
{
while( true )
{
const int ret = std::fflush( f );
if( ret == 0 ) return true;
if( ret == EOF && errno == EINTR ) continue;
return false;
}
}
bool Mapbook::save_mapfile( const char * const name )
{
std::remove( name );
FILE * const f = std::fopen( name, "w" );
if( f && write_mapfile( f, true, true ) && std::fclose( f ) == 0 )
{
char buf[80];
snprintf( buf, sizeof buf, "Mapfile saved in '%s'", name );
final_msg( buf );
return true;
}
return false;
}
bool Mapbook::emergency_save()
{
static bool first_time = true;
static std::string home_name;
const std::string dead_name( "ddrescue.map" );
if( filename() != dead_name && save_mapfile( dead_name.c_str() ) )
return true;
if( first_time )
{
first_time = false;
const char * const p = std::getenv( "HOME" );
if( p ) { home_name = p; home_name += '/'; home_name += dead_name; }
}
if( home_name.size() &&
filename() != home_name && save_mapfile( home_name.c_str() ) )
return true;
show_file_error( dead_name.c_str(), "Emergency save failed." );
return false;
}
Mapbook::Mapbook( const long long offset, const long long insize,
Domain & dom, const Mb_options & mb_opts,
const char * const mapname, const int cluster,
const int hardbs, const bool complete_only,
const bool rescue )
: Mapfile( mapname ), Mb_options( mb_opts ), offset_( offset ),
mapfile_insize_( 0 ), domain_( dom ), hardbs_( hardbs ),
softbs_( cluster * hardbs_ ),
iobuf_size_( softbs_ + hardbs_ ), // +hardbs for direct unaligned reads
final_errno_( 0 ), um_t1( 0 ), um_t1s( 0 ), um_prev_mf_sync( false ),
mapfile_exists_( false )
{
long alignment = sysconf( _SC_PAGESIZE );
if( alignment < hardbs_ || alignment % hardbs_ ) alignment = hardbs_;
if( alignment < 2 ) alignment = 0;
iobuf_ = iobuf_base = new uint8_t[ alignment + ( 2 * iobuf_size_ ) ];
if( alignment > 1 ) // align iobuf for direct disc access
{
const int disp =
alignment - ( reinterpret_cast<unsigned long long> (iobuf_) % alignment );
if( disp > 0 && disp < alignment ) iobuf_ += disp;
}
if( insize > 0 )
{
if( domain_.pos() >= insize )
{ input_pos_error( domain_.pos(), insize ); std::exit( 1 ); }
domain_.crop_by_file_size( insize );
}
if( filename() )
{
mapfile_exists_ = read_mapfile( 0, false );
if( mapfile_exists_ ) mapfile_insize_ = extent().end();
}
if( !complete_only ) extend_sblock_vector( insize );
else domain_.crop( extent() ); // limit domain to blocks read from mapfile
compact_sblock_vector();
if( rescue ) join_subsectors( hardbs_ );
split_by_domain_borders( domain_ );
if( sblocks() == 0 ) domain_.clear();
}
/* Write periodically the mapfile to disc.
Return false only if update is attempted and fails.
*/
bool Mapbook::update_mapfile( const int odes, const bool force )
{
if( !filename() ) return true;
const int interval = ( mapfile_save_interval >= 0 ) ? mapfile_save_interval :
30 + std::min( 270L, sblocks() / 38 ); // auto, 30s to 5m
const long long t2 = std::time( 0 );
if( um_t1 == 0 || um_t1 > t2 ) um_t1 = um_t1s = t2; // initialize
if( !force && t2 - um_t1 < interval ) return true;
const bool mf_sync = force || t2 - um_t1s >= mapfile_sync_interval;
if( odes >= 0 ) fsync( odes );
if( um_prev_mf_sync )
{
std::string mapname_bak( filename() ); mapname_bak += ".bak";
std::remove( mapname_bak.c_str() ); // break possible hard link
std::rename( filename(), mapname_bak.c_str() );
}
um_prev_mf_sync = mf_sync;
for( bool first_post = true; ; first_post = false )
{
errno = 0;
if( write_mapfile( 0, true, mf_sync ) )
// update times here to exclude writing time from intervals
{ um_t1 = std::time( 0 ); if( mf_sync ) um_t1s = um_t1; return true; }
if( verbosity < 0 ) return false;
const int saved_errno = errno;
if( first_post ) std::fputc( '\n', stderr );
show_file_error( filename(), "Error writing mapfile", saved_errno );
std::fputs( "Fix the problem and press ENTER to retry,\n"
" or E+ENTER for an emergency save and exit,\n"
" or Q+ENTER to abort.\n", stderr );
safe_fflush( stderr );
while( true )
{
tcflush( STDIN_FILENO, TCIFLUSH );
const int c = std::tolower( std::fgetc( stdin ) );
int tmp = c;
while( tmp != '\n' && tmp != EOF ) tmp = std::fgetc( stdin );
if( c == '\r' || c == '\n' || c == 'e' || c == 'q' )
{
if( c == 'q' || ( c == 'e' && emergency_save() ) )
{ if( !force ) std::fputs( "\n\n\n\n\n", stdout ); return false; }
break;
}
}
}
}
|