diff options
Diffstat (limited to 'split.cc')
-rw-r--r-- | split.cc | 176 |
1 files changed, 176 insertions, 0 deletions
diff --git a/split.cc b/split.cc new file mode 100644 index 0000000..d6538f8 --- /dev/null +++ b/split.cc @@ -0,0 +1,176 @@ +/* Lziprecover - Data recovery tool for lzipped files + Copyright (C) 2009, 2010, 2011 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 3 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 <http://www.gnu.org/licenses/>. +*/ + +#define _FILE_OFFSET_BITS 64 + +#include <cerrno> +#include <climits> +#include <cstdio> +#include <cstring> +#include <string> +#include <vector> +#include <stdint.h> +#include <sys/stat.h> + +#include "lzip.h" + + +namespace { + +void first_filename( const std::string & input_filename, + const std::string & default_output_filename, + std::string & output_filename ) + { + if( default_output_filename.size() ) + output_filename = default_output_filename; + else + output_filename = input_filename; + int b = output_filename.size(); + while( b > 0 && output_filename[b-1] != '/' ) --b; + output_filename.insert( b, "rec00001" ); + } + + +bool next_filename( std::string & output_filename ) + { + int b = output_filename.size(); + while( b > 0 && output_filename[b-1] != '/' ) --b; + for( int i = b + 7; i >= b + 3; --i ) // "rec00001" + { + 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) +// Return pos of found string or 'pos+size' if not found. +// +int find_magic( const uint8_t * const buffer, const int pos, const int size ) throw() + { + 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 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, &in_stats, true, true ); + if( infd < 0 ) return 1; + int size = readblock( infd, buffer, buffer_size + hsize ) - hsize; + bool at_stream_end = ( size < buffer_size ); + if( size != buffer_size && errno ) + { show_error( "Read error", errno ); return 1; } + if( size <= tsize ) + { show_error( "Input file is too short." ); return 2; } + if( !verify_header( *(File_header *)buffer ) ) return 2; + + std::string output_filename; + first_filename( input_filename, default_output_filename, output_filename ); + int outfd = open_outstream_rw( output_filename, force ); + if( outfd < 0 ) { close( infd ); return 1; } + + 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 ) + { + long long member_size = 0; + for( int i = 1; i <= 8; ++i ) + { member_size <<= 8; member_size += base_buffer[tsize+newpos-i]; } + if( partial_member_size + newpos - pos == 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( outfd ) != 0 ) + { show_error( "Error closing output file", errno ); return 1; } + if( !next_filename( output_filename ) ) + { show_error( "Too many members in file." ); close( infd ); return 1; } + outfd = open_outstream_rw( output_filename, force ); + if( outfd < 0 ) { 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( outfd ) != 0 ) + { show_error( "Error closing output file", errno ); return 1; } + return 0; + } + +} // end namespace + + +int split_file( const std::string & input_filename, + const std::string & default_output_filename, const bool force ) + { + uint8_t * base_buffer; + const int retval = do_split_file( input_filename, base_buffer, + default_output_filename, force ); + delete[] base_buffer; + return retval; + } |