| |
| /*-----------------------------------------------------------*/ |
| /*--- 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 ---*/ |
| /*-----------------------------------------------------------*/ |