|  |  | 
|  | /*-----------------------------------------------------------*/ | 
|  | /*--- Block recoverer program for bzip2                   ---*/ | 
|  | /*---                                      bzip2recover.c ---*/ | 
|  | /*-----------------------------------------------------------*/ | 
|  |  | 
|  | /*-- | 
|  | This program is bzip2recover, a program to attempt data | 
|  | salvage from damaged files created by the accompanying | 
|  | bzip2-1.0 program. | 
|  |  | 
|  | Copyright (C) 1996-2000 Julian R Seward.  All rights reserved. | 
|  |  | 
|  | Redistribution and use in source and binary forms, with or without | 
|  | modification, are permitted provided that the following conditions | 
|  | are met: | 
|  |  | 
|  | 1. Redistributions of source code must retain the above copyright | 
|  | notice, this list of conditions and the following disclaimer. | 
|  |  | 
|  | 2. The origin of this software must not be misrepresented; you must | 
|  | not claim that you wrote the original software.  If you use this | 
|  | software in a product, an acknowledgment in the product | 
|  | documentation would be appreciated but is not required. | 
|  |  | 
|  | 3. Altered source versions must be plainly marked as such, and must | 
|  | not be misrepresented as being the original software. | 
|  |  | 
|  | 4. The name of the author may not be used to endorse or promote | 
|  | products derived from this software without specific prior written | 
|  | permission. | 
|  |  | 
|  | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS | 
|  | OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | 
|  | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | 
|  | ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY | 
|  | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | 
|  | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE | 
|  | GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | 
|  | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | 
|  | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | 
|  | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | 
|  | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
|  |  | 
|  | Julian Seward, Cambridge, UK. | 
|  | jseward@acm.org | 
|  | bzip2/libbzip2 version 1.0 of 21 March 2000 | 
|  | --*/ | 
|  |  | 
|  | /*-- | 
|  | This program is a complete hack and should be rewritten | 
|  | properly.  It isn't very complicated. | 
|  | --*/ | 
|  |  | 
|  | #include <stdio.h> | 
|  | #include <errno.h> | 
|  | #include <stdlib.h> | 
|  | #include <string.h> | 
|  |  | 
|  | typedef  unsigned int   UInt32; | 
|  | typedef  int            Int32; | 
|  | typedef  unsigned char  UChar; | 
|  | typedef  char           Char; | 
|  | typedef  unsigned char  Bool; | 
|  | #define True    ((Bool)1) | 
|  | #define False   ((Bool)0) | 
|  |  | 
|  |  | 
|  | Char inFileName[2000]; | 
|  | Char outFileName[2000]; | 
|  | Char progName[2000]; | 
|  |  | 
|  | UInt32 bytesOut = 0; | 
|  | UInt32 bytesIn  = 0; | 
|  |  | 
|  |  | 
|  | /*---------------------------------------------------*/ | 
|  | /*--- I/O errors                                  ---*/ | 
|  | /*---------------------------------------------------*/ | 
|  |  | 
|  | /*---------------------------------------------*/ | 
|  | void readError ( void ) | 
|  | { | 
|  | fprintf ( stderr, | 
|  | "%s: I/O error reading `%s', possible reason follows.\n", | 
|  | progName, inFileName ); | 
|  | perror ( progName ); | 
|  | fprintf ( stderr, "%s: warning: output file(s) may be incomplete.\n", | 
|  | progName ); | 
|  | exit ( 1 ); | 
|  | } | 
|  |  | 
|  |  | 
|  | /*---------------------------------------------*/ | 
|  | void writeError ( void ) | 
|  | { | 
|  | fprintf ( stderr, | 
|  | "%s: I/O error reading `%s', possible reason follows.\n", | 
|  | progName, inFileName ); | 
|  | perror ( progName ); | 
|  | fprintf ( stderr, "%s: warning: output file(s) may be incomplete.\n", | 
|  | progName ); | 
|  | exit ( 1 ); | 
|  | } | 
|  |  | 
|  |  | 
|  | /*---------------------------------------------*/ | 
|  | void mallocFail ( Int32 n ) | 
|  | { | 
|  | fprintf ( stderr, | 
|  | "%s: malloc failed on request for %d bytes.\n", | 
|  | progName, n ); | 
|  | fprintf ( stderr, "%s: warning: output file(s) may be incomplete.\n", | 
|  | progName ); | 
|  | exit ( 1 ); | 
|  | } | 
|  |  | 
|  |  | 
|  | /*---------------------------------------------------*/ | 
|  | /*--- Bit stream I/O                              ---*/ | 
|  | /*---------------------------------------------------*/ | 
|  |  | 
|  | typedef | 
|  | struct { | 
|  | FILE*  handle; | 
|  | Int32  buffer; | 
|  | Int32  buffLive; | 
|  | Char   mode; | 
|  | } | 
|  | BitStream; | 
|  |  | 
|  |  | 
|  | /*---------------------------------------------*/ | 
|  | BitStream* bsOpenReadStream ( FILE* stream ) | 
|  | { | 
|  | BitStream *bs = malloc ( sizeof(BitStream) ); | 
|  | if (bs == NULL) mallocFail ( sizeof(BitStream) ); | 
|  | bs->handle = stream; | 
|  | bs->buffer = 0; | 
|  | bs->buffLive = 0; | 
|  | bs->mode = 'r'; | 
|  | return bs; | 
|  | } | 
|  |  | 
|  |  | 
|  | /*---------------------------------------------*/ | 
|  | BitStream* bsOpenWriteStream ( FILE* stream ) | 
|  | { | 
|  | BitStream *bs = malloc ( sizeof(BitStream) ); | 
|  | if (bs == NULL) mallocFail ( sizeof(BitStream) ); | 
|  | bs->handle = stream; | 
|  | bs->buffer = 0; | 
|  | bs->buffLive = 0; | 
|  | bs->mode = 'w'; | 
|  | return bs; | 
|  | } | 
|  |  | 
|  |  | 
|  | /*---------------------------------------------*/ | 
|  | void bsPutBit ( BitStream* bs, Int32 bit ) | 
|  | { | 
|  | if (bs->buffLive == 8) { | 
|  | Int32 retVal = putc ( (UChar) bs->buffer, bs->handle ); | 
|  | if (retVal == EOF) writeError(); | 
|  | bytesOut++; | 
|  | bs->buffLive = 1; | 
|  | bs->buffer = bit & 0x1; | 
|  | } else { | 
|  | bs->buffer = ( (bs->buffer << 1) | (bit & 0x1) ); | 
|  | bs->buffLive++; | 
|  | }; | 
|  | } | 
|  |  | 
|  |  | 
|  | /*---------------------------------------------*/ | 
|  | /*-- | 
|  | Returns 0 or 1, or 2 to indicate EOF. | 
|  | --*/ | 
|  | Int32 bsGetBit ( BitStream* bs ) | 
|  | { | 
|  | if (bs->buffLive > 0) { | 
|  | bs->buffLive --; | 
|  | return ( ((bs->buffer) >> (bs->buffLive)) & 0x1 ); | 
|  | } else { | 
|  | Int32 retVal = getc ( bs->handle ); | 
|  | if ( retVal == EOF ) { | 
|  | if (errno != 0) readError(); | 
|  | return 2; | 
|  | } | 
|  | bs->buffLive = 7; | 
|  | bs->buffer = retVal; | 
|  | return ( ((bs->buffer) >> 7) & 0x1 ); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | /*---------------------------------------------*/ | 
|  | void bsClose ( BitStream* bs ) | 
|  | { | 
|  | Int32 retVal; | 
|  |  | 
|  | if ( bs->mode == 'w' ) { | 
|  | while ( bs->buffLive < 8 ) { | 
|  | bs->buffLive++; | 
|  | bs->buffer <<= 1; | 
|  | }; | 
|  | retVal = putc ( (UChar) (bs->buffer), bs->handle ); | 
|  | if (retVal == EOF) writeError(); | 
|  | bytesOut++; | 
|  | retVal = fflush ( bs->handle ); | 
|  | if (retVal == EOF) writeError(); | 
|  | } | 
|  | retVal = fclose ( bs->handle ); | 
|  | if (retVal == EOF) { | 
|  | if (bs->mode == 'w') writeError(); else readError(); | 
|  | } | 
|  | free ( bs ); | 
|  | } | 
|  |  | 
|  |  | 
|  | /*---------------------------------------------*/ | 
|  | void bsPutUChar ( BitStream* bs, UChar c ) | 
|  | { | 
|  | Int32 i; | 
|  | for (i = 7; i >= 0; i--) | 
|  | bsPutBit ( bs, (((UInt32) c) >> i) & 0x1 ); | 
|  | } | 
|  |  | 
|  |  | 
|  | /*---------------------------------------------*/ | 
|  | void bsPutUInt32 ( BitStream* bs, UInt32 c ) | 
|  | { | 
|  | Int32 i; | 
|  |  | 
|  | for (i = 31; i >= 0; i--) | 
|  | bsPutBit ( bs, (c >> i) & 0x1 ); | 
|  | } | 
|  |  | 
|  |  | 
|  | /*---------------------------------------------*/ | 
|  | Bool endsInBz2 ( Char* name ) | 
|  | { | 
|  | Int32 n = strlen ( name ); | 
|  | if (n <= 4) return False; | 
|  | return | 
|  | (name[n-4] == '.' && | 
|  | name[n-3] == 'b' && | 
|  | name[n-2] == 'z' && | 
|  | name[n-1] == '2'); | 
|  | } | 
|  |  | 
|  |  | 
|  | /*---------------------------------------------------*/ | 
|  | /*---                                             ---*/ | 
|  | /*---------------------------------------------------*/ | 
|  |  | 
|  | #define BLOCK_HEADER_HI  0x00003141UL | 
|  | #define BLOCK_HEADER_LO  0x59265359UL | 
|  |  | 
|  | #define BLOCK_ENDMARK_HI 0x00001772UL | 
|  | #define BLOCK_ENDMARK_LO 0x45385090UL | 
|  |  | 
|  |  | 
|  | UInt32 bStart[20000]; | 
|  | UInt32 bEnd[20000]; | 
|  | UInt32 rbStart[20000]; | 
|  | UInt32 rbEnd[20000]; | 
|  |  | 
|  | Int32 main ( Int32 argc, Char** argv ) | 
|  | { | 
|  | FILE*       inFile; | 
|  | FILE*       outFile; | 
|  | BitStream*  bsIn, *bsWr; | 
|  | Int32       currBlock, b, wrBlock; | 
|  | UInt32      bitsRead; | 
|  | Int32       rbCtr; | 
|  |  | 
|  |  | 
|  | UInt32      buffHi, buffLo, blockCRC; | 
|  | Char*       p; | 
|  |  | 
|  | strcpy ( progName, argv[0] ); | 
|  | inFileName[0] = outFileName[0] = 0; | 
|  |  | 
|  | fprintf ( stderr, "bzip2recover 1.0: extracts blocks from damaged .bz2 files.\n" ); | 
|  |  | 
|  | if (argc != 2) { | 
|  | fprintf ( stderr, "%s: usage is `%s damaged_file_name'.\n", | 
|  | progName, progName ); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | strcpy ( inFileName, argv[1] ); | 
|  |  | 
|  | inFile = fopen ( inFileName, "rb" ); | 
|  | if (inFile == NULL) { | 
|  | fprintf ( stderr, "%s: can't read `%s'\n", progName, inFileName ); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | bsIn = bsOpenReadStream ( inFile ); | 
|  | fprintf ( stderr, "%s: searching for block boundaries ...\n", progName ); | 
|  |  | 
|  | bitsRead = 0; | 
|  | buffHi = buffLo = 0; | 
|  | currBlock = 0; | 
|  | bStart[currBlock] = 0; | 
|  |  | 
|  | rbCtr = 0; | 
|  |  | 
|  | while (True) { | 
|  | b = bsGetBit ( bsIn ); | 
|  | bitsRead++; | 
|  | if (b == 2) { | 
|  | if (bitsRead >= bStart[currBlock] && | 
|  | (bitsRead - bStart[currBlock]) >= 40) { | 
|  | bEnd[currBlock] = bitsRead-1; | 
|  | if (currBlock > 0) | 
|  | fprintf ( stderr, "   block %d runs from %d to %d (incomplete)\n", | 
|  | currBlock,  bStart[currBlock], bEnd[currBlock] ); | 
|  | } else | 
|  | currBlock--; | 
|  | break; | 
|  | } | 
|  | buffHi = (buffHi << 1) | (buffLo >> 31); | 
|  | buffLo = (buffLo << 1) | (b & 1); | 
|  | if ( ( (buffHi & 0x0000ffff) == BLOCK_HEADER_HI | 
|  | && buffLo == BLOCK_HEADER_LO) | 
|  | || | 
|  | ( (buffHi & 0x0000ffff) == BLOCK_ENDMARK_HI | 
|  | && buffLo == BLOCK_ENDMARK_LO) | 
|  | ) { | 
|  | if (bitsRead > 49) | 
|  | bEnd[currBlock] = bitsRead-49; else | 
|  | bEnd[currBlock] = 0; | 
|  | if (currBlock > 0 && | 
|  | (bEnd[currBlock] - bStart[currBlock]) >= 130) { | 
|  | fprintf ( stderr, "   block %d runs from %d to %d\n", | 
|  | rbCtr+1,  bStart[currBlock], bEnd[currBlock] ); | 
|  | rbStart[rbCtr] = bStart[currBlock]; | 
|  | rbEnd[rbCtr] = bEnd[currBlock]; | 
|  | rbCtr++; | 
|  | } | 
|  | currBlock++; | 
|  |  | 
|  | bStart[currBlock] = bitsRead; | 
|  | } | 
|  | } | 
|  |  | 
|  | bsClose ( bsIn ); | 
|  |  | 
|  | /*-- identified blocks run from 1 to rbCtr inclusive. --*/ | 
|  |  | 
|  | if (rbCtr < 1) { | 
|  | fprintf ( stderr, | 
|  | "%s: sorry, I couldn't find any block boundaries.\n", | 
|  | progName ); | 
|  | exit(1); | 
|  | }; | 
|  |  | 
|  | fprintf ( stderr, "%s: splitting into blocks\n", progName ); | 
|  |  | 
|  | inFile = fopen ( inFileName, "rb" ); | 
|  | if (inFile == NULL) { | 
|  | fprintf ( stderr, "%s: can't open `%s'\n", progName, inFileName ); | 
|  | exit(1); | 
|  | } | 
|  | bsIn = bsOpenReadStream ( inFile ); | 
|  |  | 
|  | /*-- placate gcc's dataflow analyser --*/ | 
|  | blockCRC = 0; bsWr = 0; | 
|  |  | 
|  | bitsRead = 0; | 
|  | outFile = NULL; | 
|  | wrBlock = 0; | 
|  | while (True) { | 
|  | b = bsGetBit(bsIn); | 
|  | if (b == 2) break; | 
|  | buffHi = (buffHi << 1) | (buffLo >> 31); | 
|  | buffLo = (buffLo << 1) | (b & 1); | 
|  | if (bitsRead == 47+rbStart[wrBlock]) | 
|  | blockCRC = (buffHi << 16) | (buffLo >> 16); | 
|  |  | 
|  | if (outFile != NULL && bitsRead >= rbStart[wrBlock] | 
|  | && bitsRead <= rbEnd[wrBlock]) { | 
|  | bsPutBit ( bsWr, b ); | 
|  | } | 
|  |  | 
|  | bitsRead++; | 
|  |  | 
|  | if (bitsRead == rbEnd[wrBlock]+1) { | 
|  | if (outFile != NULL) { | 
|  | bsPutUChar ( bsWr, 0x17 ); bsPutUChar ( bsWr, 0x72 ); | 
|  | bsPutUChar ( bsWr, 0x45 ); bsPutUChar ( bsWr, 0x38 ); | 
|  | bsPutUChar ( bsWr, 0x50 ); bsPutUChar ( bsWr, 0x90 ); | 
|  | bsPutUInt32 ( bsWr, blockCRC ); | 
|  | bsClose ( bsWr ); | 
|  | } | 
|  | if (wrBlock >= rbCtr) break; | 
|  | wrBlock++; | 
|  | } else | 
|  | if (bitsRead == rbStart[wrBlock]) { | 
|  | outFileName[0] = 0; | 
|  | sprintf ( outFileName, "rec%4d", wrBlock+1 ); | 
|  | for (p = outFileName; *p != 0; p++) if (*p == ' ') *p = '0'; | 
|  | strcat ( outFileName, inFileName ); | 
|  | if ( !endsInBz2(outFileName)) strcat ( outFileName, ".bz2" ); | 
|  |  | 
|  | fprintf ( stderr, "   writing block %d to `%s' ...\n", | 
|  | wrBlock+1, outFileName ); | 
|  |  | 
|  | outFile = fopen ( outFileName, "wb" ); | 
|  | if (outFile == NULL) { | 
|  | fprintf ( stderr, "%s: can't write `%s'\n", | 
|  | progName, outFileName ); | 
|  | exit(1); | 
|  | } | 
|  | bsWr = bsOpenWriteStream ( outFile ); | 
|  | bsPutUChar ( bsWr, 'B' ); bsPutUChar ( bsWr, 'Z' ); | 
|  | bsPutUChar ( bsWr, 'h' ); bsPutUChar ( bsWr, '9' ); | 
|  | bsPutUChar ( bsWr, 0x31 ); bsPutUChar ( bsWr, 0x41 ); | 
|  | bsPutUChar ( bsWr, 0x59 ); bsPutUChar ( bsWr, 0x26 ); | 
|  | bsPutUChar ( bsWr, 0x53 ); bsPutUChar ( bsWr, 0x59 ); | 
|  | } | 
|  | } | 
|  |  | 
|  | fprintf ( stderr, "%s: finished\n", progName ); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  |  | 
|  | /*-----------------------------------------------------------*/ | 
|  | /*--- end                                  bzip2recover.c ---*/ | 
|  | /*-----------------------------------------------------------*/ |