1
0
Fork 0

Adding upstream version 0.24.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-02-17 21:27:02 +01:00
parent 9a8733dd3b
commit 4f5d0de2b2
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
33 changed files with 905 additions and 882 deletions

180
create.cc
View file

@ -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
@ -20,8 +20,6 @@
#include <algorithm>
#include <cerrno>
#include <cstdio>
#include <cstdlib>
#include <pthread.h>
#include <stdint.h> // for lzlib.h
#include <unistd.h>
#include <sys/stat.h>
@ -50,7 +48,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 +75,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 +83,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 +97,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 +139,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 +147,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 +201,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 +242,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 +263,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 +383,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 +492,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 +507,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 +516,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 +530,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 +558,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 +581,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 +621,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 +646,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 ) )