skip to content

Department of Genetics

 

Recovering data from RAID5 arrays with bad blocks all over the shop

Overview

Someone brought me some disks from a dead raid machine. No backups. Bad blocks all over. Thought it must be possible to rebuild the raid on a block by block basis rather than disk by disk. It worked! Requires a lot of space and GNU ddrescue.

rebuild.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define BUFFERSIZE 255

/* This file parses ddrescue logs to find which parts of a raid set contained
* unreadable data. It then attempts to recreate the data and write it out */
int main (int argc, char *argv[]) {
int filenum,numfiles;
unsigned long int **allstarts;
unsigned long int **allends;
unsigned long int filelen;
FILE **allfiles;

numfiles=argc-1;
allstarts=calloc(numfiles,sizeof(unsigned long int *));
allends=calloc(numfiles,sizeof(unsigned long int *));
allfiles=calloc(numfiles,sizeof(FILE *));
for(filenum=0;filenum<numfiles;filenum++) {
unsigned long int *starts,*ends ;
char imagefile[BUFFERSIZE];
FILE *fs;

starts=calloc(BUFFERSIZE,sizeof(unsigned long int));
ends=calloc(BUFFERSIZE,sizeof(unsigned long int));

printf("Trying to open logfile %s\n",argv[filenum+1]);
if(read_ddrescue_log(argv[filenum+1],starts,ends,&imagefile,&filelen)==0) {
/* yay read teh log successfully */
allstarts[filenum]=starts;
allends[filenum]=ends;
printf("Trying to open image %s\n",imagefile);
fs=fopen(imagefile,"r+");
if(fs==NULL) {
perror("Opening image failed");
return 1;
}
allfiles[filenum]=fs;
printf("Success\n");
}
}
printf("Resilvering\n");
if(resilver(allfiles,allstarts,allends,numfiles,filelen)!=0) {
printf("resilver threw a hissy fit\n");
return 1;
}
return 0;
}

/* Import an image file, loading the list of bad extents and also finding out
* what the disk image is called */
int read_ddrescue_log(char *filename, unsigned long int *starts, unsigned long int *ends, char *imagefile, unsigned long int *filelen) {
FILE *fs;
char *buff,*line;
unsigned long int start;
unsigned int length,lineno;

buff=calloc(BUFFERSIZE, sizeof(char));
line=calloc(BUFFERSIZE, sizeof(char));
fs=fopen(filename,"r");
if(fs==NULL) {
perror("Opening log failed");
return 1;
}

lineno=0;
while(fgets(line,BUFFERSIZE,fs)) {
if(sscanf(line,"# Command line: %*s %*s %s %*s",buff)==1) {
strncpy(imagefile,buff,BUFFERSIZE);
} else if(sscanf(line,"%lx %x %s",&start,&length,buff)==3) {
*filelen=start+length;
if(buff[0]=='-'){
/* Bad extent */
starts[lineno]=start;
ends[lineno]=start+length;
lineno++;
}
}
}
return 0;
}

/* Iterate through all the bad extents for each image.
* Check that the extent does not overlap with bad extents on other images.
* Replace the bad extent in the image with xored data from other images */
int resilver(FILE **allfiles, unsigned long int **allstarts, unsigned long int **allends, int numfiles, unsigned long int filelen) {
int filenum,extent,byte;
unsigned long int pos;
unsigned char *bytes;

bytes=calloc(numfiles,sizeof(char));

for(filenum=0;filenum<numfiles;filenum++) {
for(extent=0;extent<BUFFERSIZE && allends[filenum][extent]!=0;extent++) {
printf("Extent %lx - %lx on filenum %u\n",allstarts[filenum][extent],allends[filenum][extent],filenum);
sleep(1);
for(pos=allstarts[filenum][extent];pos<allends[filenum][extent];pos++) {
if(check_overlap(pos,allstarts,allends,filenum,numfiles)!=0) {
return 1;
} else {
/* in theory we can xor! */
if(seekall(pos,allfiles,numfiles)!=0)
return 1;
for(byte=0;byte<numfiles;byte++) {
if(byte!=filenum){
bytes[byte]=(unsigned char)fgetc(allfiles[byte]);
bytes[filenum]=bytes[filenum]^bytes[byte];
}
}
if(putc((int)bytes[filenum],allfiles[filenum])==EOF) {
perror("writing out failed");
}
bytes[filenum]=0;
}
}
}
}
return 0;
}

/* given an offset and a file id, check against other file ids */
int check_overlap(unsigned long int pos, unsigned long int **allstarts, unsigned long int **allends, int filenum, int numfiles) {
int i,j;

for(i=0;i<numfiles;i++) { /* interate across files */
if(i!=filenum) { /* don't compare against ourself */
for(j=0;j<BUFFERSIZE && allends[i][j]!=0;j++) {
unsigned long int s=allstarts[i][j];
unsigned long int e=allstarts[i][j];
if(e>pos>s) {
/* oh noes */
printf("Double fail %u@%lu %u(%lu-%lu)\n",filenum,pos,i,s,e);
return 1;
}
}
}
}
return 0;
}

int seekall(unsigned long int pos,FILE **allfiles, int numfiles) {
int filenum;

for(filenum=0;filenum<numfiles;filenum++) {
if(fseek(allfiles[filenum],pos,SEEK_SET)!=0) {
perror("Seeking");
return -1;
}
}
return 0;
}