#!/usr/bin/perl # # Perl script to scan raw disk blocks looking for certain patterns. This allows you to "undelete" # a file that I just wrote over somehow if any old temporary copies are still around. # # Usage: # # perl recover regex-pattern1 [regiex-pattern2] disk-device | gzip -9 > hopeful.gz # # A block must match all of the patterns to be "found". # # WARNING: You must pipe the output from this command to netcat to get it off of the box or, if # not possible, make sure to gzip the output so the script doesn't find the log file on disk # and go recursive. For example: # # # piping to netcat to send it to another server # perl recover.pl sendmail_pid /dev/md0 | nc server1 5000 # # # or piping to gzip to avoid the recursion # perl recover.pl sendmail_pid /dev/sda1 | gzip -9 > hopeful.gz # # $Id: recover.pl,v 1.3 2010/05/05 20:29:00 gray Exp $ # use strict; # how big is a block my $BLOCK_SIZE = $ENV{RECOVER_BLOCK_SIZE} || 10240; # how many bytes to keep from the last block so we won't miss patterns that are on block boundaries my $OVERLAP = $ENV{RECOVER_OVERLAP} || 128; # once we find a match how many blocks before the match to dump my $BEFORE_NUM = $ENV{RECOVER_BEFORE_NUM} || 2; # once we find a match how many blocks after the match to dump my $AFTER_NUM = $ENV{RECOVER_AFTER_NUM} || 2; #################################################### die "Usage: recover regex1 [regex2 ...] disk-device\n" if scalar(@ARGV) < 2; my @pats; while (@ARGV > 1) { my $pat = shift; # compile regex patterns push(@pats, qr{$pat}); } # now open our disk file open(DISK, '<', $ARGV[0]) || die "Cannot open $ARGV[0]: $!\n"; print STDERR "**** WARNING: output from this script should pipe through gzip or go to another volume ****\n"; my $block; my $blockC = 0; my @prev_blocks; my $nextC = 0; READ: while (1) { # save the last bit of the block so the patterns with match data at the very end of a block my $overlap = substr($block, (- $OVERLAP)); last unless sysread(DISK, $block, $BLOCK_SIZE) > 0; $blockC++; # make sure this block has all of the patterns my $matched = 1; # prepend the overlap my $block_with_overlap = $overlap . $block; for my $pat (@pats) { if ($block_with_overlap !~ $pat) { $matched = 0; last; } } # are we dumping blocks after a previous match? if ($nextC > 0) { $nextC--; # if this block too matched then we need to read more next blocks $nextC = $AFTER_NUM if $matched; print $block; # we don't look for the pattern in this block since we are already dumping it next; } if ($matched) { print STDERR "Found match at block $blockC\n"; # found all patterns print "\n\n--- MATCH @ block $blockC ---\n\n"; for my $prev (@prev_blocks) { print $prev; } if (scalar(@prev_blocks) == 0) { print $block_with_overlap; } else { print $block; } # clear the prev-blocks @prev_blocks = (); # we want the next ones as well $nextC = $AFTER_NUM; } else { # remove the first block from list shift(@prev_blocks) if scalar(@prev_blocks) >= $BEFORE_NUM; push(@prev_blocks, $block); } }