/* * $Id: recovery.c,v 1.5 2003/05/03 22:18:48 andrew_belov Exp $ * --------------------------------------------------------------------------- * This module contains a set of procedures to create and use special recovery * records (XRJ files) introduced with ARJ v 2.55. * */ #include "arj.h" DEBUGHDR(__FILE__) /* Debug information block */ #define DEFAULT_PROTPAD_SIZE 1024 /* Protection data granularity */ #define MAX_BLOCK_SIZE 4096 /* Maximum size of temporary block */ #define DIVISOR_BITS 10 /* Depends on data granularity */ #define PROTBLOCK_HDR_SIZE 14 /* Header of protection block */ #define PROT_SIG_SIZE 6 /* Signature size */ /* Local variables */ static char prot_sig[]="PSigx"; /* Protection block signature */ static char blocks_numstr[]="%3dx "; /* Number of blocks to process */ static char prot_ticker[]="."; /* Block counter */ static unsigned long prot_blk_size=0L; /* Size of std. protection block */ /* Calculates CRC-16 of the given block. The result differs from the one given by CCITT V.41 CRC-16. */ static unsigned short crc16_for_block(char *data, int len) { int remain; unsigned char *tmp_dptr; unsigned short crc16term; tmp_dptr=(unsigned char *)data; crc16term=0; for(remain=len; remain>0; remain--) { crc16term=crc16tab[crc16term>>8]^((crc16term<<8)|(unsigned short)(*(tmp_dptr++))); } return(crc16term^0xAAAA); } /* Returns number of bytes needed to hold the temporary block */ static int calc_protpad_size(int len) { long tmp_len, divisor; tmp_len=(long)len+1; while(1) { divisor=2L; while(divisor*divisor<=tmp_len) { if(tmp_len%divisor==0L) break; else divisor++; } if(tmp_len%divisor!=0L) break; tmp_len++; } return((int)tmp_len); } /* Returns the proportion of protection data size to archive size, per mille */ static long calc_protdata_pct(unsigned long protsize, unsigned long archsize) { int dec; for(dec=0; dec<3; dec++) { if(protsize<=0x19999999) protsize*=10L; else archsize/=10L; } if(archsize==0) return(0); else return(protsize/archsize); } /* Relocates the protection data */ static void relocate_protdata(char *dest, char *src, int len) { int i; for(i=0; i>1; if(prot_blk_size>16384L) prot_blk_size=16384L; } ct=0; while(prot_blk_size>MAX_BLOCK_SIZE) { prot_blk_size>>=1; ct++; } prot_blk_size*=DEFAULT_PROTPAD_SIZE; prot_blk_size>>=(DIVISOR_BITS-ct); return((((limit>>(DIVISOR_BITS-ct))/prot_blk_size+1L)* threshold*DEFAULT_PROTPAD_SIZE)<<2); } /* Creates a protection file for the given archive */ int create_protfile(FILE *stream, unsigned long offset, int state) { int protpad_size; int pad1_size, pad2_size; int block_size; char *pad_array[4]; char *protpad, *protpad_r; char *single_pad, *single_pad_r; char *protpad_bck; unsigned long ifile_size; /* Input file size */ unsigned long block_offset; int block_divisor; int total_blocks; /* Total number of MAX_BLOCK_SIZE-byte blocks in the recovery file. */ int cur_block; /* 0-relative */ int pad_ctr; int section_size; /* Number of bytes read */ unsigned long crc32_tmp; long per_mille; unsigned long data_offset; protpad_size=calc_protpad_size(DEFAULT_PROTPAD_SIZE); pad1_size=protpad_size+4-DEFAULT_PROTPAD_SIZE; pad2_size=(pad1_size<<1)+PROTBLOCK_HDR_SIZE; block_size=(DEFAULT_PROTPAD_SIZE-pad2_size)>>1; #ifndef __32BIT__ if(block_size>MAX_BLOCK_SIZE) block_size=MAX_BLOCK_SIZE; #else if(block_size>16384) block_size=16384; #endif protpad=malloc_msg(protpad_size+2); protpad_r=malloc_msg(protpad_size+2); single_pad=malloc_msg(DEFAULT_PROTPAD_SIZE+2); single_pad_r=malloc_msg(DEFAULT_PROTPAD_SIZE+2); protpad_bck=malloc_msg(protpad_size+2); pad_array[0]=single_pad; pad_array[1]=protpad; pad_array[2]=protpad_r; pad_array[3]=single_pad_r; fseek(stream, 0L, SEEK_END); ifile_size=ftell(stream); fseek(stream, 0L, SEEK_END); file_write(prot_sig, 1, PROT_SIG_SIZE, stream); fseek(stream, 0L, SEEK_END); data_offset=ftell(stream); block_offset=(unsigned long)block_size; for(block_divisor=0; block_offset>MAX_BLOCK_SIZE; block_divisor++) block_offset>>=1; block_offset*=(unsigned long)DEFAULT_PROTPAD_SIZE; block_offset>>=(DIVISOR_BITS-block_divisor); total_blocks=((ifile_size>>(DIVISOR_BITS-block_divisor))/block_offset+1); total_blocks=state?offset:offset*total_blocks; msg_cprintf(0, M_WORKING); msg_cprintf(0, (FMSG *)blocks_numstr, total_blocks); for(cur_block=0; cur_block>1)+DIVISOR_BITS; if(pad_ctr+1>=DEFAULT_PROTPAD_SIZE) error(M_PROTECT_BUG); mput_word(crc32_tmp, &single_pad [pad_ctr]); mput_word(crc32_tmp>>16, &single_pad_r[pad_ctr]); block_offset+=(unsigned long)total_blocks* (unsigned long)DEFAULT_PROTPAD_SIZE; block_divisor++; } for(pad_ctr=0; pad_ctr=fsize) break; if(fread(pad, 1, PROT_SIG_SIZE-2, stream)!=PROT_SIG_SIZE-2) break; if(!strcmp(prot_sig+2, pad)) { sig_offset=ftell(stream); if(rp_ofs!=0) break; } rp_ofs++; } return(sig_offset); } /* Verifies and/or repairs a damaged archive */ int recover_file(char *name, char *protname, char *rec_name, int test_mode, unsigned long sig_offset) { unsigned int cur_stream; unsigned int cur_section; unsigned long block_offset; unsigned long ifile_size; unsigned long orig_ifile_size; /* Size of undamaged input file (stored in the protection file) */ unsigned long dest_file_size; /* Size of output file (number of bytes to write) */ int errors; /* Total number of damaged sections */ int protpad_size; int pad1_size; int section_size; /* Number of bytes read */ int total_blocks; int data_damage; /* Data damage flag */ int damage_level; /* 0, 1 or 2 (highest) */ int damage_flag, pad_damage_flag; int pad_flag, bck_pad_flag; /* Section damage flag */ int rec_size=0, bck_rec_size=0; /* Recovered sections size */ char *protpad, *protpad_r; char *single_pad, *single_pad_r; char *protpad_bck; FILE *astream, *xstream; /* ARJ and XRJ files */ FILE *ostream; /* Destination file */ int pad_ctr; unsigned long rd_offset; /* Offset of recovery data within xstream */ int ins_lf=0; errors=0; protpad_size=calc_protpad_size(DEFAULT_PROTPAD_SIZE); /* -> EBX */ pad1_size=protpad_size+4-DEFAULT_PROTPAD_SIZE; protpad=malloc_msg(protpad_size+2); protpad_r=malloc_msg(protpad_size+2); single_pad=malloc_msg(DEFAULT_PROTPAD_SIZE+2); single_pad_r=malloc_msg(DEFAULT_PROTPAD_SIZE+2); protpad_bck=malloc_msg(protpad_size+2); astream=file_open_noarch(name, m_rb); if(file_exists(protname)) { xstream=file_open_noarch(protname, m_rb); rd_offset=0L; } else { xstream=astream; rd_offset=chk_prot_sig(xstream, sig_offset); if(rd_offset==0L) { if(test_mode) { msg_cprintf(0, M_NO_PROT_DATA); nputlf(); return(0); } else error(M_NO_PROT_DATA); } } fseek(astream, 0L, SEEK_END); ifile_size=ftell(astream); if(rd_offset!=0L) ifile_size=rd_offset-6L; fseek(xstream, rd_offset, SEEK_SET); fread(single_pad, 1, DEFAULT_PROTPAD_SIZE, xstream); crc32term=CRC_MASK; crc32_for_block(single_pad+4, DEFAULT_PROTPAD_SIZE-4); /* Possible XRJ damage */ if(mget_dword(&single_pad[0])!=crc32term|| mget_word (&single_pad[4])!=0x1111) { fseek(xstream, (long)DEFAULT_PROTPAD_SIZE*3+rd_offset, SEEK_SET); fread(single_pad, 1, DEFAULT_PROTPAD_SIZE, xstream); crc32term=CRC_MASK; crc32_for_block(single_pad+4, DEFAULT_PROTPAD_SIZE-4); if(mget_dword(&single_pad[0])!=crc32term|| mget_word (&single_pad[4])!=0x1111) error(M_RECOVERY_CRC_DAMAGED); } fseek(xstream, rd_offset, SEEK_SET); total_blocks=mget_word(&single_pad[6]); orig_ifile_size=mget_dword(&single_pad[10]); if(ifile_size==orig_ifile_size) { fseek(astream, 0L, SEEK_SET); msg_cprintf(0, M_WORKING); for(cur_stream=0; cur_stream>1)])!= (unsigned short)(crc32term%65536L)) goto recovery; } if(damage_level==0||damage_level==2) { if(mget_word (&single_pad_r[(pad1_size<<1)+((cur_section<<2)>>1)])!= (unsigned short)(crc32term>>16)) goto recovery; } block_offset+=(unsigned long) DEFAULT_PROTPAD_SIZE*(unsigned long)total_blocks; cur_section++; } } if(test_mode==2) goto recovery; } else { recovery: nputlf(); if(test_mode!=1) { atstream=ostream=file_open_noarch(rec_name, m_wbp); fseek(astream, 0L, SEEK_SET); errors=0; for(dest_file_size=min(ifile_size, orig_ifile_size); dest_file_size>0; dest_file_size-=(unsigned long)section_size) { section_size=min(DEFAULT_PROTPAD_SIZE, dest_file_size); if(fread(single_pad, 1, section_size, astream)!=section_size) break; if(fwrite(single_pad, 1, section_size, ostream)!=section_size) break; } for(dest_file_size= (ifile_size>=orig_ifile_size)?0:orig_ifile_size-ifile_size; dest_file_size>0; dest_file_size-=(unsigned long)section_size) { section_size=min(DEFAULT_PROTPAD_SIZE, dest_file_size); if(fwrite(single_pad, 1, section_size, ostream)!=section_size) break; } fseek(ostream, 0L, SEEK_END); if(ftell(ostream)!=orig_ifile_size) error(M_DISK_FULL); ifile_size=orig_ifile_size; for(cur_stream=0; cur_stream>1)])!= (unsigned short)(crc32term%65536L)) data_damage=1; } if(damage_level==0||damage_level==2) { if (mget_word (&single_pad_r[(pad1_size<<1)+((cur_section<<2)>>1)])!= (unsigned short)(crc32term>>16)) data_damage=1; } if(data_damage) { errors++; if(ins_lf) { nputlf(); ins_lf=0; } msg_cprintf(0, M_SECTION_DAMAGED, cur_stream, cur_section); if(bck_pad_flag==-1) { bck_pad_flag=cur_section; rec_size=section_size; } else if(pad_flag==-1) { pad_flag=cur_section; bck_rec_size=section_size; } else abort_recovery(); } else { for(pad_ctr=0; pad_ctr>1)])!= (unsigned short)(crc32term%65536L)) data_damage=1; } if(damage_level==0||damage_level==2) { if(mget_word (&single_pad_r[(pad1_size<<1)+((cur_section<<2)>>1)])!= (unsigned short)(crc32term>>16)) data_damage=1; } if(data_damage) { errors++; if(ins_lf) { nputlf(); ins_lf=0; } msg_cprintf(0, M_SECTION_DAMAGED, cur_stream, cur_section); if(bck_pad_flag==-1) { bck_pad_flag=cur_section; rec_size=section_size; } else if(pad_flag==-1) { pad_flag=cur_section; bck_rec_size=section_size; } else abort_recovery(); } else { for(pad_ctr=0; pad_ctr