/* Lziprecover - Data recovery tool for the lzip format Copyright (C) 2009-2019 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 "lzip_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 ) { if( verbosity >= 1 ) { std::printf( "Member '%s' done \n", output_filename.c_str() ); std::fflush( stdout ); } 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; } } // end namespace int split_file( const std::string & input_filename, const std::string & default_output_filename, const bool force ) { struct stat in_stats; const int infd = open_instream( input_filename.c_str(), &in_stats, true, true ); if( infd < 0 ) return 1; Lzip_index lzip_index( infd, true, true, true, true ); if( lzip_index.retval() != 0 ) { show_file_error( input_filename.c_str(), lzip_index.error().c_str() ); return lzip_index.retval(); } // verify last member const Block b = lzip_index.mblock( lzip_index.members() - 1 ); long long mpos = b.pos(); long long msize = b.size(); long long failure_pos = 0; if( !safe_seek( infd, mpos ) ) return 1; if( test_member_from_file( infd, msize, &failure_pos ) == 1 ) { // corrupt or fake trailer while( true ) { mpos += failure_pos; msize -= failure_pos; if( msize < min_member_size ) break; // trailing data if( !safe_seek( infd, mpos ) ) return 1; if( test_member_from_file( infd, msize, &failure_pos ) != 1 ) break; } lzip_index = Lzip_index( infd, true, true, true, true, mpos ); if( lzip_index.retval() != 0 ) { show_file_error( input_filename.c_str(), lzip_index.error().c_str() ); return lzip_index.retval(); } } if( !safe_seek( infd, 0 ) ) return 1; int max_digits = 1; for( long i = lzip_index.blocks( true ); i >= 10; i /= 10 ) ++max_digits; first_filename( input_filename, default_output_filename, max_digits ); long long stream_pos = 0; // first pos not yet written to file set_signal_handler(); for( long i = 0; i < lzip_index.members(); ++i ) { const Block & mb = lzip_index.mblock( i ); if( mb.pos() > stream_pos ) // gap { if( !open_outstream( force, false, false, false ) ) { close( infd ); return 1; } if( !copy_file( infd, outfd, mb.pos() - stream_pos ) || close_outstream( &in_stats ) != 0 ) cleanup_and_fail( 1 ); next_filename( max_digits ); } if( !open_outstream( force, false, false, false ) ) // member { close( infd ); return 1; } if( !copy_file( infd, outfd, mb.size() ) || close_outstream( &in_stats ) != 0 ) cleanup_and_fail( 1 ); next_filename( max_digits ); stream_pos = mb.end(); } if( lzip_index.file_size() > stream_pos ) // trailing data { if( !open_outstream( force, false, false, false ) ) { close( infd ); return 1; } if( !copy_file( infd, outfd, lzip_index.file_size() - stream_pos ) || close_outstream( &in_stats ) != 0 ) cleanup_and_fail( 1 ); next_filename( max_digits ); } close( infd ); return 0; }