#!/opt/i386-linux/perl/bin/perl -w # # copy_badblocks.pl # # Copyright (C) 2002-2004 Blair Zajac. All rights reserved. # Author: Blair Zajac . # # $URL$ # $LastChangedDate$ # $LastChangedBy$ # $LastChangedRevision$ # # Distributed under the terms of the GNU General Public License, # version 2 or later, which is available for download at # http://www.gnu.org/licenses/gpl.html. # # This script is used to make a copy of a file that contains bad # blocks. Normally, programs that read a file, such as cp, will quit # immediately after the first read failure and not continue to attempt # to read the remaining portion of the file. If you have a file that # contains one or more bad blocks and you want to make a copy of the # file containing as much readable data as you can, this script # accomplishes this. It reads a block at a time from the input file # to the output file. If a particular block cannot be read, it writes # to the output file a standard filler string that can be searched # for. $| = 1; use strict; use Fcntl qw(O_RDONLY SEEK_SET); use Getopt::Long 2.26; # These are configurable parameters. # Set this to the default blocksize to read from the file. my $opt_default_blocksize = 4096; sub usage { die "usage: $0 [-b blocksize] from to\n"; } GetOptions('blocksize=i' => \$opt_default_blocksize) or usage; usage unless @ARGV == 2; my $from_filename = shift; my $to_filename = shift; my @stat = stat($from_filename); unless (@stat) { die "$0: cannot stat '$from_filename': $!\n"; } my $file_length = $stat[7]; my $remaining = $file_length; unless (sysopen(READ, $from_filename, O_RDONLY)) { die "$0: cannot sysopen '$from_filename' for reading: $!\n"; } unless (open(WRITE, ">$to_filename")) { die "$0: cannot open '$to_filename' for writing: $!\n"; } print "Using $opt_default_blocksize bytes per block.\n"; my $pos = 0; while ($remaining) { my $buffer = "BAD BLOCKS.....\n" x ($opt_default_blocksize/16); my $bytes_to_read = 0; if ($remaining < $opt_default_blocksize) { $bytes_to_read = $remaining; $buffer = substr($buffer, 0, $remaining); } else { $bytes_to_read = $opt_default_blocksize; } my $block_number = $pos/$opt_default_blocksize; if (sysseek(READ, $pos, SEEK_SET)) { my $bytes_read = sysread(READ, $buffer, $bytes_to_read); unless (defined $bytes_read and $bytes_read == $bytes_to_read) { warn "$0: cannot read $opt_default_blocksize bytes at block ", "$block_number byte $pos: $!\n"; } } else { warn "$0: cannot sysseek to $pos: $!\n"; } print WRITE $buffer; $pos += $bytes_to_read; $remaining -= $bytes_to_read; } unless (close(WRITE)) { unlink($to_filename); die "$0: cannot close '$to_filename' for writing: $!\n"; } unless (close(READ)) { unlink($to_filename); die "$0: cannot close '$from_filename' for reading: $!\n"; } chmod($stat[2], $to_filename) or warn "$0: cannot chmod '$to_filename': $!\n"; if ($> == 0) { chown(@stat[4,5], $to_filename) or warn "$0: cannot chown '$to_filename': $!\n"; }