diff options
Diffstat (limited to 'extract.cc')
-rw-r--r-- | extract.cc | 94 |
1 files changed, 54 insertions, 40 deletions
@@ -44,9 +44,9 @@ namespace { -Resizable_buffer grbuf( initial_line_length ); +Resizable_buffer grbuf; bool archive_is_uncompressed_seekable = false; -bool has_lz_ext; // global var for archive_read +bool archive_has_lz_ext; // local var for archive_read bool skip_warn( const bool reset = false ) // avoid duplicate warnings { @@ -120,7 +120,7 @@ int archive_read( const int infd, uint8_t * const buf, const int size, if( !islz && !istar && !iseof ) // corrupt or invalid format { show_error( "This does not look like a POSIX tar archive." ); - if( has_lz_ext && rd >= min_member_size ) islz = true; + if( archive_has_lz_ext && rd >= min_member_size ) islz = true; if( !islz ) return 1; } if( !islz ) // uncompressed @@ -247,7 +247,7 @@ bool block_is_zero( const uint8_t * const buf, const int size ) } -void format_member_name( const Extended & extended, const Tar_header header, +bool format_member_name( const Extended & extended, const Tar_header header, Resizable_buffer & rbuf, const bool long_format ) { if( long_format ) @@ -279,27 +279,32 @@ void format_member_name( const Extended & extended, const Tar_header header, 1900 + tm->tm_year, 1 + tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, extended.path().c_str(), link_string, islink ? extended.linkpath().c_str() : "" ); - if( (int)rbuf.size() > len + offset || !rbuf.resize( len + offset + 1 ) ) - break; + if( (int)rbuf.size() > len + offset ) break; + if( !rbuf.resize( len + offset + 1 ) ) return false; } } else { - if( rbuf.size() < extended.path().size() + 2 ) - rbuf.resize( extended.path().size() + 2 ); + if( rbuf.size() < extended.path().size() + 2 && + !rbuf.resize( extended.path().size() + 2 ) ) return false; snprintf( rbuf(), rbuf.size(), "%s\n", extended.path().c_str() ); } + return true; } namespace { -void show_member_name( const Extended & extended, const Tar_header header, +bool show_member_name( const Extended & extended, const Tar_header header, const int vlevel, Resizable_buffer & rbuf ) { - if( verbosity < vlevel ) return; - format_member_name( extended, header, rbuf, verbosity > vlevel ); - std::fputs( rbuf(), stdout ); - std::fflush( stdout ); + if( verbosity >= vlevel ) + { + if( !format_member_name( extended, header, rbuf, verbosity > vlevel ) ) + { show_error( mem_msg ); return false; } + std::fputs( rbuf(), stdout ); + std::fflush( stdout ); + } + return true; } @@ -326,20 +331,21 @@ int skip_member( const int infd, const Extended & extended ) void show_file_diff( const char * const filename, const char * const msg ) { - if( verbosity >= 0 ) std::fprintf( stderr, "%s: %s\n", filename, msg ); + if( verbosity >= 0 ) + { std::printf( "%s: %s\n", filename, msg ); std::fflush( stdout ); } } int compare_member( const int infd1, const Extended & extended, const Tar_header header, const bool ignore_ids ) { - show_member_name( extended, header, 1, grbuf ); + if( !show_member_name( extended, header, 1, grbuf ) ) return 1; unsigned 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; struct stat st; - if( lstat( filename, &st ) != 0 ) + if( hstat( filename, &st ) != 0 ) show_file_error( filename, "Warning: Can't stat", errno ); else if( ( typeflag == tf_regular || typeflag == tf_hiperf ) && !S_ISREG( st.st_mode ) ) @@ -453,7 +459,7 @@ int compare_member( const int infd1, const Extended & extended, int list_member( const int infd, const Extended & extended, const Tar_header header ) { - show_member_name( extended, header, 0, grbuf ); + if( !show_member_name( extended, header, 0, grbuf ) ) return 1; return skip_member( infd, extended ); } @@ -481,7 +487,7 @@ int extract_member( const int infd, const Extended & extended, const bool islink = ( typeflag == tf_link || typeflag == tf_symlink ); int outfd = -1; - show_member_name( extended, header, 1, grbuf ); + if( !show_member_name( extended, header, 1, grbuf ) ) return 1; std::remove( filename ); make_path( filename ); switch( typeflag ) @@ -615,17 +621,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, const bool permissive ) + 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( bufsize == 0 || edsize == 0 || edsize >= 1ULL << 33 ) + if( edsize == 0 || edsize >= 1ULL << 33 || bufsize == 0 || bufsize >= INT_MAX ) return false; // overflow or no extended data - char * const buf = new char[bufsize]; // extended records buffer - const bool ret = ( archive_read( infd, (uint8_t *)buf, bufsize ) == 0 && - extended.parse( buf, edsize, permissive ) ); - delete[] buf; - return ret; + if( !rbuf.resize( bufsize ) ) return false; // extended records buffer + return ( archive_read( infd, (uint8_t *)rbuf(), bufsize ) == 0 && + extended.parse( rbuf(), edsize, permissive ) ); } } // end namespace @@ -702,7 +707,9 @@ int decode( const std::string & archive_name, const Arg_parser & parser, { show_file_error( dir, "Error changing working directory", errno ); return 1; } } - if( !code && parser.argument( i ).size() ) name_pending[i] = true; + if( !code && parser.argument( i ).size() && + !Exclude::excluded( parser.argument( i ).c_str() ) ) + name_pending[i] = true; } // multi-threaded --list is faster even with 1 thread and 1 file in archive @@ -722,11 +729,7 @@ int decode( const std::string & archive_name, const Arg_parser & parser, archive_is_uncompressed_seekable = true; // unless compressed corrupt } - has_lz_ext = // global var for archive_read - ( archive_name.size() > 3 && - archive_name.compare( archive_name.size() - 3, 3, ".lz" ) == 0 ) || - ( archive_name.size() > 4 && - archive_name.compare( archive_name.size() - 4, 4, ".tlz" ) == 0 ); + archive_has_lz_ext = has_lz_ext( archive_name ); // var for archive_read Extended extended; // metadata from extended records int retval = 0; bool prev_extended = false; // prev header was extended @@ -737,35 +740,46 @@ int decode( const std::string & archive_name, const Arg_parser & parser, if( ret == 2 ) return 2; if( ret != 0 || !verify_ustar_chksum( header ) ) { - if( ret == 0 && block_is_zero( header, header_size ) ) break; // EOF + 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( skip_warn() && verbosity >= 2 ) std::fprintf( stderr, "ustar chksum = %07o\n", ustar_chksum( header ) ); set_error_status( 2 ); continue; } - skip_warn( true ); // reset warning + skip_warn( true ); // reset warning const Typeflag typeflag = (Typeflag)header[typeflag_o]; if( typeflag == tf_global ) { if( prev_extended ) - { show_error( "Format violation: global header after extended header." ); + { show_file_error( archive_name.c_str(), + "Format violation: extended header followed by global header." ); return 2; } Extended dummy; // global headers are parsed and ignored - if( !parse_records( infd, dummy, header, true ) ) - { show_error( "Error in global extended records. Skipping to next header." ); + if( !parse_records( infd, dummy, header, grbuf, true ) ) + { show_file_error( archive_name.c_str(), + "Error in global extended records. Skipping to next header." ); set_error_status( 2 ); } continue; } if( typeflag == tf_extended ) { if( prev_extended && !permissive ) - { show_error( "Format violation: consecutive extended headers found." + { 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, permissive ) ) - { show_error( "Error in extended records. Skipping to next header." ); + if( !parse_records( infd, extended, header, grbuf, permissive ) ) + { show_file_error( archive_name.c_str(), + "Error in extended records. Skipping to next header." ); extended.reset(); set_error_status( 2 ); } else if( !extended.crc_present() && missing_crc ) - { show_error( "Missing CRC in extended records.", 0, true ); return 2; } + { show_file_error( archive_name.c_str(), + "Missing CRC in extended records." ); return 2; } prev_extended = true; continue; } |