diff options
Diffstat (limited to 'extract.cc')
-rw-r--r-- | extract.cc | 575 |
1 files changed, 242 insertions, 333 deletions
@@ -1,18 +1,18 @@ -/* Tarlz - Archiver with multimember lzip compression - Copyright (C) 2013-2019 Antonio Diaz Diaz. +/* Tarlz - Archiver with multimember lzip compression + Copyright (C) 2013-2020 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 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. + 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/>. + 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 @@ -32,19 +32,21 @@ #include <utime.h> #include <sys/stat.h> #include <sys/types.h> +#if !defined __FreeBSD__ && !defined __OpenBSD__ && !defined __NetBSD__ && \ + !defined __DragonFly__ && !defined __APPLE__ #include <sys/sysmacros.h> // for major, minor, makedev +#endif #include <lzlib.h> #include "arg_parser.h" -#include "lzip_index.h" #include "tarlz.h" +#include "lzip_index.h" +#include "archive_reader.h" namespace { Resizable_buffer grbuf; -bool archive_is_uncompressed_seekable = false; -bool archive_has_lz_ext; // local var for archive_read bool skip_warn( const bool reset = false ) // avoid duplicate warnings { @@ -57,6 +59,13 @@ bool skip_warn( const bool reset = false ) // avoid duplicate warnings } +void read_error( const Archive_reader & ar ) + { + show_file_error( ar.ad.namep, ar.e_msg(), ar.e_code() ); + if( ar.e_skip() ) skip_warn(); + } + + bool make_path( const std::string & name ) { const mode_t mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; @@ -86,255 +95,47 @@ 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 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; - static bool fatal = false; - static bool first_call = true; - - if( sizep ) *sizep = 0; - if( fatal ) return 2; - if( first_call ) // check format - { - first_call = false; - if( size != header_size ) - internal_error( "size != header_size on first call." ); - const int rd = readblock( infd, buf, size ); - if( sizep ) *sizep = rd; - if( rd != size && errno ) - { 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() && - isvalid_ds( header.dictionary_size() ) ); - const bool istar = ( rd == size && verify_ustar_chksum( buf ) ); - const bool iseof = - ( !islz && !istar && rd == size && block_is_zero( buf, size ) ); - if( !islz && !istar && !iseof ) // corrupt or invalid format - { - show_file_error( archive_namep, posix_msg ); - if( archive_has_lz_ext && rd >= min_member_size ) islz = true; - if( !islz ) return 1; - } - if( !islz ) // uncompressed - { if( rd == size ) return 0; fatal = true; return 2; } - archive_is_uncompressed_seekable = false; // compressed - decoder = LZ_decompress_open(); - if( !decoder || LZ_decompress_errno( decoder ) != LZ_ok ) - { show_error( mem_msg ); - 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( 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_file_error( archive_namep, posix_lz_msg ); - fatal = true; return 2; - } - - if( !decoder ) // uncompressed - { - const int rd = readblock( infd, buf, size ); if( rd == size ) return 0; - if( sizep ) *sizep = rd; - show_file_error( archive_namep, end_msg ); fatal = true; return 2; - } - const int ibuf_size = 16384; - uint8_t ibuf[ibuf_size]; - int sz = 0; - while( sz < size ) - { - const int rd = LZ_decompress_read( decoder, buf + sz, size - sz ); - if( rd < 0 ) - { - if( LZ_decompress_sync_to_member( decoder ) < 0 ) - internal_error( "library error (LZ_decompress_sync_to_member)." ); - skip_warn(); set_error_status( 2 ); return 1; - } - if( rd == 0 && LZ_decompress_finished( decoder ) == 1 ) - { LZ_decompress_close( decoder ); - show_file_error( archive_namep, end_msg ); fatal = true; return 2; } - sz += rd; if( sizep ) *sizep = sz; - if( sz < size && !at_eof && LZ_decompress_write_size( decoder ) > 0 ) - { - const int rsize = std::min( ibuf_size, LZ_decompress_write_size( decoder ) ); - const int rd = readblock( infd, ibuf, rsize ); - if( LZ_decompress_write( decoder, ibuf, rd ) != rd ) - internal_error( "library error (LZ_decompress_write)." ); - if( rd < rsize ) - { - at_eof = true; LZ_decompress_finish( decoder ); - if( errno ) - { show_file_error( archive_namep, "Error reading archive", errno ); - fatal = true; return 2; } - } - } - } - return 0; - } - - -int skip_member( const char * const archive_namep, const int infd, - const Extended & extended ) +int skip_member( Archive_reader & ar, const Extended & extended ) { - long long rest = extended.file_size(); - const int rem = rest % 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; - const int ret = archive_read( archive_namep, infd, buf, rsize ); - if( ret != 0 ) { if( ret == 2 ) return 2; else break; } - rest -= rsize; - } + const int ret = ar.skip_member( extended ); + if( ret != 0 ) { read_error( ar ); if( ret == 2 ) return 2; } return 0; } -void show_file_diff( const char * const filename, const char * const msg ) - { - if( verbosity >= 0 ) - { std::printf( "%s: %s\n", filename, msg ); std::fflush( stdout ); } - } - - -int compare_member( const char * const archive_namep, const int infd1, - const Extended & extended, const Tar_header header, - const bool ignore_ids ) +int compare_member( const Cl_options & cl_opts, Archive_reader & ar, + const Extended & extended, const Tar_header header ) { if( !show_member_name( extended, header, 1, grbuf ) ) return 1; - long long rest = extended.file_size(); - const char * const filename = extended.path().c_str(); + std::string estr, ostr; + const bool stat_differs = + !compare_file_type( estr, ostr, cl_opts, extended, header ); + if( estr.size() ) std::fputs( estr.c_str(), stderr ); + if( ostr.size() ) { std::fputs( ostr.c_str(), stdout ); std::fflush( stdout ); } + if( extended.file_size() <= 0 ) return 0; const Typeflag typeflag = (Typeflag)header[typeflag_o]; - bool diff = false, size_differs = false, type_differs = true; - struct stat st; - 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 ) ) - show_file_diff( filename, "Is not a regular file" ); - else if( typeflag == tf_symlink && !S_ISLNK( st.st_mode ) ) - show_file_diff( filename, "Is not a symlink" ); - else if( typeflag == tf_chardev && !S_ISCHR( st.st_mode ) ) - show_file_diff( filename, "Is not a character device" ); - else if( typeflag == tf_blockdev && !S_ISBLK( st.st_mode ) ) - show_file_diff( filename, "Is not a block device" ); - else if( typeflag == tf_directory && !S_ISDIR( st.st_mode ) ) - show_file_diff( filename, "Is not a directory" ); - else if( typeflag == tf_fifo && !S_ISFIFO( st.st_mode ) ) - show_file_diff( filename, "Is not a FIFO" ); - else - { - type_differs = false; - if( typeflag != tf_symlink ) - { - const mode_t mode = parse_octal( header + mode_o, mode_l ); // 12 bits - if( mode != ( st.st_mode & ( S_ISUID | S_ISGID | S_ISVTX | - S_IRWXU | S_IRWXG | S_IRWXO ) ) ) - { show_file_diff( filename, "Mode differs" ); diff = true; } - } - if( !ignore_ids ) - { - if( (uid_t)parse_octal( header + uid_o, uid_l ) != st.st_uid ) - { show_file_diff( filename, "Uid differs" ); diff = true; } - if( (gid_t)parse_octal( header + gid_o, gid_l ) != st.st_gid ) - { show_file_diff( filename, "Gid differs" ); diff = true; } - } - if( typeflag != tf_symlink ) - { - if( typeflag != tf_directory ) - { - const time_t mtime = parse_octal( header + mtime_o, mtime_l ); // 33 bits - if( mtime != st.st_mtime ) - { show_file_diff( filename, "Mod time differs" ); diff = true; } - } - if( ( typeflag == tf_regular || typeflag == tf_hiperf ) && - 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 ) != - (unsigned)major( st.st_rdev ) || - parse_octal( header + devminor_o, devminor_l ) != - (unsigned)minor( st.st_rdev ) ) ) - { show_file_diff( filename, "Device number differs" ); diff = true; } - } - else - { - char * const buf = new char[st.st_size+1]; - long len = readlink( filename, buf, st.st_size ); - bool e = ( len != st.st_size ); - if( !e ) - { - while( len > 1 && buf[len-1] == '/' ) --len; // trailing '/' - buf[len] = 0; - if( extended.linkpath() != buf ) e = true; - } - delete[] buf; - if( e ) { show_file_diff( filename, "Symlink differs" ); diff = true; } - } - } - if( diff || size_differs || type_differs ) - { diff = false; set_error_status( 1 ); } - if( rest <= 0 ) return 0; - if( ( typeflag != tf_regular && typeflag != tf_hiperf ) || - size_differs || type_differs ) - return skip_member( archive_namep, infd1, extended ); + if( ( typeflag != tf_regular && typeflag != tf_hiperf ) || stat_differs ) + return skip_member( ar, extended ); // else compare file contents - const int rem = rest % header_size; - const int padding = rem ? header_size - rem : 0; - const int bufsize = 32 * header_size; - uint8_t buf1[bufsize]; - uint8_t buf2[bufsize]; + const char * const filename = extended.path().c_str(); const int infd2 = open_instream( filename ); if( infd2 < 0 ) - { 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( archive_namep, infd1, buf1, rsize1 ); - if( ret != 0 ) { if( ret == 2 ) retval = 2; diff = true; break; } - if( !diff ) - { - const int rd = readblock( infd2, buf2, rsize2 ); - if( rd != rsize2 ) - { - if( errno ) show_file_error( filename, "Read error", errno ); - else show_file_diff( filename, "EOF found in file" ); - diff = true; - } - else - { - int i = 0; while( i < rsize2 && buf1[i] == buf2[i] ) ++i; - if( i < rsize2 ) - { show_file_diff( filename, "Contents differ" ); diff = true; } - } - } - if( rest < bufsize ) break; - rest -= rsize1; - } - if( diff ) set_error_status( 1 ); - close( infd2 ); + { set_error_status( 1 ); return skip_member( ar, extended ); } + int retval = compare_file_contents( estr, ostr, ar, extended.file_size(), + filename, infd2 ); + if( retval ) { read_error( ar ); if( retval != 2 ) retval = 0; } + else { if( estr.size() ) std::fputs( estr.c_str(), stderr ); + if( ostr.size() ) + { std::fputs( ostr.c_str(), stdout ); std::fflush( stdout ); } } return retval; } -int list_member( const char * const archive_namep, const int infd, +int list_member( Archive_reader & ar, const Extended & extended, const Tar_header header ) { if( !show_member_name( extended, header, 0, grbuf ) ) return 1; - return skip_member( archive_namep, infd, extended ); + return skip_member( ar, extended ); } @@ -346,17 +147,27 @@ bool contains_dotdot( const char * const filename ) } -int extract_member( const char * const archive_namep, const int infd, - const Extended & extended, const Tar_header header, - const bool keep_damaged ) +mode_t get_umask() + { + static mode_t mask = 0; // read once, cache the result + static bool first_call = true; + if( first_call ) { first_call = false; mask = umask( 0 ); umask( mask ); + mask &= S_IRWXU | S_IRWXG | S_IRWXO; } + return mask; + } + + +int extract_member( const Cl_options & cl_opts, Archive_reader & ar, + const Extended & extended, const Tar_header header ) { const char * const filename = extended.path().c_str(); if( contains_dotdot( filename ) ) { show_file_error( filename, "Contains a '..' component, skipping." ); - return skip_member( archive_namep, infd, extended ); + return skip_member( ar, extended ); } - const mode_t mode = parse_octal( header + mode_o, mode_l ); // 12 bits + mode_t mode = parse_octal( header + mode_o, mode_l ); // 12 bits + if( geteuid() != 0 && !cl_opts.preserve_permissions ) mode &= ~get_umask(); const time_t mtime = parse_octal( header + mtime_o, mtime_l ); // 33 bits const Typeflag typeflag = (Typeflag)header[typeflag_o]; const bool islink = ( typeflag == tf_link || typeflag == tf_symlink ); @@ -381,7 +192,7 @@ int extract_member( const char * const archive_namep, const int infd, ( !hard && symlink( linkname, filename ) != 0 ) ) { if( verbosity >= 0 ) - std::fprintf( stderr, "Can't %slink file '%s' to '%s': %s.\n", + std::fprintf( stderr, "Can't %slink '%s' to '%s': %s.\n", hard ? "" : "sym", linkname, filename, std::strerror( errno ) ); return 2; @@ -442,14 +253,14 @@ int extract_member( const char * const archive_namep, const int infd, while( rest > 0 ) { const int rsize = ( rest >= bufsize ) ? bufsize : rest + padding; - int rd; - const int ret = archive_read( archive_namep, infd, buf, rsize, &rd ); + const int ret = ar.read( buf, rsize ); if( ret != 0 ) { + read_error( ar ); if( outfd >= 0 ) { - if( keep_damaged ) - { writeblock( outfd, buf, std::min( rest, (long long)rd ) ); + if( cl_opts.keep_damaged ) + { writeblock( outfd, buf, std::min( rest, (long long)ar.e_size() ) ); close( outfd ); } else { close( outfd ); std::remove( filename ); } } @@ -473,85 +284,188 @@ int extract_member( const char * const archive_namep, const int infd, } -bool parse_records( const char * const archive_namep, const int infd, - Extended & extended, const Tar_header header, - Resizable_buffer & rbuf, const bool permissive ) +void format_file_diff( std::string & ostr, const char * const filename, + const char * const msg ) { - 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( archive_namep, infd, (uint8_t *)rbuf(), bufsize ) == 0 && - extended.parse( rbuf(), edsize, permissive ) ); + if( verbosity < 0 ) return; + { ostr += filename; ostr += ": "; ostr += msg; ostr += '\n'; } } } // end namespace -int decode( const std::string & archive_name, const Arg_parser & parser, - const int filenames, const int num_workers, const int debug_level, - const Program_mode program_mode, const bool ignore_ids, - const bool keep_damaged, const bool missing_crc, - const bool permissive ) +bool compare_file_type( std::string & estr, std::string & ostr, + const Cl_options & cl_opts, + const Extended & extended, const Tar_header header ) + { + const char * const filename = extended.path().c_str(); + const Typeflag typeflag = (Typeflag)header[typeflag_o]; + struct stat st; + bool diff = false, size_differs = false, type_differs = true; + if( hstat( filename, &st, cl_opts.dereference ) != 0 ) + format_file_error( estr, filename, "Warning: Can't stat", errno ); + else if( ( typeflag == tf_regular || typeflag == tf_hiperf ) && + !S_ISREG( st.st_mode ) ) + format_file_diff( ostr, filename, "Is not a regular file" ); + else if( typeflag == tf_symlink && !S_ISLNK( st.st_mode ) ) + format_file_diff( ostr, filename, "Is not a symlink" ); + else if( typeflag == tf_chardev && !S_ISCHR( st.st_mode ) ) + format_file_diff( ostr, filename, "Is not a character device" ); + else if( typeflag == tf_blockdev && !S_ISBLK( st.st_mode ) ) + format_file_diff( ostr, filename, "Is not a block device" ); + else if( typeflag == tf_directory && !S_ISDIR( st.st_mode ) ) + format_file_diff( ostr, filename, "Is not a directory" ); + else if( typeflag == tf_fifo && !S_ISFIFO( st.st_mode ) ) + format_file_diff( ostr, filename, "Is not a FIFO" ); + else + { + type_differs = false; + if( typeflag != tf_symlink ) + { + const mode_t mode = parse_octal( header + mode_o, mode_l ); // 12 bits + if( mode != ( st.st_mode & ( S_ISUID | S_ISGID | S_ISVTX | + S_IRWXU | S_IRWXG | S_IRWXO ) ) ) + { format_file_diff( ostr, filename, "Mode differs" ); diff = true; } + } + if( !cl_opts.ignore_ids ) + { + if( (uid_t)parse_octal( header + uid_o, uid_l ) != st.st_uid ) + { format_file_diff( ostr, filename, "Uid differs" ); diff = true; } + if( (gid_t)parse_octal( header + gid_o, gid_l ) != st.st_gid ) + { format_file_diff( ostr, filename, "Gid differs" ); diff = true; } + } + if( typeflag != tf_symlink ) + { + if( typeflag != tf_directory ) + { + const time_t mtime = parse_octal( header + mtime_o, mtime_l ); // 33 bits + if( mtime != st.st_mtime ) + { format_file_diff( ostr, filename, "Mod time differs" ); diff = true; } + } + if( ( typeflag == tf_regular || typeflag == tf_hiperf ) && + extended.file_size() != st.st_size ) // don't compare contents + { format_file_diff( ostr, filename, "Size differs" ); size_differs = true; } + if( ( typeflag == tf_chardev || typeflag == tf_blockdev ) && + ( parse_octal( header + devmajor_o, devmajor_l ) != + (unsigned)major( st.st_rdev ) || + parse_octal( header + devminor_o, devminor_l ) != + (unsigned)minor( st.st_rdev ) ) ) + { format_file_diff( ostr, filename, "Device number differs" ); diff = true; } + } + else + { + char * const buf = new char[st.st_size+1]; + long len = readlink( filename, buf, st.st_size ); + bool e = ( len != st.st_size ); + if( !e ) + { + while( len > 1 && buf[len-1] == '/' ) --len; // trailing '/' + buf[len] = 0; + if( extended.linkpath() != buf ) e = true; + } + delete[] buf; + if( e ) { format_file_diff( ostr, filename, "Symlink differs" ); diff = true; } + } + } + if( diff || size_differs || type_differs ) set_error_status( 1 ); + return !( size_differs || type_differs ); + } + + +bool compare_file_contents( std::string & estr, std::string & ostr, + Archive_reader_base & ar, const long long file_size, + const char * const filename, const int infd2 ) + { + long long rest = file_size; + const int rem = rest % header_size; + const int padding = rem ? header_size - rem : 0; + const int bufsize = 32 * header_size; + uint8_t buf1[bufsize]; + uint8_t buf2[bufsize]; + int retval = 0; + bool diff = false; + estr.clear(); ostr.clear(); + while( rest > 0 ) + { + const int rsize1 = ( rest >= bufsize ) ? bufsize : rest + padding; + const int rsize2 = ( rest >= bufsize ) ? bufsize : rest; + if( ( retval = ar.read( buf1, rsize1 ) ) != 0 ) { diff = true; break; } + if( !diff ) + { + const int rd = readblock( infd2, buf2, rsize2 ); + if( rd != rsize2 ) + { + if( errno ) format_file_error( estr, filename, "Read error", errno ); + else format_file_diff( ostr, filename, "EOF found in file" ); + diff = true; + } + else + { + int i = 0; while( i < rsize2 && buf1[i] == buf2[i] ) ++i; + if( i < rsize2 ) + { format_file_diff( ostr, filename, "Contents differ" ); diff = true; } + } + } + if( rest < bufsize ) break; + rest -= rsize1; + } + close( infd2 ); + if( diff ) set_error_status( 1 ); + return retval; + } + + +int decode( const Cl_options & cl_opts ) { - 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; + // open archive before changing working directory + const Archive_descriptor ad( cl_opts.archive_name ); + if( ad.infd < 0 ) return 1; // Execute -C options and mark filenames to be compared, extracted or listed. // name_pending is of type char instead of bool to allow concurrent update. - std::vector< char > name_pending( parser.arguments(), false ); - for( int i = 0; i < parser.arguments(); ++i ) + std::vector< char > name_pending( cl_opts.parser.arguments(), false ); + for( int i = 0; i < cl_opts.parser.arguments(); ++i ) { - const int code = parser.code( i ); - if( code == 'C' && program_mode != m_list ) + const int code = cl_opts.parser.code( i ); + if( code == 'C' && cl_opts.program_mode != m_list ) { - const char * const dir = parser.argument( i ).c_str(); + const char * const dir = cl_opts.parser.argument( i ).c_str(); if( chdir( dir ) != 0 ) { show_file_error( dir, "Error changing working directory", errno ); return 1; } } - if( !code && parser.argument( i ).size() && - !Exclude::excluded( parser.argument( i ).c_str() ) ) + if( !code && cl_opts.parser.argument( i ).size() && + !Exclude::excluded( cl_opts.parser.argument( i ).c_str() ) ) name_pending[i] = true; } // multi-threaded --list is faster even with 1 thread and 1 file in archive - if( program_mode == m_list && num_workers > 0 ) + // (but multi-threaded --diff probably needs at least 2 of each) + if( ( cl_opts.program_mode == m_diff || cl_opts.program_mode == m_list ) && + cl_opts.num_workers > 0 ) { - const Lzip_index lzip_index( infd, true, false ); // only regular files - const long members = lzip_index.members(); - if( lzip_index.retval() == 0 && members >= 2 ) // one file + eof + if( ad.indexed && ad.lzip_index.members() >= 2 ) // one file + eof { - // 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 ); + // show_file_error( ad.namep, "Is compressed seekable" ); + return decode_lz( cl_opts, ad, name_pending ); } - if( lseek( infd, 0, SEEK_SET ) == 0 && lzip_index.retval() != 0 && - lzip_index.file_size() > 3 * header_size ) - archive_is_uncompressed_seekable = true; // unless compressed corrupt } - archive_has_lz_ext = has_lz_ext( archive_name ); // var for archive_read + Archive_reader ar( ad ); Extended extended; // metadata from extended records int retval = 0; bool prev_extended = false; // prev header was extended while( true ) // process one tar header per iteration { Tar_header header; - const int ret = archive_read( archive_namep, infd, header, header_size ); - if( ret == 2 ) { retval = 2; break; } + const int ret = ar.read( header, header_size ); + if( ret != 0 ) { read_error( ar ); 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 || permissive ) break; // EOF - show_file_error( archive_namep, fv_msg1 ); + if( !prev_extended || cl_opts.permissive ) break; // EOF + show_file_error( ad.namep, fv_msg1 ); retval = 2; break; } if( skip_warn() && verbosity >= 2 ) @@ -563,26 +477,24 @@ 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 && !permissive ) - { show_file_error( archive_namep, fv_msg2 ); retval = 2; break; } + if( prev_extended && !cl_opts.permissive ) + { show_file_error( ad.namep, fv_msg2 ); retval = 2; break; } Extended dummy; // global headers are parsed and ignored - 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 ); } + const int ret = ar.parse_records( dummy, header, grbuf, true ); + if( ret != 0 ) { show_file_error( ad.namep, gblrec_msg ); skip_warn(); + set_error_status( ret ); } continue; } if( typeflag == tf_extended ) { - if( prev_extended && !permissive ) - { 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_namep, mcrc_msg ); retval = 2; break; } + if( prev_extended && !cl_opts.permissive ) + { show_file_error( ad.namep, fv_msg3 ); retval = 2; break; } + const int ret = ar.parse_records( extended, header, grbuf, + cl_opts.permissive ); + if( ret != 0 ) { show_file_error( ad.namep, extrec_msg ); skip_warn(); + extended.reset(); set_error_status( ret ); } + else if( !extended.crc_present() && cl_opts.missing_crc ) + { show_file_error( ad.namep, mcrc_msg ); retval = 2; break; } prev_extended = true; continue; } @@ -590,29 +502,26 @@ int decode( const std::string & archive_name, const Arg_parser & parser, extended.fill_from_ustar( header ); // copy metadata from header - 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( archive_namep, infd, extended, header ); - else if( program_mode == m_diff ) - retval = compare_member( archive_namep, infd, extended, header, ignore_ids ); - else retval = extract_member( archive_namep, infd, extended, header, - keep_damaged ); + if( check_skip_filename( cl_opts, name_pending, extended.path().c_str() ) ) + retval = skip_member( ar, extended ); + else if( cl_opts.program_mode == m_list ) + retval = list_member( ar, extended, header ); + else if( cl_opts.program_mode == m_diff ) + retval = compare_member( cl_opts, ar, extended, header ); + else retval = extract_member( cl_opts, ar, extended, header ); extended.reset(); if( retval ) { show_error( "Error is not recoverable: exiting now." ); break; } } - if( close( infd ) != 0 && !retval ) - { show_file_error( archive_namep, "Error closing archive", errno ); + if( close( ad.infd ) != 0 && !retval ) + { show_file_error( ad.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." ); - retval = 1; - } - return final_exit_status( retval, program_mode != m_diff ); + if( retval == 0 ) + for( int i = 0; i < cl_opts.parser.arguments(); ++i ) + if( nonempty_arg( cl_opts.parser, i ) && name_pending[i] ) + { show_file_error( cl_opts.parser.argument( i ).c_str(), + "Not found in archive." ); retval = 1; } + return final_exit_status( retval, cl_opts.program_mode != m_diff ); } |