2025-02-21 11:21:20 +01:00
|
|
|
/* Lziprecover - Data recovery tool for the lzip format
|
|
|
|
Copyright (C) 2009-2014 Antonio Diaz Diaz.
|
2025-02-21 10:09:52 +01:00
|
|
|
|
|
|
|
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
|
2025-02-21 11:20:42 +01:00
|
|
|
the Free Software Foundation, either version 2 of the License, or
|
2025-02-21 10:09:52 +01:00
|
|
|
(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>
|
2025-02-21 10:12:24 +01:00
|
|
|
#include <unistd.h>
|
2025-02-21 10:09:52 +01:00
|
|
|
#include <sys/stat.h>
|
|
|
|
|
|
|
|
#include "lzip.h"
|
2025-02-21 11:13:27 +01:00
|
|
|
#include "file_index.h"
|
2025-02-21 10:09:52 +01:00
|
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
void first_filename( const std::string & input_filename,
|
|
|
|
const std::string & default_output_filename,
|
2025-02-21 11:13:27 +01:00
|
|
|
std::string & output_filename, const int max_digits )
|
2025-02-21 10:09:52 +01:00
|
|
|
{
|
|
|
|
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;
|
2025-02-21 11:13:27 +01:00
|
|
|
output_filename.insert( b, 1, '1' );
|
|
|
|
if( max_digits > 1 ) output_filename.insert( b, max_digits - 1, '0' );
|
|
|
|
output_filename.insert( b, "rec" );
|
2025-02-21 10:09:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2025-02-21 11:13:27 +01:00
|
|
|
bool next_filename( std::string & output_filename, const int max_digits )
|
2025-02-21 10:09:52 +01:00
|
|
|
{
|
|
|
|
int b = output_filename.size();
|
|
|
|
while( b > 0 && output_filename[b-1] != '/' ) --b;
|
2025-02-21 11:13:27 +01:00
|
|
|
for( int i = b + max_digits + 2; i > b + 2; --i ) // "rec<max_digits>"
|
2025-02-21 10:09:52 +01:00
|
|
|
{
|
|
|
|
if( output_filename[i] < '9' ) { ++output_filename[i]; return true; }
|
|
|
|
else output_filename[i] = '0';
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2025-02-21 11:19:49 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2025-02-21 10:09:52 +01:00
|
|
|
// Search forward from 'pos' for "LZIP" (Boyer-Moore algorithm)
|
|
|
|
// Return pos of found string or 'pos+size' if not found.
|
|
|
|
//
|
2025-02-21 10:13:11 +01:00
|
|
|
int find_magic( const uint8_t * const buffer, const int pos, const int size )
|
2025-02-21 10:09:52 +01:00
|
|
|
{
|
|
|
|
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,
|
2025-02-21 10:16:51 +01:00
|
|
|
const std::string & default_output_filename,
|
|
|
|
const int verbosity, const bool force )
|
2025-02-21 10:09:52 +01:00
|
|
|
{
|
|
|
|
const int hsize = File_header::size;
|
2025-02-21 11:16:33 +01:00
|
|
|
const int tsize = File_trailer::size;
|
2025-02-21 10:09:52 +01:00
|
|
|
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;
|
2025-02-21 11:19:49 +01:00
|
|
|
const int infd = open_instream( input_filename.c_str(), &in_stats, true, true );
|
2025-02-21 10:09:52 +01:00
|
|
|
if( infd < 0 ) return 1;
|
2025-02-21 11:19:49 +01:00
|
|
|
Pretty_print pp( input_filename, verbosity );
|
2025-02-21 11:13:27 +01:00
|
|
|
int size = seek_read( infd, buffer, buffer_size + hsize, 0 ) - hsize;
|
2025-02-21 10:09:52 +01:00
|
|
|
bool at_stream_end = ( size < buffer_size );
|
|
|
|
if( size != buffer_size && errno )
|
|
|
|
{ show_error( "Read error", errno ); return 1; }
|
2025-02-21 10:16:51 +01:00
|
|
|
if( size < min_member_size )
|
2025-02-21 11:19:49 +01:00
|
|
|
{ pp( "Input file is too short." ); return 2; }
|
|
|
|
if( !verify_header( *(File_header *)buffer, pp ) ) return 2;
|
|
|
|
|
|
|
|
const File_index file_index( infd );
|
|
|
|
if( file_index.retval() != 0 ) pp( file_index.error().c_str() );
|
2025-02-21 11:20:21 +01:00
|
|
|
const long max_members = file_index.retval() ? 999999 : file_index.members();
|
2025-02-21 11:19:49 +01:00
|
|
|
int max_digits = 1;
|
2025-02-21 11:20:21 +01:00
|
|
|
for( long i = max_members; i >= 10; i /= 10 ) ++max_digits;
|
2025-02-21 10:09:52 +01:00
|
|
|
|
|
|
|
std::string output_filename;
|
2025-02-21 11:13:27 +01:00
|
|
|
first_filename( input_filename, default_output_filename, output_filename,
|
|
|
|
max_digits );
|
2025-02-21 10:09:52 +01:00
|
|
|
int outfd = open_outstream_rw( output_filename, force );
|
|
|
|
if( outfd < 0 ) { close( infd ); return 1; }
|
|
|
|
|
2025-02-21 10:16:51 +01:00
|
|
|
unsigned long long partial_member_size = 0;
|
2025-02-21 10:09:52 +01:00
|
|
|
while( true )
|
|
|
|
{
|
|
|
|
int pos = 0;
|
|
|
|
for( int newpos = 1; newpos <= size; ++newpos )
|
|
|
|
{
|
|
|
|
newpos = find_magic( buffer, newpos, size + 4 - newpos );
|
|
|
|
if( newpos <= size )
|
|
|
|
{
|
2025-02-21 10:16:51 +01:00
|
|
|
const File_trailer & trailer = *(File_trailer *)(base_buffer + newpos);
|
|
|
|
if( partial_member_size + newpos - pos == trailer.member_size() )
|
2025-02-21 10:09:52 +01:00
|
|
|
{ // 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; }
|
2025-02-21 11:13:27 +01:00
|
|
|
if( verbosity >= 1 )
|
|
|
|
{
|
|
|
|
std::printf( "Member '%s' done \r", output_filename.c_str() );
|
|
|
|
std::fflush( stdout );
|
|
|
|
}
|
|
|
|
if( !next_filename( output_filename, max_digits ) )
|
2025-02-21 10:09:52 +01:00
|
|
|
{ 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; }
|
2025-02-21 11:13:27 +01:00
|
|
|
if( verbosity >= 1 )
|
|
|
|
{
|
|
|
|
std::printf( "Member '%s' done \n", output_filename.c_str() );
|
|
|
|
std::fflush( stdout );
|
|
|
|
}
|
2025-02-21 10:09:52 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // end namespace
|
|
|
|
|
|
|
|
|
|
|
|
int split_file( const std::string & input_filename,
|
2025-02-21 10:16:51 +01:00
|
|
|
const std::string & default_output_filename,
|
|
|
|
const int verbosity, const bool force )
|
2025-02-21 10:09:52 +01:00
|
|
|
{
|
|
|
|
uint8_t * base_buffer;
|
|
|
|
const int retval = do_split_file( input_filename, base_buffer,
|
2025-02-21 10:16:51 +01:00
|
|
|
default_output_filename, verbosity, force );
|
2025-02-21 10:09:52 +01:00
|
|
|
delete[] base_buffer;
|
|
|
|
return retval;
|
|
|
|
}
|