Merging upstream version 0.15.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
edd0dce1fe
commit
b3a2ab2af7
51 changed files with 1255 additions and 507 deletions
172
extract.cc
172
extract.cc
|
@ -91,8 +91,8 @@ bool make_path( const std::string & name )
|
|||
// Return value: 0 = OK, 1 = damaged member, 2 = fatal error.
|
||||
// If sizep and error, return in *sizep the number of bytes read.
|
||||
// The first 6 bytes of the archive must be intact for islz to be meaningful.
|
||||
int archive_read( const int infd, uint8_t * const buf, const int size,
|
||||
int * const sizep = 0 )
|
||||
int archive_read( const char * const archive_namep, const int infd,
|
||||
uint8_t * const buf, const int size, int * const sizep = 0 )
|
||||
{
|
||||
static LZ_Decoder * decoder = 0;
|
||||
static bool at_eof = false;
|
||||
|
@ -109,7 +109,8 @@ int archive_read( const int infd, uint8_t * const buf, const int size,
|
|||
const int rd = readblock( infd, buf, size );
|
||||
if( sizep ) *sizep = rd;
|
||||
if( rd != size && errno )
|
||||
{ show_error( "Error reading archive", errno ); fatal = true; return 2; }
|
||||
{ show_file_error( archive_namep, "Error reading archive", errno );
|
||||
fatal = true; return 2; }
|
||||
const Lzip_header & header = (*(const Lzip_header *)buf);
|
||||
bool islz = ( rd >= min_member_size && header.verify_magic() &&
|
||||
header.verify_version() &&
|
||||
|
@ -119,7 +120,7 @@ int archive_read( const int infd, uint8_t * const buf, const int size,
|
|||
( !islz && !istar && rd == size && block_is_zero( buf, size ) );
|
||||
if( !islz && !istar && !iseof ) // corrupt or invalid format
|
||||
{
|
||||
show_error( "This does not look like a POSIX tar archive." );
|
||||
show_file_error( archive_namep, posix_msg );
|
||||
if( archive_has_lz_ext && rd >= min_member_size ) islz = true;
|
||||
if( !islz ) return 1;
|
||||
}
|
||||
|
@ -132,10 +133,10 @@ int archive_read( const int infd, uint8_t * const buf, const int size,
|
|||
LZ_decompress_close( decoder ); fatal = true; return 2; }
|
||||
if( LZ_decompress_write( decoder, buf, rd ) != rd )
|
||||
internal_error( "library error (LZ_decompress_write)." );
|
||||
const int res = archive_read( infd, buf, size, sizep );
|
||||
const int res = archive_read( archive_namep, infd, buf, size, sizep );
|
||||
if( res != 0 ) { if( res == 2 ) fatal = true; return res; }
|
||||
if( verify_ustar_chksum( buf ) || block_is_zero( buf, size ) ) return 0;
|
||||
show_error( "This does not look like a POSIX tar.lz archive." );
|
||||
show_file_error( archive_namep, posix_lz_msg );
|
||||
fatal = true; return 2;
|
||||
}
|
||||
|
||||
|
@ -143,7 +144,7 @@ int archive_read( const int infd, uint8_t * const buf, const int size,
|
|||
{
|
||||
const int rd = readblock( infd, buf, size ); if( rd == size ) return 0;
|
||||
if( sizep ) *sizep = rd;
|
||||
show_error( "Archive ends unexpectedly." ); fatal = true; return 2;
|
||||
show_file_error( archive_namep, end_msg ); fatal = true; return 2;
|
||||
}
|
||||
const int ibuf_size = 16384;
|
||||
uint8_t ibuf[ibuf_size];
|
||||
|
@ -159,11 +160,8 @@ int archive_read( const int infd, uint8_t * const buf, const int size,
|
|||
}
|
||||
if( rd == 0 && LZ_decompress_finished( decoder ) == 1 )
|
||||
{ LZ_decompress_close( decoder );
|
||||
show_error( "Archive ends unexpectedly." ); fatal = true; return 2; }
|
||||
show_file_error( archive_namep, end_msg ); fatal = true; return 2; }
|
||||
sz += rd; if( sizep ) *sizep = sz;
|
||||
if( sz == size && LZ_decompress_finished( decoder ) == 1 &&
|
||||
LZ_decompress_close( decoder ) < 0 )
|
||||
{ show_error( "LZ_decompress_close failed." ); fatal = true; return 2; }
|
||||
if( sz < size && !at_eof && LZ_decompress_write_size( decoder ) > 0 )
|
||||
{
|
||||
const int rsize = std::min( ibuf_size, LZ_decompress_write_size( decoder ) );
|
||||
|
@ -174,8 +172,8 @@ int archive_read( const int infd, uint8_t * const buf, const int size,
|
|||
{
|
||||
at_eof = true; LZ_decompress_finish( decoder );
|
||||
if( errno )
|
||||
{ show_error( "Error reading archive", errno ); fatal = true;
|
||||
return 2; }
|
||||
{ show_file_error( archive_namep, "Error reading archive", errno );
|
||||
fatal = true; return 2; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -292,7 +290,6 @@ bool format_member_name( const Extended & extended, const Tar_header header,
|
|||
return true;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
bool show_member_name( const Extended & extended, const Tar_header header,
|
||||
const int vlevel, Resizable_buffer & rbuf )
|
||||
|
@ -307,22 +304,23 @@ bool show_member_name( const Extended & extended, const Tar_header header,
|
|||
return true;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
int skip_member( const int infd, const Extended & extended )
|
||||
int skip_member( const char * const archive_namep, const int infd,
|
||||
const Extended & extended )
|
||||
{
|
||||
unsigned long long rest = extended.file_size();
|
||||
long long rest = extended.file_size();
|
||||
const int rem = rest % header_size;
|
||||
const int padding = rem ? header_size - rem : 0;
|
||||
if( archive_is_uncompressed_seekable &&
|
||||
lseek( infd, rest + padding, SEEK_CUR ) > 0 ) return 0;
|
||||
const unsigned bufsize = 32 * header_size;
|
||||
if( rem ) rest += header_size - rem; // padding
|
||||
if( archive_is_uncompressed_seekable && lseek( infd, rest, SEEK_CUR ) > 0 )
|
||||
return 0;
|
||||
const int bufsize = 32 * header_size;
|
||||
uint8_t buf[bufsize];
|
||||
while( rest > 0 )
|
||||
{
|
||||
const int rsize = ( rest >= bufsize ) ? bufsize : rest + padding;
|
||||
const int ret = archive_read( infd, buf, rsize );
|
||||
const int rsize = ( rest >= bufsize ) ? bufsize : rest;
|
||||
const int ret = archive_read( archive_namep, infd, buf, rsize );
|
||||
if( ret != 0 ) { if( ret == 2 ) return 2; else break; }
|
||||
if( rest < bufsize ) break;
|
||||
rest -= rsize;
|
||||
}
|
||||
return 0;
|
||||
|
@ -336,11 +334,12 @@ void show_file_diff( const char * const filename, const char * const msg )
|
|||
}
|
||||
|
||||
|
||||
int compare_member( const int infd1, const Extended & extended,
|
||||
const Tar_header header, const bool ignore_ids )
|
||||
int compare_member( const char * const archive_namep, const int infd1,
|
||||
const Extended & extended, const Tar_header header,
|
||||
const bool ignore_ids )
|
||||
{
|
||||
if( !show_member_name( extended, header, 1, grbuf ) ) return 1;
|
||||
unsigned long long rest = extended.file_size();
|
||||
long long rest = extended.file_size();
|
||||
const char * const filename = extended.path().c_str();
|
||||
const Typeflag typeflag = (Typeflag)header[typeflag_o];
|
||||
bool diff = false, size_differs = false, type_differs = true;
|
||||
|
@ -386,7 +385,7 @@ int compare_member( const int infd1, const Extended & extended,
|
|||
{ show_file_diff( filename, "Mod time differs" ); diff = true; }
|
||||
}
|
||||
if( ( typeflag == tf_regular || typeflag == tf_hiperf ) &&
|
||||
(off_t)rest != st.st_size ) // don't compare contents
|
||||
rest != st.st_size ) // don't compare contents
|
||||
{ show_file_diff( filename, "Size differs" ); size_differs = true; }
|
||||
if( ( typeflag == tf_chardev || typeflag == tf_blockdev ) &&
|
||||
( parse_octal( header + devmajor_o, devmajor_l ) !=
|
||||
|
@ -412,24 +411,26 @@ int compare_member( const int infd1, const Extended & extended,
|
|||
}
|
||||
if( diff || size_differs || type_differs )
|
||||
{ diff = false; set_error_status( 1 ); }
|
||||
if( rest == 0 ) return 0;
|
||||
if( rest <= 0 ) return 0;
|
||||
if( ( typeflag != tf_regular && typeflag != tf_hiperf ) ||
|
||||
size_differs || type_differs ) return skip_member( infd1, extended );
|
||||
size_differs || type_differs )
|
||||
return skip_member( archive_namep, infd1, extended );
|
||||
// else compare file contents
|
||||
const int rem = rest % header_size;
|
||||
const int padding = rem ? header_size - rem : 0;
|
||||
const unsigned bufsize = 32 * header_size;
|
||||
const int bufsize = 32 * header_size;
|
||||
uint8_t buf1[bufsize];
|
||||
uint8_t buf2[bufsize];
|
||||
const int infd2 = open_instream( filename );
|
||||
if( infd2 < 0 )
|
||||
{ set_error_status( 1 ); return skip_member( infd1, extended ); }
|
||||
{ set_error_status( 1 );
|
||||
return skip_member( archive_namep, infd1, extended ); }
|
||||
int retval = 0;
|
||||
while( rest > 0 )
|
||||
{
|
||||
const int rsize1 = ( rest >= bufsize ) ? bufsize : rest + padding;
|
||||
const int rsize2 = ( rest >= bufsize ) ? bufsize : rest;
|
||||
const int ret = archive_read( infd1, buf1, rsize1 );
|
||||
const int ret = archive_read( archive_namep, infd1, buf1, rsize1 );
|
||||
if( ret != 0 ) { if( ret == 2 ) retval = 2; diff = true; break; }
|
||||
if( !diff )
|
||||
{
|
||||
|
@ -456,11 +457,11 @@ int compare_member( const int infd1, const Extended & extended,
|
|||
}
|
||||
|
||||
|
||||
int list_member( const int infd, const Extended & extended,
|
||||
const Tar_header header )
|
||||
int list_member( const char * const archive_namep, const int infd,
|
||||
const Extended & extended, const Tar_header header )
|
||||
{
|
||||
if( !show_member_name( extended, header, 0, grbuf ) ) return 1;
|
||||
return skip_member( infd, extended );
|
||||
return skip_member( archive_namep, infd, extended );
|
||||
}
|
||||
|
||||
|
||||
|
@ -472,14 +473,15 @@ bool contains_dotdot( const char * const filename )
|
|||
}
|
||||
|
||||
|
||||
int extract_member( const int infd, const Extended & extended,
|
||||
const Tar_header header, const bool keep_damaged )
|
||||
int extract_member( const char * const archive_namep, const int infd,
|
||||
const Extended & extended, const Tar_header header,
|
||||
const bool keep_damaged )
|
||||
{
|
||||
const char * const filename = extended.path().c_str();
|
||||
if( contains_dotdot( filename ) )
|
||||
{
|
||||
show_file_error( filename, "Contains a '..' component, skipping." );
|
||||
return skip_member( infd, extended );
|
||||
return skip_member( archive_namep, infd, extended );
|
||||
}
|
||||
const mode_t mode = parse_octal( header + mode_o, mode_l ); // 12 bits
|
||||
const time_t mtime = parse_octal( header + mtime_o, mtime_l ); // 33 bits
|
||||
|
@ -557,22 +559,22 @@ int extract_member( const int infd, const Extended & extended,
|
|||
return 2;
|
||||
}
|
||||
|
||||
const unsigned bufsize = 32 * header_size;
|
||||
const int bufsize = 32 * header_size;
|
||||
uint8_t buf[bufsize];
|
||||
unsigned long long rest = extended.file_size();
|
||||
long long rest = extended.file_size();
|
||||
const int rem = rest % header_size;
|
||||
const int padding = rem ? header_size - rem : 0;
|
||||
while( rest > 0 )
|
||||
{
|
||||
const int rsize = ( rest >= bufsize ) ? bufsize : rest + padding;
|
||||
int rd;
|
||||
const int ret = archive_read( infd, buf, rsize, &rd );
|
||||
const int ret = archive_read( archive_namep, infd, buf, rsize, &rd );
|
||||
if( ret != 0 )
|
||||
{
|
||||
if( outfd >= 0 )
|
||||
{
|
||||
if( keep_damaged )
|
||||
{ writeblock( outfd, buf, std::min( rest, (unsigned long long)rd ) );
|
||||
{ writeblock( outfd, buf, std::min( rest, (long long)rd ) );
|
||||
close( outfd ); }
|
||||
else { close( outfd ); std::remove( filename ); }
|
||||
}
|
||||
|
@ -620,16 +622,16 @@ bool compare_tslash( const char * const name1, const char * const name2 )
|
|||
|
||||
namespace {
|
||||
|
||||
bool parse_records( const int infd, Extended & extended,
|
||||
const Tar_header header, Resizable_buffer & rbuf,
|
||||
const bool permissive )
|
||||
bool parse_records( const char * const archive_namep, const int infd,
|
||||
Extended & extended, const Tar_header header,
|
||||
Resizable_buffer & rbuf, const bool permissive )
|
||||
{
|
||||
const unsigned long long edsize = parse_octal( header + size_o, size_l );
|
||||
const unsigned long long bufsize = round_up( edsize );
|
||||
if( edsize == 0 || edsize >= 1ULL << 33 || bufsize == 0 || bufsize >= INT_MAX )
|
||||
const long long edsize = parse_octal( header + size_o, size_l );
|
||||
const long long bufsize = round_up( edsize );
|
||||
if( edsize <= 0 || edsize >= 1LL << 33 || bufsize >= INT_MAX )
|
||||
return false; // overflow or no extended data
|
||||
if( !rbuf.resize( bufsize ) ) return false; // extended records buffer
|
||||
return ( archive_read( infd, (uint8_t *)rbuf(), bufsize ) == 0 &&
|
||||
return ( archive_read( archive_namep, infd, (uint8_t *)rbuf(), bufsize ) == 0 &&
|
||||
extended.parse( rbuf(), edsize, permissive ) );
|
||||
}
|
||||
|
||||
|
@ -690,8 +692,10 @@ int decode( const std::string & archive_name, const Arg_parser & parser,
|
|||
const bool keep_damaged, const bool missing_crc,
|
||||
const bool permissive )
|
||||
{
|
||||
const int infd = archive_name.size() ?
|
||||
open_instream( archive_name ) : STDIN_FILENO;
|
||||
const bool from_stdin = archive_name.empty();
|
||||
const char * const archive_namep =
|
||||
from_stdin ? "(stdin)" : archive_name.c_str();
|
||||
const int infd = from_stdin ? STDIN_FILENO : open_instream( archive_name );
|
||||
if( infd < 0 ) return 1;
|
||||
|
||||
// Execute -C options and mark filenames to be compared, extracted or listed.
|
||||
|
@ -719,9 +723,10 @@ int decode( const std::string & archive_name, const Arg_parser & parser,
|
|||
const long members = lzip_index.members();
|
||||
if( lzip_index.retval() == 0 && members >= 2 ) // one file + eof
|
||||
{
|
||||
// show_file_error( archive_name.c_str(), "Is compressed seekable" );
|
||||
return list_lz( parser, name_pending, lzip_index, filenames, debug_level,
|
||||
infd, std::min( (long)num_workers, members ),
|
||||
// show_file_error( archive_namep, "Is compressed seekable" );
|
||||
return list_lz( archive_namep, parser, name_pending, lzip_index,
|
||||
filenames, debug_level, infd,
|
||||
std::min( (long)num_workers, members ),
|
||||
missing_crc, permissive );
|
||||
}
|
||||
if( lseek( infd, 0, SEEK_SET ) == 0 && lzip_index.retval() != 0 &&
|
||||
|
@ -733,19 +738,18 @@ int decode( const std::string & archive_name, const Arg_parser & parser,
|
|||
Extended extended; // metadata from extended records
|
||||
int retval = 0;
|
||||
bool prev_extended = false; // prev header was extended
|
||||
while( true ) // process one tar member per iteration
|
||||
while( true ) // process one tar header per iteration
|
||||
{
|
||||
Tar_header header;
|
||||
const int ret = archive_read( infd, header, header_size );
|
||||
if( ret == 2 ) return 2;
|
||||
const int ret = archive_read( archive_namep, infd, header, header_size );
|
||||
if( ret == 2 ) { retval = 2; break; }
|
||||
if( ret != 0 || !verify_ustar_chksum( header ) )
|
||||
{
|
||||
if( ret == 0 && block_is_zero( header, header_size ) )
|
||||
{
|
||||
if( !prev_extended ) break; // EOF
|
||||
show_file_error( archive_name.c_str(),
|
||||
"Format violation: extended header followed by EOF blocks." );
|
||||
return 2;
|
||||
if( !prev_extended || permissive ) break; // EOF
|
||||
show_file_error( archive_namep, fv_msg1 );
|
||||
retval = 2; break;
|
||||
}
|
||||
if( skip_warn() && verbosity >= 2 )
|
||||
std::fprintf( stderr, "ustar chksum = %07o\n", ustar_chksum( header ) );
|
||||
|
@ -756,13 +760,11 @@ int decode( const std::string & archive_name, const Arg_parser & parser,
|
|||
const Typeflag typeflag = (Typeflag)header[typeflag_o];
|
||||
if( typeflag == tf_global )
|
||||
{
|
||||
if( prev_extended )
|
||||
{ show_file_error( archive_name.c_str(),
|
||||
"Format violation: extended header followed by global header." );
|
||||
return 2; }
|
||||
if( prev_extended && !permissive )
|
||||
{ show_file_error( archive_namep, fv_msg2 ); retval = 2; break; }
|
||||
Extended dummy; // global headers are parsed and ignored
|
||||
if( !parse_records( infd, dummy, header, grbuf, true ) )
|
||||
{ show_file_error( archive_name.c_str(),
|
||||
if( !parse_records( archive_namep, infd, dummy, header, grbuf, true ) )
|
||||
{ show_file_error( archive_namep,
|
||||
"Error in global extended records. Skipping to next header." );
|
||||
set_error_status( 2 ); }
|
||||
continue;
|
||||
|
@ -770,16 +772,14 @@ int decode( const std::string & archive_name, const Arg_parser & parser,
|
|||
if( typeflag == tf_extended )
|
||||
{
|
||||
if( prev_extended && !permissive )
|
||||
{ show_file_error( archive_name.c_str(),
|
||||
"Format violation: consecutive extended headers found."
|
||||
/*" Use --permissive.", 0, true*/ ); return 2; }
|
||||
if( !parse_records( infd, extended, header, grbuf, permissive ) )
|
||||
{ show_file_error( archive_name.c_str(),
|
||||
{ show_file_error( archive_namep, fv_msg3 ); retval = 2; break; }
|
||||
if( !parse_records( archive_namep, infd, extended, header, grbuf,
|
||||
permissive ) )
|
||||
{ show_file_error( archive_namep,
|
||||
"Error in extended records. Skipping to next header." );
|
||||
extended.reset(); set_error_status( 2 ); }
|
||||
else if( !extended.crc_present() && missing_crc )
|
||||
{ show_file_error( archive_name.c_str(),
|
||||
"Missing CRC in extended records." ); return 2; }
|
||||
{ show_file_error( archive_namep, mcrc_msg ); retval = 2; break; }
|
||||
prev_extended = true;
|
||||
continue;
|
||||
}
|
||||
|
@ -787,23 +787,25 @@ int decode( const std::string & archive_name, const Arg_parser & parser,
|
|||
|
||||
extended.fill_from_ustar( header ); // copy metadata from header
|
||||
|
||||
const bool skip = check_skip_filename( parser, name_pending,
|
||||
extended.path().c_str(), filenames );
|
||||
if( skip )
|
||||
retval = skip_member( infd, extended );
|
||||
if( check_skip_filename( parser, name_pending, extended.path().c_str(),
|
||||
filenames ) )
|
||||
retval = skip_member( archive_namep, infd, extended );
|
||||
else if( program_mode == m_list )
|
||||
retval = list_member( infd, extended, header );
|
||||
retval = list_member( archive_namep, infd, extended, header );
|
||||
else if( program_mode == m_diff )
|
||||
retval = compare_member( infd, extended, header, ignore_ids );
|
||||
else
|
||||
retval = extract_member( infd, extended, header, keep_damaged );
|
||||
retval = compare_member( archive_namep, infd, extended, header, ignore_ids );
|
||||
else retval = extract_member( archive_namep, infd, extended, header,
|
||||
keep_damaged );
|
||||
extended.reset();
|
||||
if( retval )
|
||||
{ show_error( "Error is not recoverable: exiting now." );
|
||||
return retval; }
|
||||
{ show_error( "Error is not recoverable: exiting now." ); break; }
|
||||
}
|
||||
|
||||
for( int i = 0; i < parser.arguments(); ++i )
|
||||
if( close( infd ) != 0 && !retval )
|
||||
{ show_file_error( archive_namep, "Error closing archive", errno );
|
||||
retval = 1; }
|
||||
|
||||
if( retval == 0 ) for( int i = 0; i < parser.arguments(); ++i )
|
||||
if( !parser.code( i ) && parser.argument( i ).size() && name_pending[i] )
|
||||
{
|
||||
show_file_error( parser.argument( i ).c_str(), "Not found in archive." );
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue