summaryrefslogtreecommitdiffstats
path: root/file_index.cc
diff options
context:
space:
mode:
Diffstat (limited to 'file_index.cc')
-rw-r--r--file_index.cc136
1 files changed, 124 insertions, 12 deletions
diff --git a/file_index.cc b/file_index.cc
index 41bee41..997003a 100644
--- a/file_index.cc
+++ b/file_index.cc
@@ -52,21 +52,32 @@ const char * format_num( unsigned long long num,
}
-File_index::File_index( const int infd ) : retval_( 0 )
+Block Block::split( const long long pos )
+ {
+ if( pos_ < pos && end() > pos )
+ {
+ const Block b( pos_, pos - pos_ );
+ pos_ = pos; size_ -= b.size_;
+ return b;
+ }
+ return Block( 0, 0 );
+ }
+
+
+File_index::File_index( const int infd )
+ :
+ isize( lseek( infd, 0, SEEK_END ) ), retval_( 0 )
{
- const long long isize = lseek( infd, 0, SEEK_END );
if( isize < 0 )
{ error_ = "Input file is not seekable :";
error_ += std::strerror( errno ); retval_ = 1; return; }
+ if( isize < min_member_size )
+ { error_ = "Input file is too short."; retval_ = 2; return; }
if( isize > INT64_MAX )
{ error_ = "Input file is too long (2^63 bytes or more).";
retval_ = 2; return; }
- long long pos = isize; // always points to a header or EOF
- File_header header;
- File_trailer trailer;
- if( isize < min_member_size )
- { error_ = "Input file is too short."; retval_ = 2; return; }
+ File_header header;
if( seek_read( infd, header.data, File_header::size, 0 ) != File_header::size )
{ error_ = "Error reading member header :";
error_ += std::strerror( errno ); retval_ = 1; return; }
@@ -77,10 +88,12 @@ File_index::File_index( const int infd ) : retval_( 0 )
{ error_ = "Version "; error_ += format_num( header.version() );
error_ += "member format not supported."; retval_ = 2; return; }
+ long long pos = isize; // always points to a header or to EOF
while( pos >= min_member_size )
{
- if( seek_read( infd, trailer.data, File_trailer::size(),
- pos - File_trailer::size() ) != File_trailer::size() )
+ File_trailer trailer;
+ if( seek_read( infd, trailer.data, File_trailer::size,
+ pos - File_trailer::size ) != File_trailer::size )
{ error_ = "Error reading member trailer :";
error_ += std::strerror( errno ); retval_ = 1; break; }
const long long member_size = trailer.member_size();
@@ -105,14 +118,113 @@ File_index::File_index( const int infd ) : retval_( 0 )
if( member_vector.size() == 0 && isize - pos > File_header::size &&
seek_read( infd, header.data, File_header::size, pos ) == File_header::size &&
header.verify_magic() && header.verify_version() )
- { // last trailer is corrupt
- error_ = "Member size in trailer is corrupt at pos ";
- error_ += format_num( isize - 8 ); retval_ = 2; break;
+ {
+ error_ = "Last member in input file is truncated or corrupt.";
+ retval_ = 2; break;
+ }
+ pos -= member_size;
+ member_vector.push_back( Member( 0, trailer.data_size(),
+ pos, member_size ) );
+ }
+ if( pos != 0 || member_vector.size() == 0 )
+ {
+ member_vector.clear();
+ if( retval_ == 0 ) { error_ = "Can't create file index."; retval_ = 2; }
+ return;
+ }
+ std::reverse( member_vector.begin(), member_vector.end() );
+ for( unsigned i = 0; i < member_vector.size() - 1; ++i )
+ {
+ const long long end = member_vector[i].dblock.end();
+ if( end < 0 || end > INT64_MAX )
+ {
+ member_vector.clear();
+ error_ = "Data in input file is too long (2^63 bytes or more).";
+ retval_ = 2; return;
+ }
+ member_vector[i+1].dblock.pos( end );
+ }
+ }
+
+
+// All files in 'infd_vector' must be at least 'fsize' bytes long.
+File_index::File_index( const std::vector< int > & infd_vector,
+ const long long fsize )
+ :
+ isize( fsize ), retval_( 0 )
+ {
+ if( isize < 0 )
+ { error_ = "Input file is not seekable :";
+ error_ += std::strerror( errno ); retval_ = 1; return; }
+ if( isize < min_member_size )
+ { error_ = "Input file is too short."; retval_ = 2; return; }
+ if( isize > INT64_MAX )
+ { error_ = "Input file is too long (2^63 bytes or more).";
+ retval_ = 2; return; }
+
+ const int files = infd_vector.size();
+ File_header header;
+ bool done = false;
+ for( int i = 0; i < files && !done; ++i )
+ {
+ const int infd = infd_vector[i];
+ if( seek_read( infd, header.data, File_header::size, 0 ) != File_header::size )
+ { error_ = "Error reading member header :";
+ error_ += std::strerror( errno ); retval_ = 1; return; }
+ if( header.verify_magic() && header.verify_version() ) done = true;
+ }
+ if( !done )
+ { error_ = "Bad magic number (file not in lzip format).";
+ retval_ = 2; return; }
+
+ long long pos = isize; // always points to a header or to EOF
+ while( pos >= min_member_size )
+ {
+ long long member_size;
+ File_trailer trailer;
+ done = false;
+ for( int it = 0; it < files && !done; ++it )
+ {
+ const int tfd = infd_vector[it];
+ if( seek_read( tfd, trailer.data, File_trailer::size,
+ pos - File_trailer::size ) != File_trailer::size )
+ { error_ = "Error reading member trailer :";
+ error_ += std::strerror( errno ); retval_ = 1; goto error; }
+ member_size = trailer.member_size();
+ if( member_size >= min_member_size && member_size <= pos )
+ for( int ih = 0; ih < files && !done; ++ih )
+ {
+ const int hfd = infd_vector[ih];
+ if( seek_read( hfd, header.data, File_header::size,
+ pos - member_size ) != File_header::size )
+ { error_ = "Error reading member header :";
+ error_ += std::strerror( errno ); retval_ = 1; goto error; }
+ if( header.verify_magic() && header.verify_version() ) done = true;
+ }
+ }
+ if( !done )
+ {
+ if( member_vector.size() == 0 ) // maybe trailing garbage
+ { --pos; continue; }
+ error_ = "Member size in trailer may be corrupt at pos ";
+ error_ += format_num( pos - 8 ); retval_ = 2; break;
}
+ if( member_vector.size() == 0 && isize - pos > File_header::size )
+ for( int i = 0; i < files; ++i )
+ {
+ const int infd = infd_vector[i];
+ if( seek_read( infd, header.data, File_header::size, pos ) == File_header::size &&
+ header.verify_magic() && header.verify_version() )
+ {
+ error_ = "Last member in input file is truncated or corrupt.";
+ retval_ = 2; goto error;
+ }
+ }
pos -= member_size;
member_vector.push_back( Member( 0, trailer.data_size(),
pos, member_size ) );
}
+error:
if( pos != 0 || member_vector.size() == 0 )
{
member_vector.clear();