xtreemfs/tests/test_scripts/marked_block.pl

197 lines
5.7 KiB
Perl
Executable File

#!/usr/bin/perl
# Trying to detect disk write errors...
# Write a bunch of large files with a fixed pattern for each 512 bytes
# block. The block and file name can be recognised from its content.
#
# 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
#
# Copyright (c) Erich Focht <efocht@hpce.nec.com>
# All rights reserved
use strict;
use Digest::MD5 qw(md5_hex);
use Getopt::Long;
sub usage {
print <<'EOF';
Trying to detect disk write errors...
-------------------------------------
Write a bunch of large files with a fixed pattern for each 512 bytes
block. The block and file name can be recognised from its content,
each block contains a repetition of the filename and the block number
filled up to 512 bytes with "x" characters.
The md5sum of the file is computed on the fly and written to a corresponding
file, such that the integrity of each file can be checked separately.
The file checking is done in groups, the default group size is 50 files.
The group size should be chosen such that at check time the file is flushed
to disk. Whether this makes sense or not depends on the origin of the error,
of course...
Usage:
marked_block.pl [--start=<startindex>] [--nfiles=<number_of_files>] \
[--group=<groupsize>] [--check] [--file <name>]
--start=startindex : file number to start with (default = 1)
--nfiles=n : number of files to write (default = 100)
--group=groupsize : invoke md5sum after writing <groupsize> files
(default = 50)
--check : check files instead of writing, compare with
expected content and print deviating blocks.
--file <name> : write/check only one particular file
--base <basename> : basename for building the filename
--size <megabytes> : size of testfiles (default: 100MB)
--help : print this help message
Typical usage:
marked_blocks.pl --start=1 --nfiles=1000
runs until it finds some checksum discrepancy in a file,
suppose this is file testfile0054. In order to display the
discrepancy to the file on disk:
marked_blocks.pl --start=54 --nfiles=1 --check
or
marked_blocks.pl --file testfile0054 --check
Written and (c) by Erich Focht @ NEC. Use at your own risk!
EOF
exit 0;
}
sub min {
my ($a, $b) = @_;
return $a if ($a <= $b);
return $b;
}
my $filesize = 100 * 1024 * 1024;
my $nfiles = 100;
my $namebase = "testfile";
my $nfstart = 1;
my $group = 50;
my $check = 0;
my $file;
GetOptions(
"help|h" => \&usage,
"start=i" => \$nfstart,
"nfiles|n=i"=> \$nfiles,
"group=i" => \$group,
"check" => \$check,
"file=s" => \$file,
"base=s" => \$namebase,
"size=i" => \$filesize,
) || &usage();
my $nblocks = $filesize * 1024 * 1024 / 512; # 100MB
if ($file) {
$file =~ /^(\D+)(\d+)$/;
$namebase = $1;
$nfstart = int($2);
$nfiles = 1;
}
if (!$check) {
print "Writing $nfiles files with prefix $namebase, starting with $nfstart.\n";
} else {
print "Checking $nfiles files with prefix $namebase, starting with $nfstart.\n";
}
my ($string, $block, $slen, $written, $read, $bread);
my $nfend = $nfstart + $nfiles - 1;
for (my $fb = $nfstart; $fb <= $nfend; $fb = $fb + $group) {
for (my $f = $fb; $f <= min($fb + $group - 1, $nfend); $f++) {
my $name = sprintf("%s%04d",$namebase,$f);
my @bad;
$| = 1;
print "File: $name\n";
if (!$check) {
open OUT, "> $name" or die "Could not open file $name: $!";
} else {
open IN, "$name" or die "Could not open file $name: $!";
}
my $md5 = Digest::MD5->new;
for (my $i = 0; $i < $nblocks; $i++) {
$string = "file $name block $i:";
$slen = length($string);
$block = $string x int(512/$slen);
$block .= "x" x (512 - length($block));
if (!$check) {
$written = syswrite(OUT, $block, 512);
die "written = $written instead of 512!" if ($written != 512);
$md5->add($block);
} else {
$read = sysread(IN, $bread, 512);
die "read = $read instead of 512!" if ($read != 512);
if (substr($bread, 0, 512) ne substr($block, 0, 512)) {
push @bad, $i;
print "Block $i has unexpected content:\n";
print_hex($bread,512);
print "-" x 70 . "\n";
$bread =~ s/[:^print:]/./g;
print "$bread\n";
print "-" x 70 . "\n";
}
}
}
if (!$check) {
close OUT;
open MD5, "> $name.md5" or die "Could not open $name.md5 : $!";
print MD5 $md5->hexdigest . " $name\n";
close MD5;
} else {
if (@bad) {
print "Following 512 byte blocks were bad: "
. join(" ",@bad) . "\n";
}
close IN;
}
}
if (!$check) {
for (my $f = $fb; $f <= min($fb + $group - 1, $nfend); $f++) {
my $mdfile = sprintf("%s%04d%s",$namebase,$f,".md5");
!system("md5sum -c $mdfile") or die "md5sum failed!";
}
}
}
exit 0;
sub print_hex {
my ($data, $len) = @_;
for (my $i = 0; $i < $len; $i += 24) {
printf "%03d: ",$i;
for (my $j = $i; $j < min($i + 24, $len); $j++) {
my $b = unpack "C", substr($data,$j,1);
printf "%02x ", $b;
}
print "\n";
}
}