added checksum option to image.check
authorHeiko Schlittermann (JUMPER) <hs@schlittermann.de>
Tue, 16 Aug 2011 16:44:55 +0200
changeset 67 c0a522905faf
parent 66 35a0ea276176
child 68 25d318915ae9
added checksum option to image.check
bin/imager.check
--- a/bin/imager.check	Tue Aug 16 16:04:02 2011 +0200
+++ b/bin/imager.check	Tue Aug 16 16:44:55 2011 +0200
@@ -7,20 +7,24 @@
 use Hash::Util qw(lock_keys);
 use File::Find;
 use File::Temp;
-use DB_File;
+use Digest::MD5 qw(md5_hex);
 use File::Basename;
 use autodie qw(:all);
 use Cwd qw(abs_path);
+use IO::Compress::Gzip qw(&gzip $GzipError Z_BEST_SPEED);
+use IO::Uncompress::Gunzip qw(&gunzip $GunzipError);
 
 use Getopt::Long;
+use constant CIPHER => "aes-128-cbc";
 sub get_block_list;
 sub purge_unused;
 sub check_images;
 
 my %o = (
-    yes     => undef,
-    verbose => 1,
-    check   => undef,
+    yes      => undef,
+    verbose  => 1,
+    checksum => undef,
+    pass     => undef,
 );
 lock_keys(%o);
 
@@ -29,7 +33,8 @@
     GetOptions(
         "y|yes!"     => \$o{yes},
         "v|verbose!" => \$o{verbose},
-        "c|check"    => \$o{check},
+        "c|checksum" => \$o{checksum},
+        "p|pass"     => \$o{pass},
         "h|help"     => sub { pod2usage(-verbose => 1, -exit => 0) },
         "m|man"      => sub {
             pod2usage(
@@ -45,8 +50,8 @@
       and @ARGV
       or pod2usage;
     my $dir = shift;
-    
-    while (1) {
+
+    for (my $pass = 1 ; 1 ; ++$pass) {
         my %block = get_block_list($dir);
 
         verbose("# reading index files");
@@ -56,10 +61,11 @@
               . (grep !/^\.idx$/ => keys(%block))
               . " blocks");
 
-        purge_unused($dir => %block);
-        check_images($dir => %block) and last;
+        my $subpass = 0;
+        purge_unused($pass => ++$subpass, $dir => %block);
+        check_images($pass => ++$subpass, $dir => %block) and last;
 
-	verbose("# STARTING OVER!");
+        verbose("# STARTING OVER!");
     }
 }
 
@@ -93,11 +99,10 @@
 }
 
 sub purge_unused {
-    my ($dir, %block) = @_;
+    my ($pass, $subpass, $dir, %block) = @_;
     my ($total, $done, $t0);
-    state $subpass = -1;
 
-    verbose("# pass 1.@{[++$subpass]} - checking for unused blocks");
+    verbose("# pass $pass.$subpass - checking for unused blocks");
     verbose("#          estimating file count");
 
     # calculate the number of files we expect
@@ -120,7 +125,7 @@
         return alarm 1 if not $done;
         my $speed = $done / (time - $t0 + 1);
         verbose sprintf
-          "# pass 1.$subpass done %5.1f%% | %25s (%*d of %d blocks)",
+          "# pass $pass.$subpass done %5.1f%% | %25s (%*d of %d blocks)",
           100 * ($done / $total),
           scalar(localtime $t0 + $total / $speed), length($total) => $done,
           $total;
@@ -182,21 +187,20 @@
 }
 
 sub check_images {
-    my ($dir, %block) = @_;
+    my ($pass, $subpass, $dir, %block) = @_;
 
     my $total = grep { $_ ne "" } keys(%block);
     my $done  = 0;
     my $t0    = time;
 
-    state $subpass = -1;
-    verbose("# pass 2.@{[++$subpass]} - checking image completeness");
+    verbose("# pass $pass.$subpass - checking image completeness");
 
     # progress
     local $SIG{ALRM} = sub {
         return alarm 1 if not $done;
         my $speed = $done / (time - $t0 + 1);
         verbose sprintf
-          "# pass 2.$subpass done %5.1f%% | %25s (%*d of %d blocks)",
+          "# pass $pass.$subpass done %5.1f%% | %25s (%*d of %d blocks)",
           100 * $done / $total,
           scalar(localtime $t0 + $total / $speed), length($total) => $done,
           $total;
@@ -210,14 +214,43 @@
         next if $k eq "";
         ++$done;
 
-        next
-          if -f "$dir/data/$k"
-              or -f "$dir/data/$k.gz"
-              or -f "$dir/data/$k.x"
-              or -f "$dir/data/$k.x.gz"
-              or -f "$dir/data/$k.gz.x";
-        say "missing $k @$i";
+        my ($file) =
+          grep { -f }
+          map { "$dir/data/$_" } ($k, "$k.gz", "$k.x", "$k.x.gz", "$k.gz.x");
+
+        if (not $file) {
+            say "missing $k @$i";
+            @invalid{@$i} = ();
+            next;
+        }
+
+        next if not $o{checksum};
+
+        # checking the checksum
+        my $buffer;
+        given ($file) {
+            when (/\.gz\.x$/) {
+                open(
+                    my $fh =>
+                      "openssl @{[CIPHER]} -d -pass $o{pass} -in $file|");
+                local $/ = undef;
+                gunzip($fh => \$buffer) or die $GunzipError;
+            }
+            when (/\.gz$/) { gunzip($file => \$buffer) or die $GunzipError }
+            when (/\.x$/) {
+                open(
+                    my $fh =>
+                      "openssl @{[CIPHER]} -d -pass $o{pass} -in $file|");
+                local $/ = undef;
+                $buffer = <$fh>;
+            }
+            default { open(my $fh => $file); local $/ = undef; $buffer = <$fh> }
+        }
+
+        next if md5_hex($buffer) eq basename($file, qw(.gz .x .gz.x));
+        say "wrong checksum for $file\n";
         @invalid{@$i} = ();
+
     }
     $SIG{ALRM}->();
     alarm 0;
@@ -268,6 +301,15 @@
 
 =over
 
+=item B<-c>|B<--checksum>
+
+Read all block files and check their checksum. (default: off)
+
+=item B<-p>|B<--pass> I<pass>
+
+In case you're using encrypted blocks, the param is passed to
+C<openssl>s C<-pass> option. (default: unset)
+
 =item B<-v>|B<-->[no]B<verbose>
 
 Generate more output about what's going on. (default: on)