--- /dev/null
+#! /usr/bin/perl
+
+use autodie;
+
+(my $prog = $0) =~ s!^.*/!!;
+
+die "usage: $prog ORIG COPY\n" unless @ARGV == 2;
+my ($orig, $copy) = @ARGV;
+
+open my $ofh, "<", $orig; binmode $ofh;
+open my $cfh, "<", $copy; binmode $cfh;
+
+my $SECSZ = 2048;
+my $ZERO = "\0" x $SECSZ;
+
+my $rc = 0;
+sub bad ($) {
+ my ($msg) = @_;
+ print STDERR "$prog: $msg\n";
+ $rc = 2 unless $rc > 2;
+}
+
+my ($obuf, $cbuf);
+my $i = 0;
+my $report = "";
+my $prev = undef;
+my $st = undef;
+sub update ($) {
+ my ($newst) = @_;
+ if ($newst ne $st) {
+ if (defined $st) {
+ $report .= ", " if $report;
+ if ($st eq 'copy') { $report .= "$prev .. $i"; }
+ elsif ($st eq 'zero') { $report .= "[$prev .. $i]"; }
+ else { $report .= "($st: $prev .. $i)"; }
+ }
+ $st = $newst; $prev = $i;
+ }
+}
+SECTOR: for (;;) {
+ my $on = read $ofh, $obuf, $SECSZ;
+ my $cn = read $cfh, $cbuf, $SECSZ;
+ if ($on < $SECSZ || $cn < $SECSZ) {
+ if ($on == $SECSZ) {
+ if (!$cn) { bad "premature end at sector #$i in `$copy'"; }
+ else { bad "short sector #$i in `$copy'"; }
+ } elsif (!on) {
+ bad "continuation from sector #$i in `$copy'";
+ } elsif ($on == $cn) {
+ bad "final short sector #$i in both files" if $on;
+ if ($cbuf eq $obuf) { $newst = 'copy'; }
+ elsif ($cbuf eq "\0" x $on) { $newst = 'zero'; }
+ else { bad "corrupted short sector #$i in `$copy'"; }
+ } else {
+ bad "short sector #$i in both files"
+ }
+ last SECTOR;
+ }
+
+ my $newst;
+ if ($cbuf eq $obuf) { $newst = 'copy'; }
+ elsif ($cbuf eq $ZERO) { $newst = 'zero'; }
+ else { bad "corrupted sector #$i in `$copy'"; $newst = 'bogus'; }
+ update $newst; $i++;
+}
+update 'end';
+print $report, "\n";
+exit $rc;