diff options
Diffstat (limited to 'compress.cc')
-rw-r--r-- | compress.cc | 69 |
1 files changed, 43 insertions, 26 deletions
diff --git a/compress.cc b/compress.cc index 4e74efa..3091889 100644 --- a/compress.cc +++ b/compress.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 @@ -20,7 +20,6 @@ #include <cerrno> #include <csignal> #include <cstdio> -#include <cstdlib> #include <stdint.h> // for lzlib.h #include <unistd.h> #include <utime.h> @@ -54,12 +53,11 @@ void cleanup_and_fail( const int retval ) if( delete_output_on_interrupt ) { delete_output_on_interrupt = false; - if( verbosity >= 0 ) - std::fprintf( stderr, "%s: Deleting output file '%s', if it exists.\n", - program_name, output_filename.c_str() ); + show_file_error( output_filename.c_str(), + "Deleting output file, if it exists." ); if( outfd >= 0 ) { close( outfd ); outfd = -1; } if( std::remove( output_filename.c_str() ) != 0 && errno != ENOENT ) - show_error( "WARNING: deletion of output file (apparently) failed." ); + show_error( "warning: deletion of output file failed", errno ); } std::exit( retval ); } @@ -104,7 +102,7 @@ void close_and_set_permissions( const struct stat * const in_statsp ) if( in_statsp ) { const mode_t mode = in_statsp->st_mode; - // fchown will in many cases return with EPERM, which can be safely ignored. + // fchown in many cases returns with EPERM, which can be safely ignored. if( fchown( outfd, in_statsp->st_uid, in_statsp->st_gid ) == 0 ) { if( fchmod( outfd, mode ) != 0 ) warning = true; } else @@ -113,10 +111,8 @@ void close_and_set_permissions( const struct stat * const in_statsp ) warning = true; } if( close( outfd ) != 0 ) - { - show_error( "Error closing output file", errno ); - cleanup_and_fail( 1 ); - } + { show_file_error( output_filename.c_str(), "Error closing output file", + errno ); cleanup_and_fail( 1 ); } outfd = -1; delete_output_on_interrupt = false; if( in_statsp ) @@ -127,11 +123,12 @@ void close_and_set_permissions( const struct stat * const in_statsp ) if( utime( output_filename.c_str(), &t ) != 0 ) warning = true; } if( warning && verbosity >= 1 ) - show_error( "Can't change output file attributes." ); + show_file_error( output_filename.c_str(), + "warning: can't change output file attributes", errno ); } -bool archive_write( const uint8_t * const buf, const long long size, +bool archive_write( const uint8_t * const buf, const int size, LZ_Encoder * const encoder ) { static bool flushed = true; // avoid flushing empty lzip members @@ -140,13 +137,12 @@ bool archive_write( const uint8_t * const buf, const long long size, flushed = ( size <= 0 ); enum { obuf_size = 65536 }; uint8_t obuf[obuf_size]; - long long sz = 0; + int sz = 0; if( flushed ) LZ_compress_finish( encoder ); // flush encoder while( sz < size || flushed ) { if( sz < size ) - { const int wr = LZ_compress_write( encoder, buf + sz, - std::min( size - sz, (long long)max_dictionary_size ) ); + { const int wr = LZ_compress_write( encoder, buf + sz, size - sz ); if( wr < 0 ) internal_error( "library error (LZ_compress_write)." ); sz += wr; } if( sz >= size && !flushed ) break; // minimize dictionary size @@ -216,26 +212,37 @@ int compress_archive( const Cl_options & cl_opts, Resizable_buffer rbuf; // headers and extended records buffer if( !rbuf.size() ) { show_error( mem_msg ); return 1; } const char * const rderr_msg = "Read error"; + bool first_header = true; while( true ) // process one tar member per iteration { - int total_header_size = header_size; // size of header(s) read + int total_header_size = header_size; // e_header + edata + u_header const int rd = readblock( infd, rbuf.u8(), header_size ); - if( rd == 0 && errno == 0 ) break; // missing EOA blocks + if( rd == 0 && errno == 0 ) // missing EOA blocks + { if( !first_header ) break; + show_file_error( filename, "Archive is empty." ); + close( infd ); return 2; } if( rd != header_size ) { show_file_error( filename, rderr_msg, errno ); close( infd ); return 1; } + first_header = false; - if( to_file && outfd < 0 ) // open outfd after verifying infd + const bool is_header = check_ustar_chksum( rbuf.u8() ); + const bool is_zero = !is_header && block_is_zero( rbuf.u8(), header_size ); + if( to_file && outfd < 0 && ( is_header || is_zero ) ) { + // open outfd after checking infd + if( !make_dirs( output_filename ) ) + { show_file_error( output_filename.c_str(), intdir_msg, errno ); + return 1; } outfd = open_outstream( output_filename, true, 0, false ); // check tty only once and don't try to delete a tty if( outfd < 0 || !check_tty_out() ) { close( infd ); return 1; } delete_output_on_interrupt = true; } - if( !verify_ustar_chksum( rbuf.u8() ) ) // maybe EOA block + if( !is_header ) // maybe EOA block { - if( block_is_zero( rbuf.u8(), header_size ) ) // first EOA block + if( is_zero ) // first EOA block { tail_compress( cl_opts, infd, rbuf.u8(), encoder ); break; } show_file_error( filename, bad_hdr_msg ); close( infd ); return 2; } @@ -246,7 +253,7 @@ int compress_archive( const Cl_options & cl_opts, const long long edsize = parse_octal( rbuf.u8() + size_o, size_l ); const long long bufsize = round_up( edsize ); // overflow or no extended data - if( edsize <= 0 || edsize >= 1LL << 33 || bufsize >= INT_MAX ) + if( edsize <= 0 || edsize >= 1LL << 33 || bufsize > max_edata_size ) { show_file_error( filename, bad_hdr_msg ); close( infd ); return 2; } if( !rbuf.resize( total_header_size + bufsize ) ) { show_file_error( filename, mem_msg ); close( infd ); return 1; } @@ -263,7 +270,7 @@ int compress_archive( const Cl_options & cl_opts, if( readblock( infd, rbuf.u8() + total_header_size, header_size ) != header_size ) { show_file_error( filename, errno ? rderr_msg : end_msg, errno ); close( infd ); return errno ? 1 : 2; } - if( !verify_ustar_chksum( rbuf.u8() ) ) + if( !check_ustar_chksum( rbuf.u8() ) ) { show_file_error( filename, bad_hdr_msg ); close( infd ); return 2; } const Typeflag typeflag2 = (Typeflag)(rbuf() + total_header_size)[typeflag_o]; if( typeflag2 == tf_extended || typeflag2 == tf_global ) @@ -294,9 +301,7 @@ int compress_archive( const Cl_options & cl_opts, rest -= rd; if( rd != size ) { - if( verbosity >= 0 ) - std::fprintf( stderr, "'%s' ends unexpectedly at pos %llu\n", - filename, file_size - rest ); + show_atpos_error( filename, file_size - rest, true ); close( infd ); return 1; } if( !archive_write( buf, size, encoder ) ) { close( infd ); return 1; } @@ -321,6 +326,18 @@ int compress_archive( const Cl_options & cl_opts, } // end namespace +void show_atpos_error( const char * const filename, const long long pos, + const bool isarchive ) + { + if( verbosity < 0 ) return; + std::fprintf( stderr, "%s: %s: %s %s at pos %llu%s%s\n", program_name, + filename, isarchive ? "Archive" : "File", + ( errno > 0 ) ? "read error" : "ends unexpectedly", pos, + ( errno > 0 ) ? ": " : "", + ( errno > 0 ) ? std::strerror( errno ) : "" ); + } + + int compress( const Cl_options & cl_opts ) { if( cl_opts.num_files > 1 && cl_opts.output_filename.size() ) |