diff options
Diffstat (limited to 'create.cc')
-rw-r--r-- | create.cc | 182 |
1 files changed, 67 insertions, 115 deletions
@@ -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,14 +20,14 @@ #include <algorithm> #include <cerrno> #include <cstdio> -#include <cstdlib> -#include <pthread.h> #include <stdint.h> // for lzlib.h #include <unistd.h> #include <sys/stat.h> #if !defined __FreeBSD__ && !defined __OpenBSD__ && !defined __NetBSD__ && \ !defined __DragonFly__ && !defined __APPLE__ && !defined __OS2__ #include <sys/sysmacros.h> // for major, minor +#else +#include <sys/types.h> // for major, minor #endif #include <ftw.h> #include <grp.h> @@ -50,7 +50,6 @@ const char * archive_namep = 0; unsigned long long partial_data_size = 0; // size of current block Resizable_buffer grbuf; // extended header + data int goutfd = -1; -int error_status = 0; bool option_C_after_relative_filename( const Arg_parser & parser ) @@ -78,7 +77,7 @@ long long check_compressed_appendable( const int fd, const bool remove_eoa ) if( rd == 0 && errno == 0 ) return 0; // append to empty archive if( rd < min_member_size || ( rd != bufsize && errno ) ) return -1; const Lzip_header * const p = (const Lzip_header *)buf; // shut up gcc - if( !p->verify_magic() || !p->verify_version() ) return -1; + if( !p->check_magic() || !p->check_version() ) return -1; LZ_Decoder * decoder = LZ_decompress_open(); // decompress first header if( !decoder || LZ_decompress_errno( decoder ) != LZ_ok || LZ_decompress_write( decoder, buf, rd ) != rd || @@ -86,7 +85,7 @@ long long check_compressed_appendable( const int fd, const bool remove_eoa ) { LZ_decompress_close( decoder ); return -1; } LZ_decompress_close( decoder ); const bool maybe_eoa = block_is_zero( buf, header_size ); - if( !verify_ustar_chksum( buf ) && !maybe_eoa ) return -1; + if( !check_ustar_chksum( buf ) && !maybe_eoa ) return -1; const long long end = lseek( fd, 0, SEEK_END ); if( end < min_member_size ) return -1; @@ -100,7 +99,7 @@ long long check_compressed_appendable( const int fd, const bool remove_eoa ) Lzip_header header; // read last header if( seek_read( fd, header.data, Lzip_header::size, end - member_size ) != Lzip_header::size ) return -1; - if( !header.verify_magic() || !header.verify_version() || + if( !header.check_magic() || !header.check_version() || !isvalid_ds( header.dictionary_size() ) ) return -1; // EOA marker in last member must contain between 512 and 32256 zeros alone @@ -142,7 +141,7 @@ long long check_uncompressed_appendable( const int fd, const bool remove_eoa ) const int rd = readblock( fd, header, header_size ); if( rd == 0 && errno == 0 ) break; // missing EOA blocks if( rd != header_size ) return -1; - if( !verify_ustar_chksum( header ) ) // maybe EOA block + if( !check_ustar_chksum( header ) ) // maybe EOA block { if( block_is_zero( header, header_size ) ) break; else return -1; } const Typeflag typeflag = (Typeflag)header[typeflag_o]; if( typeflag == tf_extended || typeflag == tf_global ) @@ -150,7 +149,7 @@ long long check_uncompressed_appendable( const int fd, const bool remove_eoa ) if( prev_extended ) return -1; 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 ) + if( edsize <= 0 || edsize >= 1LL << 33 || bufsize > max_edata_size ) return -1; // overflow or no extended data if( !rbuf.resize( bufsize ) ) return -2; if( readblock( fd, rbuf.u8(), bufsize ) != bufsize ) @@ -204,21 +203,6 @@ bool archive_write( const uint8_t * const buf, const int size ) } -bool write_extended( const Extended & extended ) - { - const long long ebsize = extended.format_block( grbuf ); // may be 0 - if( ebsize < 0 ) - { show_error( ( ebsize == -2 ) ? mem_msg2 : eferec_msg ); return false; } - for( long long pos = 0; pos < ebsize; ) // write extended block to archive - { - int size = std::min( ebsize - pos, 1LL << 20 ); - if( !archive_write( grbuf.u8() + pos, size ) ) return false; - pos += size; - } - return true; - } - - // Return true if it stores filename in the ustar header. bool store_name( const char * const filename, Extended & extended, Tar_header header, const bool force_extended_name ) @@ -260,12 +244,15 @@ int add_member( const char * const filename, const struct stat *, const int infd = file_size ? open_instream( filename ) : -1; if( file_size && infd < 0 ) { set_error_status( 1 ); return 0; } + const int ebsize = extended.format_block( grbuf ); // may be 0 + if( ebsize < 0 ) { show_error( extended.full_size_error() ); return 1; } if( encoder && gcl_opts->solidity == bsolid && - block_is_full( extended.full_size(), file_size, gcl_opts->data_size, + block_is_full( ebsize, file_size, gcl_opts->data_size, partial_data_size ) && !archive_write( 0, 0 ) ) return 1; + // write extended block to archive + if( ebsize > 0 && !archive_write( grbuf.u8(), ebsize ) ) return 1; + if( !archive_write( header, header_size ) ) return 1; - if( !write_extended( extended ) || !archive_write( header, header_size ) ) - return 1; if( file_size ) { const long long bufsize = 32 * header_size; @@ -278,9 +265,7 @@ int add_member( const char * const filename, const struct stat *, rest -= rd; if( rd != size ) { - if( verbosity >= 0 ) - std::fprintf( stderr, "File '%s' ends unexpectedly at pos %llu\n", - filename, file_size - rest ); + show_atpos_error( filename, file_size - rest, false ); close( infd ); return 1; } if( rest == 0 ) // last read @@ -400,31 +385,6 @@ const char * remove_leading_dotslash( const char * const filename, } -/* If msgp is null, print the message, else return the message in *msgp. - If prefix is already in the list, print nothing or return empty *msgp. - Return true if a message is printed or returned in *msgp. */ -bool print_removed_prefix( const std::string & prefix, - std::string * const msgp ) - { - // prevent two threads from modifying the list of prefixes at the same time - static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; - static std::vector< std::string > prefixes; // list of prefixes - - if( verbosity < 0 || prefix.empty() ) - { if( msgp ) msgp->clear(); return false; } - xlock( &mutex ); - for( unsigned i = 0; i < prefixes.size(); ++i ) - if( prefixes[i] == prefix ) - { xunlock( &mutex ); if( msgp ) msgp->clear(); return false; } - prefixes.push_back( prefix ); - std::string msg( "Removing leading '" ); msg += prefix; - msg += "' from member names."; - if( msgp ) *msgp = msg; else show_error( msg.c_str() ); - xunlock( &mutex ); // put here to prevent mixing calls to show_error - return true; - } - - // set file_size != 0 only for regular files bool fill_headers( const char * const filename, Extended & extended, Tar_header header, long long & file_size, const int flag ) @@ -534,13 +494,13 @@ bool fill_headers( const char * const filename, Extended & extended, } -bool block_is_full( const long long extended_size, +bool block_is_full( const int extended_size, const unsigned long long file_size, const unsigned long long target_size, unsigned long long & partial_data_size ) { const unsigned long long member_size = // may overflow 'long long' - header_size + extended_size + round_up( file_size ); + extended_size + header_size + round_up( file_size ); if( partial_data_size >= target_size || ( partial_data_size >= min_data_size && partial_data_size + member_size / 2 > target_size ) ) @@ -549,25 +509,6 @@ bool block_is_full( const long long extended_size, } -void set_error_status( const int retval ) - { - // prevent two threads from modifying the error_status at the same time - static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; - - xlock( &mutex ); - if( error_status < retval ) error_status = retval; - xunlock( &mutex ); - } - -int final_exit_status( int retval, const bool show_msg ) - { - if( retval == 0 && error_status ) - { if( show_msg ) - show_error( "Exiting with failure status due to previous errors." ); - retval = error_status; } - return retval; - } - unsigned ustar_chksum( const Tar_header header ) { unsigned chksum = chksum_l * 0x20; // treat chksum field as spaces @@ -577,8 +518,8 @@ unsigned ustar_chksum( const Tar_header header ) } -bool verify_ustar_chksum( const Tar_header header ) - { return ( verify_ustar_magic( header ) && +bool check_ustar_chksum( const Tar_header header ) + { return ( check_ustar_magic( header ) && ustar_chksum( header ) == parse_octal( header + chksum_o, chksum_l ) ); } @@ -591,10 +532,25 @@ bool has_lz_ext( const std::string & name ) } +int Cl_options::compressed() const // tri-state bool with error (-2) + { + const int lz_ext = archive_name.empty() ? -1 : has_lz_ext( archive_name ); + if( !level_set ) return lz_ext; // no level set in command line + const bool cl_compressed = !uncompressed(); + if( lz_ext < 0 || lz_ext == cl_compressed ) return cl_compressed; + show_file_error( archive_name.c_str(), lz_ext ? + "Uncompressed archive can't have .lz or .tlz extension." : + "Compressed archive requires .lz or .tlz extension." ); + return -2; + } + + int concatenate( const Cl_options & cl_opts ) { if( cl_opts.num_files <= 0 ) { if( verbosity >= 1 ) show_error( "Nothing to concatenate." ); return 0; } + int compressed = cl_opts.compressed(); // tri-state bool + if( compressed == -2 ) return 1; const bool to_stdout = cl_opts.archive_name.empty(); archive_namep = to_stdout ? "(stdout)" : cl_opts.archive_name.c_str(); const int outfd = @@ -604,24 +560,17 @@ int concatenate( const Cl_options & cl_opts ) { close( outfd ); return 1; } if( !to_stdout && !archive_attrs.init( outfd ) ) { show_file_error( archive_namep, "Can't stat", errno ); return 1; } - int compressed; // tri-state bool - if( to_stdout ) compressed = -1; // unknown - else + if( !to_stdout && compressed >= 0 ) // level or ext are set in cl { - compressed = has_lz_ext( cl_opts.archive_name ); // default value - long long pos = check_compressed_appendable( outfd, true ); - if( pos > 0 ) compressed = true; - else if( pos < 0 ) - { - pos = check_uncompressed_appendable( outfd, true ); - if( pos > 0 ) compressed = false; - else if( pos == -2 ) { show_error( mem_msg ); close( outfd ); return 1; } - else if( pos < 0 ) - { show_file_error( archive_namep, compressed ? - "This does not look like an appendable tar.lz archive." : - "This does not look like an appendable tar archive." ); - close( outfd ); return 2; } - } + const long long pos = compressed ? + check_compressed_appendable( outfd, true ) : + check_uncompressed_appendable( outfd, true ); + if( pos == -2 ) { show_error( mem_msg ); close( outfd ); return 1; } + if( pos < 0 ) + { show_file_error( archive_namep, compressed ? + "This does not look like an appendable tar.lz archive." : + "This does not look like an appendable tar archive." ); + close( outfd ); return 2; } } int retval = 0; @@ -634,17 +583,18 @@ int concatenate( const Cl_options & cl_opts ) const int infd = open_instream( filename ); if( infd < 0 ) { retval = 1; break; } struct stat st; - if( !to_stdout && fstat( infd, &st ) == 0 && archive_attrs.is_the_archive( st ) ) - { show_file_error( filename, "Archive can't contain itself; not concatenated." ); - close( infd ); continue; } + if( !to_stdout && fstat( infd, &st ) == 0 && + archive_attrs.is_the_archive( st ) ) + { show_file_error( filename, "Archive can't contain itself; " + "not concatenated." ); close( infd ); continue; } long long size; - if( compressed < 0 ) // not initialized yet + if( compressed < 0 ) // not initialized yet { if( ( size = check_compressed_appendable( infd, false ) ) > 0 ) compressed = true; else if( ( size = check_uncompressed_appendable( infd, false ) ) > 0 ) compressed = false; - else if( size != -2 ) { size = -1 ; compressed = has_lz_ext( filename ); } + else if( size != -2 ) { size = -1; compressed = has_lz_ext( filename ); } } else size = compressed ? check_compressed_appendable( infd, false ) : check_uncompressed_appendable( infd, false ); @@ -673,15 +623,12 @@ int concatenate( const Cl_options & cl_opts ) int encode( const Cl_options & cl_opts ) { if( !grbuf.size() ) { show_error( mem_msg ); return 1; } - const bool compressed = ( cl_opts.level >= 0 && cl_opts.level <= 9 ); + int compressed = cl_opts.compressed(); // tri-state bool + if( compressed == -2 ) return 1; const bool to_stdout = cl_opts.archive_name.empty(); archive_namep = to_stdout ? "(stdout)" : cl_opts.archive_name.c_str(); gcl_opts = &cl_opts; - if( !to_stdout && !compressed && has_lz_ext( cl_opts.archive_name ) ) - { show_file_error( archive_namep, - "Uncompressed mode incompatible with .lz extension." ); return 2; } - const bool append = cl_opts.program_mode == m_append; if( cl_opts.num_files <= 0 ) { @@ -701,18 +648,23 @@ int encode( const Cl_options & cl_opts ) { close( goutfd ); return 1; } if( append && !to_stdout ) { - if( compressed && check_compressed_appendable( goutfd, true ) < 0 ) - { show_file_error( archive_namep, - "This does not look like an appendable tar.lz archive." ); - close( goutfd ); return 2; } - if( !compressed ) + long long pos; + if( compressed < 0 ) // not initialized yet { - const long long pos = check_uncompressed_appendable( goutfd, true ); - if( pos == -2 ) { show_error( mem_msg ); close( goutfd ); return 1; } - if( pos < 0 ) { show_file_error( archive_namep, - "This does not look like an appendable tar archive." ); - close( goutfd ); return 2; } + if( ( pos = check_compressed_appendable( goutfd, true ) ) > 0 ) + compressed = true; + else if( ( pos = check_uncompressed_appendable( goutfd, true ) ) > 0 ) + compressed = false; + else if( pos != -2 ) { pos = -1; compressed = false; } // unknown } + else pos = compressed ? check_compressed_appendable( goutfd, true ) : + check_uncompressed_appendable( goutfd, true ); + if( pos == -2 ) { show_error( mem_msg ); close( goutfd ); return 1; } + if( pos < 0 ) + { show_file_error( archive_namep, compressed ? + "This does not look like an appendable tar.lz archive." : + "This does not look like an appendable tar archive." ); + close( goutfd ); return 2; } } if( !archive_attrs.init( goutfd ) ) |