summaryrefslogtreecommitdiffstats
path: root/decode.cc
diff options
context:
space:
mode:
Diffstat (limited to 'decode.cc')
-rw-r--r--decode.cc102
1 files changed, 69 insertions, 33 deletions
diff --git a/decode.cc b/decode.cc
index a45a1fd..1742df2 100644
--- a/decode.cc
+++ b/decode.cc
@@ -1,5 +1,5 @@
/* Tarlz - Archiver with multimember lzip compression
- Copyright (C) 2013-2022 Antonio Diaz Diaz.
+ Copyright (C) 2013-2023 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
@@ -21,7 +21,7 @@
#include <cctype>
#include <cerrno>
#include <cstdio>
-#include <cstdlib>
+#include <fcntl.h>
#include <stdint.h> // for lzlib.h
#include <unistd.h>
#include <utime.h>
@@ -246,9 +246,37 @@ void format_file_diff( std::string & ostr, const char * const filename,
{ if( verbosity >= 0 )
{ ostr += filename; ostr += ": "; ostr += msg; ostr += '\n'; } }
+
+bool option_C_present( const Arg_parser & parser )
+ {
+ for( int i = 0; i < parser.arguments(); ++i )
+ if( parser.code( i ) == 'C' ) return true;
+ return false;
+ }
+
+
+bool option_C_after_filename( const Arg_parser & parser )
+ {
+ for( int i = 0; i < parser.arguments(); ++i )
+ if( nonempty_arg( parser, i ) )
+ while( ++i < parser.arguments() )
+ if( parser.code( i ) == 'C' ) return true;
+ return false;
+ }
+
} // end namespace
+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;
+ }
+
+
bool compare_file_type( std::string & estr, std::string & ostr,
const Cl_options & cl_opts,
const Extended & extended, const Tar_header header )
@@ -380,32 +408,37 @@ int decode( const Cl_options & cl_opts )
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( cl_opts.parser.arguments(), false );
- for( int i = 0; i < cl_opts.parser.arguments(); ++i )
- {
- const int code = cl_opts.parser.code( i );
- if( code == 'C' && cl_opts.program_mode != m_list )
+ const bool c_present = option_C_present( cl_opts.parser ) &&
+ cl_opts.program_mode != m_list;
+ const bool c_after_name = c_present &&
+ option_C_after_filename( cl_opts.parser );
+ // save current working directory for sequential decoding
+ const int chdir_fd = c_after_name ? open( ".", O_RDONLY | O_DIRECTORY ) : -1;
+ if( c_after_name && chdir_fd < 0 )
+ { show_error( "Can't save current working directory", errno ); return 1; }
+ if( c_present && !c_after_name ) // execute all -C options
+ for( int i = 0; i < cl_opts.parser.arguments(); ++i )
{
+ if( cl_opts.parser.code( i ) != 'C' ) continue;
const char * const dir = cl_opts.parser.argument( i ).c_str();
if( chdir( dir ) != 0 )
{ show_file_error( dir, chdir_msg, errno ); return 1; }
}
- if( !code && cl_opts.parser.argument( i ).size() &&
+ /* 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( cl_opts.parser.arguments(), false );
+ for( int i = 0; i < cl_opts.parser.arguments(); ++i )
+ if( nonempty_arg( cl_opts.parser, i ) && // skip opts, empty names
!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
- // but multi-threaded --diff and --extract probably need at least 2 of each
- if( ( cl_opts.program_mode == m_diff || cl_opts.program_mode == m_list ||
- cl_opts.program_mode == m_extract ) && cl_opts.num_workers > 0 &&
- ad.indexed && ad.lzip_index.members() >= 2 ) // one file + EOA
- {
- // show_file_error( ad.namep, "Is compressed seekable" );
+ /* multi-threaded --list is faster even with 1 thread and 1 file in archive
+ but multi-threaded --diff and --extract probably need at least 2 of each.
+ CWD is not per-thread; multi-threaded decode can't be used if a
+ -C option appears after a file name in the command line. */
+ if( cl_opts.num_workers > 0 && !c_after_name && ad.indexed &&
+ ad.lzip_index.members() >= 2 ) // 2 lzip members may be 1 file + EOA
return decode_lz( cl_opts, ad, name_pending );
- }
Archive_reader ar( ad ); // serial reader
Extended extended; // metadata from extended records
@@ -416,7 +449,7 @@ int decode( const Cl_options & cl_opts )
Tar_header header;
const int ret = ar.read( header, header_size );
if( ret != 0 ) { read_error( ar ); if( ar.fatal() ) { retval = ret; break; } }
- if( ret != 0 || !verify_ustar_chksum( header ) ) // error or EOA
+ if( ret != 0 || !check_ustar_chksum( header ) ) // error or EOA
{
if( ret == 0 && block_is_zero( header, header_size ) ) // EOA
{
@@ -461,20 +494,23 @@ int decode( const Cl_options & cl_opts )
extended.fill_from_ustar( header ); // copy metadata from header
- // members without name are skipped except when listing
- if( check_skip_filename( cl_opts, name_pending, extended.path().c_str() ) )
- retval = skip_member( ar, extended, typeflag );
- else
- {
- print_removed_prefix( extended.removed_prefix );
- if( cl_opts.program_mode == m_list )
- retval = list_member( ar, extended, header );
- else if( extended.path().empty() )
- retval = skip_member( ar, extended, typeflag );
- 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 );
+ try {
+ // members without name are skipped except when listing
+ if( check_skip_filename( cl_opts, name_pending, extended.path().c_str(),
+ chdir_fd ) ) retval = skip_member( ar, extended, typeflag );
+ else
+ {
+ print_removed_prefix( extended.removed_prefix );
+ if( cl_opts.program_mode == m_list )
+ retval = list_member( ar, extended, header );
+ else if( extended.path().empty() )
+ retval = skip_member( ar, extended, typeflag );
+ 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 );
+ }
}
+ catch( Chdir_error & ) { retval = 1; }
extended.reset();
if( retval )
{ show_error( "Error is not recoverable: exiting now." ); break; }