summaryrefslogtreecommitdiffstats
path: root/decode.cc
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-01-23 05:08:19 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-01-23 05:08:19 +0000
commitcb1387c92038634c063ee06a24e249b87525f519 (patch)
treeaeebf76566be407c42678fff1c2482ee9dc8fe17 /decode.cc
parentReleasing debian version 0.23-3. (diff)
downloadtarlz-cb1387c92038634c063ee06a24e249b87525f519.tar.xz
tarlz-cb1387c92038634c063ee06a24e249b87525f519.zip
Merging upstream version 0.25.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'decode.cc')
-rw-r--r--decode.cc119
1 files changed, 80 insertions, 39 deletions
diff --git a/decode.cc b/decode.cc
index a45a1fd..bcac4c8 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-2024 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>
@@ -29,6 +29,8 @@
#if !defined __FreeBSD__ && !defined __OpenBSD__ && !defined __NetBSD__ && \
!defined __DragonFly__ && !defined __APPLE__ && !defined __OS2__
#include <sys/sysmacros.h> // for major, minor, makedev
+#else
+#include <sys/types.h> // for major, minor, makedev
#endif
#include <lzlib.h>
@@ -38,6 +40,9 @@
#include "archive_reader.h"
#include "decode.h"
+#ifndef O_DIRECTORY
+#define O_DIRECTORY 0
+#endif
namespace {
@@ -124,7 +129,7 @@ int extract_member( const Cl_options & cl_opts, Archive_reader & ar,
if( !show_member_name( extended, header, 1, grbuf ) ) return 1;
// remove file (or empty dir) before extraction to prevent following links
std::remove( filename );
- if( !make_path( filename ) )
+ if( !make_dirs( filename ) )
{
show_file_error( filename, intdir_msg, errno );
set_error_status( 1 );
@@ -192,7 +197,7 @@ int extract_member( const Cl_options & cl_opts, Archive_reader & ar,
chown( filename, extended.get_uid(), extended.get_gid() ) != 0 ) )
{
if( outfd >= 0 ) mode &= ~( S_ISUID | S_ISGID | S_ISVTX );
- // chown will in many cases return with EPERM, which can be safely ignored.
+ // chown in many cases returns with EPERM, which can be safely ignored.
if( errno != EPERM && errno != EINVAL )
{ show_file_error( filename, chown_msg, errno ); set_error_status( 1 ); }
}
@@ -246,9 +251,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 )
@@ -258,7 +291,7 @@ bool compare_file_type( std::string & estr, std::string & ostr,
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 );
+ 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" );
@@ -275,14 +308,14 @@ bool compare_file_type( std::string & estr, std::string & ostr,
else
{
type_differs = false;
- if( typeflag != tf_symlink )
+ if( typeflag != tf_symlink && !cl_opts.ignore_metadata )
{
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( !cl_opts.ignore_ids && !cl_opts.ignore_metadata )
{
if( extended.get_uid() != (long long)st.st_uid )
{ format_file_diff( ostr, filename, "Uid differs" ); diff = true; }
@@ -291,7 +324,7 @@ bool compare_file_type( std::string & estr, std::string & ostr,
}
if( typeflag != tf_symlink )
{
- if( typeflag != tf_directory &&
+ if( typeflag != tf_directory && !cl_opts.ignore_metadata &&
extended.mtime().sec() != (long long)st.st_mtime )
{
if( (time_t)extended.mtime().sec() == st.st_mtime )
@@ -380,32 +413,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 +454,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 +499,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; }