/* Lziprecover - Data recovery tool for the lzip format Copyright (C) 2009-2017 Antonio Diaz Diaz. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #define _FILE_OFFSET_BITS 64 #include #include #include #include #include #include #include #include #include #include #include "lzip.h" #include "block.h" #include "file_index.h" namespace { void first_filename( const std::string & input_filename, const std::string & default_output_filename, const int max_digits ) { output_filename = default_output_filename.empty() ? input_filename : default_output_filename; int b = output_filename.size(); while( b > 0 && output_filename[b-1] != '/' ) --b; output_filename.insert( b, "rec1" ); if( max_digits > 1 ) output_filename.insert( b + 3, max_digits - 1, '0' ); } bool next_filename( const int max_digits ) { int b = output_filename.size(); while( b > 0 && output_filename[b-1] != '/' ) --b; for( int i = b + max_digits + 2; i > b + 2; --i ) // "rec" { if( output_filename[i] < '9' ) { ++output_filename[i]; return true; } else output_filename[i] = '0'; } return false; } // Search forward from 'pos' for "LZIP" (Boyer-Moore algorithm) // Returns pos of found string or 'pos+size' if not found. // int find_magic( const uint8_t * const buffer, const int pos, const int size ) { const unsigned char table[256] = { 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, 4,4,4,4,4,4,4,4,4,1,4,4,3,4,4,4,4,4,4,4,4,4,4,4,4,4,2,4,4,4,4,4, 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 }; for( int i = pos; i <= pos + size - 4; i += table[buffer[i+3]] ) if( buffer[i] == 'L' && buffer[i+1] == 'Z' && buffer[i+2] == 'I' && buffer[i+3] == 'P' ) return i; // magic string found return pos + size; } int do_split_file( const std::string & input_filename, uint8_t * & base_buffer, const std::string & default_output_filename, const int verbosity, const bool force ) { const int hsize = File_header::size; const int tsize = File_trailer::size; const int buffer_size = 65536; const int base_buffer_size = tsize + buffer_size + hsize; base_buffer = new uint8_t[base_buffer_size]; uint8_t * const buffer = base_buffer + tsize; struct stat in_stats; const int infd = open_instream( input_filename.c_str(), &in_stats, true, true ); if( infd < 0 ) return 1; Pretty_print pp( input_filename, verbosity ); int size = seek_read( infd, buffer, buffer_size + hsize, 0 ) - hsize; bool at_stream_end = ( size < buffer_size ); if( size != buffer_size && errno ) { show_error( "Read error", errno ); return 1; } if( size < min_member_size ) { pp( "Input file is too short." ); return 2; } if( !verify_header( *(File_header *)buffer, pp ) ) return 2; const File_index file_index( infd, true, true ); if( file_index.retval() != 0 ) pp( file_index.error().c_str() ); const long max_members = file_index.retval() ? 999999 : file_index.members(); int max_digits = 1; for( long i = max_members; i >= 10; i /= 10 ) ++max_digits; first_filename( input_filename, default_output_filename, max_digits ); if( !open_outstream( force, false, false, false ) ) { close( infd ); return 1; } unsigned long long partial_member_size = 0; while( true ) { int pos = 0; for( int newpos = 1; newpos <= size; ++newpos ) { newpos = find_magic( buffer, newpos, size + 4 - newpos ); if( newpos <= size ) { const File_trailer & trailer = *(File_trailer *)(base_buffer + newpos); if( partial_member_size + newpos - pos == trailer.member_size() ) { // header found const int wr = writeblock( outfd, buffer + pos, newpos - pos ); if( wr != newpos - pos ) { show_error( "Write error", errno ); return 1; } if( close_outstream( &in_stats ) != 0 ) return 1; if( verbosity >= 1 ) { std::printf( "Member '%s' done \r", output_filename.c_str() ); std::fflush( stdout ); } if( !next_filename( max_digits ) ) { show_error( "Too many members in file." ); close( infd ); return 1; } if( !open_outstream( force, false, false, false ) ) { close( infd ); return 1; } partial_member_size = 0; pos = newpos; } } } if( at_stream_end ) { const int wr = writeblock( outfd, buffer + pos, size + hsize - pos ); if( wr != size + hsize - pos ) { show_error( "Write error", errno ); return 1; } break; } if( pos < buffer_size ) { partial_member_size += buffer_size - pos; const int wr = writeblock( outfd, buffer + pos, buffer_size - pos ); if( wr != buffer_size - pos ) { show_error( "Write error", errno ); return 1; } } std::memcpy( base_buffer, base_buffer + buffer_size, tsize + hsize ); size = readblock( infd, buffer + hsize, buffer_size ); at_stream_end = ( size < buffer_size ); if( size != buffer_size && errno ) { show_error( "Read error", errno ); return 1; } } close( infd ); if( close_outstream( &in_stats ) != 0 ) return 1; if( verbosity >= 1 ) { std::printf( "Member '%s' done \n", output_filename.c_str() ); std::fflush( stdout ); } return 0; } } // end namespace bool verify_header( const File_header & header, const Pretty_print & pp ) { if( !header.verify_magic() ) { pp( "Bad magic number (file not in lzip format)." ); return false; } if( !header.verify_version() ) { if( pp.verbosity() >= 0 ) { pp(); std::fprintf( stderr, "Version %d member format not supported.\n", header.version() ); } return false; } return true; } int split_file( const std::string & input_filename, const std::string & default_output_filename, const int verbosity, const bool force ) { uint8_t * base_buffer; const int retval = do_split_file( input_filename, base_buffer, default_output_filename, verbosity, force ); delete[] base_buffer; return retval; }