Agnus Dei (jackal) wrote,
Agnus Dei

Recovery a zero length file in xfs by reading raw blocks off the drive

I used this script I wrote to recover 90,000 files that got zero'ed out in length when a XFS/DRBD based file server crashed and mysteriously zero'ed out all the files. Luckily I had the file names that were zero'ed out (recursive find command) and then I passed each file to this script and recovered each file one at a time.

And it worked flawlessly. :)


# cat
use strict;
use File::Basename;

my $debug=1;
my $inode;
my $output_dir="/recovery";
my $output_file="/tmp/foo.out";
# print "$ARGV[0]";
my $file="$ARGV[0]";
die "Exit - Need an argument/filename" if ($ARGV[0] eq "") ;

### Create the directory to put the recoveried file
my $dirname = dirname($file);
my $basename = basename($file);
print "Filename = $file\n" if ($debug) ;
print "DIRNAME = $dirname\n" if ($debug) ;
print "BASENAME= $basename\n" if ($debug);
my $single_file_recovery_directory="$output_dir/$dirname";
if ( ! -d $single_file_recovery_directory ) {
   print "$single_file_recovery_directory is not a directory\n" if ($debug);
        print "creating $single_file_recovery_directory\n" if ($debug);
        system("/bin/mkdir -p $single_file_recovery_directory");
print "Output file : $output_file\n" if ($debug);

my $dev="/dev/drbd10";
my $inode=`ls -i $file`;
$inode=(split(/ /,$inode))[0];
print "inode ='$inode'\n" if ($debug);
die "Exit - need an inode to continue" if ($inode eq "");

my $xfs_db_results=`xfs_db -r $dev -c "inode $inode" -c "bmap"`;
print $xfs_db_results;
die "Exit - xfs_db_results returned no results" if ($xfs_db_results eq "");
#data offset 0 startblock 648428604 (2/111557692) count 299 flag 0

my ($blocks_piece,$count)=(split(/ /,$xfs_db_results))[5,7];
# $blocks_piece =~ s/\//+/;
$blocks_piece =~ s/\(//;
$blocks_piece =~ s/\)//;
my ($blocks_piece_1,$blocks_piece_2)=(split(/\//,$blocks_piece))[0,1];
print "Blocks:$blocks_piece_1,$blocks_piece_2\n" if ($debug) ;
print "Count=$count\n" if ($debug) ;

# Determine the AG size
my $agblocks=`xfs_db -r $dev -c sb -c p | grep ^agblocks | sed 's/.* = //'`;
# my $agblocks="268435455";  # you can hardcode the value once you know it because it doesn't change
print "AG: $agblocks\n" if ($debug);

my $skip=$agblocks*$blocks_piece_1+$blocks_piece_2;

# Copy the extent in the first file, which consists of 6460 blocks (~26MB)
# in AG 7 starting at AG-relative block 2657536:
# system("dd if=$dev bs=4096 skip=$(($agblocks * 7 + 2657536)) count=6460 of=$output_file");
print "dd if=$dev bs=4096 skip=$skip count=$count of=$output_file\n" if ($debug);
system("dd if=$dev bs=4096 skip=$skip count=$count of=$output_file\n");

  • Post a new comment


    Anonymous comments are disabled in this journal

    default userpic

    Your reply will be screened