#!/usr/bin/perl use strict; use Math::BigInt; sub fmt { return reverse split //, join "_", unpack "(A3)*", reverse shift; } sub ddrescue { my $src = $ARGV[@ARGV-3]; my $test = `hdparm -I $src 2>&1`; die("hdparm command failed with exit code: $?\n") if $?; die("disk stopped responding\n") if $test =~ /HDIO_DRIVE_CMD\(identify\) failed/; system('./ddrescue', @_) == 0 or die("ddrescue failed: $!\n"); } sub scanlog { my $lf = shift; open LF,"<$lf" or die("cannot open file: $lf\n"); my $bs = shift; my $msize = 0; my $mstart = 0; my $ustart = 0; my $usize = 0; my $good = 0; my $bad = 0; my $unknown = 0; my @goodareas; my $prevstatus=''; my $after_header=0; foreach() { next if /^#/; $after_header=1,next if /^(0x[0-9A-F]+) +([-+?\/])$/ && !$after_header; /^(0x[0-9A-F]+) +(0x[0-9A-F]+) +([-+?\/])$/ or die("cannot parse line: $_\n"); my $start = new Math::BigInt($1)->bdiv($bs); my $size = new Math::BigInt($2)->bdiv($bs); my $status = $3; if ((($status eq '?') || ($status eq '/')) && ($prevstatus eq '+')) { $size = 1 if $size == 0; print 'Unfinished scan at '.fmt($start*$bs).' size '.fmt($size*$bs)."\n"; $ustart = $start; $usize = $size; } $prevstatus = $status; $good += $size if ($status eq '+'); push @goodareas,[$start, $size] if ($status eq '+'); $bad += $size if $status eq '-'; if (($status eq '?') || ($status eq '/')) { $unknown += $size; if ($size > $msize) { $msize = $size; $mstart = $start; } } } close LF; print 'Good: '.fmt($good*$bs)."\nUnknown: ".fmt($unknown*$bs)."\nBad: ".fmt($bad*$bs)."\n"; print "Largest good areas:\n"; my $idx = 0; @goodareas = sort { $$b[1] <=> $$a[1] } @goodareas; foreach(@goodareas) { last if ($idx++ >= 5); print " ".fmt($$_[1]*$bs)." at ".fmt($$_[0]*$bs)."\n"; } if ($usize > 0) { my @scanargs = ('-i', ($ustart*$bs), '-s', ($usize*$bs)); print "Suggested scan: ".join(' ', @scanargs)."\n"; return \@scanargs; } if ($msize > 0) { my $end = $mstart + $msize; my $newstart = $mstart+$msize/2; my $newsize = $end - $newstart; my @scanargs = ('-i', ($newstart*$bs), '-s', ($newsize*$bs)); print 'Largest unknown block: '.fmt($msize*$bs).' at '.fmt($mstart*$bs)."\n"; print "Suggested scan: ".join(' ', @scanargs)."\n"; return \@scanargs; } return undef; } my $bs; my $pa; my $dry_run = 0; for(@ARGV) { $dry_run = 1,$_='' if $_ eq '--examine'; $bs = $_ if $pa eq '-b'; $pa = $_; } $bs ||= 512; my $lf = $ARGV[@ARGV-1] or die("please supply log file name\n"); if (!$dry_run) { ddrescue(@ARGV); while (my $scan = scanlog($lf, $bs)) { ddrescue(@$scan, @ARGV); } } scanlog($lf, $bs);