Adding upstream version 0.24.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
9a8733dd3b
commit
4f5d0de2b2
33 changed files with 905 additions and 882 deletions
180
create.cc
180
create.cc
|
@ -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 ) )
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue