From cb1387c92038634c063ee06a24e249b87525f519 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Tue, 23 Jan 2024 06:08:19 +0100 Subject: Merging upstream version 0.25. Signed-off-by: Daniel Baumann --- decode.cc | 119 ++++++++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 80 insertions(+), 39 deletions(-) (limited to 'decode.cc') 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 #include #include -#include +#include #include // for lzlib.h #include #include @@ -29,6 +29,8 @@ #if !defined __FreeBSD__ && !defined __OpenBSD__ && !defined __NetBSD__ && \ !defined __DragonFly__ && !defined __APPLE__ && !defined __OS2__ #include // for major, minor, makedev +#else +#include // for major, minor, makedev #endif #include @@ -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; } -- cgit v1.2.3