From: smckown Date: Fri, 4 Apr 2008 19:30:24 +0000 (+0000) Subject: Adding BackupPC_verifyPool to the backuppc scripts. This was written by X-Git-Tag: backuppc-ovz-0.1~2 X-Git-Url: https://oss.titaniummirror.com/gitweb?p=ovzbpc.git;a=commitdiff_plain;h=9122a9d06d6bf18a9ea07968a4f3cc8dc14ab2ec Adding BackupPC_verifyPool to the backuppc scripts. This was written by another user but is not included in the standard bpc distro. It seems quite useful. --- diff --git a/BackupPC_verifyPool b/BackupPC_verifyPool new file mode 100755 index 0000000..d1b0dc9 --- /dev/null +++ b/BackupPC_verifyPool @@ -0,0 +1,191 @@ +#!/usr/bin/perl +#============================================================= -*-perl-*- +# +# BackupPC_verifyPool: Verify pool integrity +# +# DESCRIPTION +# +# BackupPC_verifyPool tries to verify the integrity of the pool files, +# based on their file names, which are supposed to correspond to MD5 +# digests calculated from the (uncompressed) length and parts of their +# (again uncompressed) contents. +# Needs to be run as backuppc user for access to the pool files and +# meta data. +# +# Usage: BackupPC_verifyPool [-v] [-p] [-u] [-r range] [-s] +# +# Options: +# +# -v Show what is going on. Without this flag, only errors found +# are displayed, which might be very boring. Use '-p' for a +# bit of entertainment without causing your tty to scroll so +# much. +# -p Show more terse progress output. +# -u Check the pool (uncompressed files), not the cpool +# (uncompressed files). +# -r range Specify the pool range to check (see below). 'range' +# can be a Perl expression like '0 .. 255' or '0, 10, 30 .. 40'. +# Only safe characters allowed [\da-fA-F,.\s]. +# -s Show summary. +# +# The pool range was chosen as in BackupPC_nightly as means to divide +# the possibly lengthy operation of verifying the pool into smaller +# steps. The full pool goes from 0 to 255, that's all you really need to +# know. For more information, see BackupPC_nightly from the BackupPC +# distribution. +# +# AUTHOR +# Holger Parplies +# +# VERSION +# $Id$ +# +# COPYRIGHT +# Copyright (C) 2007 Holger Parplies +# +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +#======================================================================== + + +use strict; +use lib '/usr/share/backuppc/lib'; # Debian; change to fit your needs +use BackupPC::Lib; +use BackupPC::FileZIO; +use Getopt::Std; +use File::Find; + +# $ENV {PATH} = '/bin:/usr/bin'; +# chdir '.'; + +my %opts = ( + v => 0, # verbose output + p => 0, # terse progress output + u => 0, # pool (1) or cpool (0) + r => '0 .. 255', # range + s => 0, # show summary + ); + +unless (getopts ('vpur:s', \%opts)) { + die < {TopDir} . '/' . ($opts {u} ? 'pool' : 'cpool') . '/'; + # Pool directory to check +my @range; # Pool range to check +my $mismatch_count = 0; # number of invalid files +my $file_count = 0; # running file count for progress output +my $md5; # handle of MD5 object + +# # untaint range specification; NOTE THAT THIS IS NOT SECURE! +# $opts {r} = $1 +# # if $opts {r} =~ /^(.*)$/; + +# check range specification +die "Range specification '$opts{r}' contains insecure characters!\n" + if $opts {r} !~ /^[0-9a-fA-Fx.,\s]*$/; +@range = eval "($opts{r});"; +if ($@) { + die "Range specification '$opts{r}' is invalid: @range"; +} elsif (not defined @range or @range == 0) { + die "Range specification '$opts{r}' is empty. Nothing to do.\n"; +} elsif (grep { $_ < 0 or $_ > 255 } @range) { + die "Range specification '$opts{r}' contains values outside (0 .. 255)\n"; +} + +# iterate over the specified part of the pool +$md5 = new Digest::MD5 + or die "Can't create MD5 object: $!\n"; +foreach my $i (@range) { + my $dir = sprintf '%s/%1x/%1x', $pooldir, int ($i / 16), $i % 16; + find (\&validate_pool_dir, $dir) + if -d $dir; +} + +# Summary +if ($opts {s}) { + printf "%d files in %d directories checked, %d had wrong digests.\n", + $file_count, @range * 16, $mismatch_count; +} elsif ($mismatch_count > 0) { + print "ERROR: $mismatch_count files in ", $opts {u} ? 'pool' : 'cpool', + ", range ($opts{r}), seem to be corrupt!\n"; +} + +# Return code +exit $mismatch_count > 0 ? 1 : 0; + +# actual verification process +sub validate_pool_dir { + my ($name_md5) = ($_ =~ /^([0-9a-fA-F]{32})/); + my $content_md5; + + return # ignore directories + if -d $File::Find::name; + + $file_count ++; + if ($opts {p} and not $opts {v}) { + if (/^([0-9a-fA-F])([0-9a-fA-F])/) { + print "[$1$2 $file_count]\r"; + } else { + print "[ $file_count]\r"; + } + } + + # since we are only reading the file, we can treat the 'compLevel' parameter + # of BackupPC::FileZIO::open as a boolean + my $fh = BackupPC::FileZIO -> open ($File::Find::name, 0, ! $opts {u}); + if (defined $fh) { + my $buf; + my $bytes = $fh -> read (\$buf, 1024 * 1024 + 100); + if ($bytes > 1024 * 1024) { + # read complete file for determining length. Keep first 1MB in $buf, + # put total length into $bytes, read in 100KB chunks for lower mem usage + my $buf2; + my $new = 1; + while ($new > 0) { + $new = $fh -> read (\$buf2, 102400); + $bytes += $new; + } + } + $content_md5 = $bpc -> Buffer2MD5 ($md5, $bytes, \$buf); + if ($content_md5 ne $name_md5) { + # print ">$content_md5< != >$name_md5<\n"; + printf "[%5d] %-36.36s != %-32.32s\n", $file_count, $_, $content_md5; + } elsif ($opts {v}) { + printf "[%5d] %-36.36s (%10d) ok\n", $file_count, $_, $bytes; + } + } else { + # open failed, count as mismatch + print "$_: BackupPC::FileZIO::open failed!\n"; + $mismatch_count ++; + } +}