summaryrefslogtreecommitdiffstats
path: root/extract.cc
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2020-08-08 17:10:19 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2020-08-08 17:11:19 +0000
commite9232deb17df1ba9d36920e1d3444d34ad6ec18e (patch)
tree86f970c2e20f2d35845918f26f55bedffcd5f82c /extract.cc
parentReleasing debian version 0.16-4. (diff)
downloadtarlz-e9232deb17df1ba9d36920e1d3444d34ad6ec18e.tar.xz
tarlz-e9232deb17df1ba9d36920e1d3444d34ad6ec18e.zip
Merging upstream version 0.17.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'extract.cc')
-rw-r--r--extract.cc575
1 files changed, 242 insertions, 333 deletions
diff --git a/extract.cc b/extract.cc
index a1f019e..0b9b1d3 100644
--- a/extract.cc
+++ b/extract.cc
@@ -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 );
}