1
0
Fork 0

Merging upstream version 0.10.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-02-17 21:11:12 +01:00
parent e076fdd679
commit 060c1457b6
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
21 changed files with 633 additions and 443 deletions

138
create.cc
View file

@ -38,20 +38,21 @@
#include <lzlib.h>
#include "arg_parser.h"
#include "lzip.h"
#include "tarlz.h"
const CRC32C crc32c;
const CRC32 crc32c( true );
int cl_owner = -1; // global vars needed by add_member
int cl_group = -1;
int cl_data_size = 0;
Solidity solidity = no_solid;
namespace {
LZ_Encoder * encoder = 0; // local vars needed by add_member
const char * archive_namep = 0;
unsigned long long partial_data_size = 0; // current block size
int outfd = -1;
int gretval = 0;
@ -150,17 +151,18 @@ bool check_appendable( const int fd, const bool remove_eof )
}
class File_is_archive
class File_is_the_archive
{
dev_t archive_dev;
ino_t archive_ino;
bool initialized;
public:
File_is_archive() : initialized( false ) {}
bool init()
File_is_the_archive() : initialized( false ) {}
bool init( const int fd )
{
struct stat st;
if( fstat( outfd, &st ) != 0 ) return false;
if( fstat( fd, &st ) != 0 ) return false;
if( S_ISREG( st.st_mode ) )
{ archive_dev = st.st_dev; archive_ino = st.st_ino; initialized = true; }
return true;
@ -169,7 +171,7 @@ public:
{
return initialized && archive_dev == st.st_dev && archive_ino == st.st_ino;
}
} file_is_archive;
} file_is_the_archive;
bool archive_write( const uint8_t * const buf, const int size )
@ -223,50 +225,32 @@ void print_octal( uint8_t * const buf, int size, unsigned long long num )
while( --size >= 0 ) { buf[size] = '0' + ( num % 8 ); num /= 8; }
}
unsigned decimal_digits( unsigned long long value )
{
unsigned digits = 1;
while( value >= 10 ) { value /= 10; ++digits; }
return digits;
}
int record_size( const unsigned keyword_size, const unsigned long value_size )
{
// size = ' ' + keyword + '=' + value + '\n'
unsigned long long size = 1 + keyword_size + 1 + value_size + 1;
const unsigned d1 = decimal_digits( size );
size += decimal_digits( d1 + size );
if( size >= INT_MAX ) size = 0; // overflows snprintf size
return size;
}
bool write_extended( const Extended & extended )
{
const int path_rec = extended.path.size() ?
record_size( 4, extended.path.size() ) : 0;
const int lpath_rec = extended.linkpath.size() ?
record_size( 8, extended.linkpath.size() ) : 0;
const int size_rec = ( extended.size > 0 ) ?
record_size( 4, decimal_digits( extended.size ) ) : 0;
const unsigned long long edsize = path_rec + lpath_rec + size_rec + 22;
const unsigned long long bufsize = round_up( edsize );
const int path_rec = extended.recsize_path();
const int lpath_rec = extended.recsize_linkpath();
const int size_rec = extended.recsize_file_size();
const unsigned long long edsize = extended.edsize();
const unsigned long long bufsize = extended.edsize_pad();
if( edsize >= 1ULL << 33 ) return false; // too much extended data
if( bufsize == 0 ) return edsize == 0; // overflow or no extended data
char * const buf = new char[bufsize+1]; // extended records buffer
unsigned long long pos = path_rec; // goto can't cross this
unsigned long long pos = path_rec; // goto can't cross these
const unsigned crc_size = Extended::crc_record.size();
if( path_rec && snprintf( buf, path_rec + 1, "%d path=%s\n",
path_rec, extended.path.c_str() ) != path_rec )
path_rec, extended.path().c_str() ) != path_rec )
goto error;
if( lpath_rec && snprintf( buf + pos, lpath_rec + 1, "%d linkpath=%s\n",
lpath_rec, extended.linkpath.c_str() ) != lpath_rec )
lpath_rec, extended.linkpath().c_str() ) != lpath_rec )
goto error;
pos += lpath_rec;
if( size_rec && snprintf( buf + pos, size_rec + 1, "%d size=%llu\n",
size_rec, extended.size ) != size_rec )
size_rec, extended.file_size() ) != size_rec )
goto error;
pos += size_rec;
if( snprintf( buf + pos, 23, "22 GNU.crc32=00000000\n" ) != 22 ) goto error;
pos += 22;
std::memcpy( buf + pos, Extended::crc_record.c_str(), crc_size );
pos += crc_size;
if( pos != edsize ) goto error;
print_hex( buf + edsize - 9, 8,
crc32c.windowed_crc( (const uint8_t *)buf, edsize - 9, edsize ) );
@ -316,27 +300,29 @@ const char * remove_leading_dotdot( const char * const filename )
}
// Return true if filename fits in the ustar header.
// Return true if it stores filename in the ustar header.
bool store_name( const char * const filename, Extended & extended,
Tar_header header )
Tar_header header, const bool force_extended_name )
{
const char * const stored_name = remove_leading_dotdot( filename );
const int len = std::strlen( stored_name );
enum { max_len = prefix_l + 1 + name_l }; // prefix + '/' + name
// first try storing filename in the ustar header
if( len <= name_l ) // stored_name fits in name
{ std::memcpy( header + name_o, stored_name, len ); return true; }
if( len <= max_len ) // find shortest prefix
for( int i = len - name_l - 1; i < len && i <= prefix_l; ++i )
if( stored_name[i] == '/' ) // stored_name can be split
{
std::memcpy( header + name_o, stored_name + i + 1, len - i - 1 );
std::memcpy( header + prefix_o, stored_name, i );
return true;
}
if( !force_extended_name ) // try storing filename in the ustar header
{
const int len = std::strlen( stored_name );
enum { max_len = prefix_l + 1 + name_l }; // prefix + '/' + name
if( len <= name_l ) // stored_name fits in name
{ std::memcpy( header + name_o, stored_name, len ); return true; }
if( len <= max_len ) // find shortest prefix
for( int i = len - name_l - 1; i < len && i <= prefix_l; ++i )
if( stored_name[i] == '/' ) // stored_name can be split
{
std::memcpy( header + name_o, stored_name + i + 1, len - i - 1 );
std::memcpy( header + prefix_o, stored_name, i );
return true;
}
}
// store filename in extended record, leave name zeroed in ustar header
extended.path = stored_name;
extended.path( stored_name );
return false;
}
@ -348,13 +334,13 @@ int add_member( const char * const filename, const struct stat *,
if( lstat( filename, &st ) != 0 )
{ show_file_error( filename, "Can't stat input file", errno );
gretval = 1; return 0; }
if( file_is_archive( st ) )
if( file_is_the_archive( st ) )
{ show_file_error( archive_namep, "File is the archive; not dumped." );
return 0; }
Extended extended; // metadata for extended records
Tar_header header;
init_tar_header( header );
store_name( filename, extended, header );
bool force_extended_name = false;
const mode_t mode = st.st_mode;
print_octal( header + mode_o, mode_l - 1,
@ -392,7 +378,8 @@ int add_member( const char * const filename, const struct stat *,
{
char * const buf = new char[st.st_size+1];
len = readlink( filename, buf, st.st_size );
if( len == st.st_size ) { buf[len] = 0; extended.linkpath = buf; }
if( len == st.st_size )
{ buf[len] = 0; extended.linkpath( buf ); force_extended_name = true; }
delete[] buf;
}
if( len != st.st_size )
@ -418,12 +405,30 @@ int add_member( const char * const filename, const struct stat *,
const struct group * const gr = getgrgid( gid );
if( gr && gr->gr_name )
std::strncpy( (char *)header + gname_o, gr->gr_name, gname_l - 1 );
if( file_size >= 1ULL << 33 ) extended.size = file_size;
if( file_size >= 1ULL << 33 )
{ extended.file_size( file_size ); force_extended_name = true; }
else print_octal( header + size_o, size_l - 1, file_size );
store_name( filename, extended, header, force_extended_name );
print_octal( header + chksum_o, chksum_l - 1, ustar_chksum( header ) );
const int infd = file_size ? open_instream( filename ) : -1;
if( file_size && infd < 0 ) { gretval = 1; return 0; }
if( encoder && solidity == bsolid )
{
const unsigned long long member_size =
header_size + extended.full_size() + round_up( file_size );
const unsigned long long target_size = cl_data_size;
if( partial_data_size >= target_size ||
( partial_data_size >= min_data_size &&
partial_data_size + member_size / 2 > target_size ) )
{
partial_data_size = member_size;
if( !archive_write( 0, 0 ) )
{ show_error( "Error flushing encoder", errno ); return 1; }
}
else partial_data_size += member_size;
}
if( !extended.empty() && !write_extended( extended ) )
{ show_error( "Error writing extended header", errno ); return 1; }
if( !archive_write( header, header_size ) )
@ -491,7 +496,7 @@ int concatenate( const std::string & archive_name, const Arg_parser & parser,
{ show_error( "'--concatenate' is incompatible with '-f -'.", 0, true );
return 1; }
if( ( outfd = open_outstream( archive_name, false ) ) < 0 ) return 1;
if( !file_is_archive.init() )
if( !file_is_the_archive.init( outfd ) )
{ show_file_error( archive_name.c_str(), "Can't stat", errno ); return 1; }
int retval = 0;
@ -507,7 +512,7 @@ int concatenate( const std::string & archive_name, const Arg_parser & parser,
{ show_file_error( filename, "Not an appendable tar.lz archive." );
close( infd ); retval = 2; break; }
struct stat st;
if( fstat( infd, &st ) == 0 && file_is_archive( st ) )
if( fstat( infd, &st ) == 0 && file_is_the_archive( st ) )
{ show_file_error( filename, "File is the archive; not concatenated." );
close( infd ); continue; }
if( !check_appendable( outfd, true ) )
@ -572,12 +577,18 @@ int encode( const std::string & archive_name, const Arg_parser & parser,
}
archive_namep = archive_name.size() ? archive_name.c_str() : "(stdout)";
if( !file_is_archive.init() )
if( !file_is_the_archive.init( outfd ) )
{ show_file_error( archive_namep, "Can't stat", errno ); return 1; }
if( compressed )
{
encoder = LZ_compress_open( option_mapping[level].dictionary_size,
const int dictionary_size = option_mapping[level].dictionary_size;
if( cl_data_size <= 0 )
{
if( level == 0 ) cl_data_size = 1 << 20;
else cl_data_size = 2 * dictionary_size;
}
encoder = LZ_compress_open( dictionary_size,
option_mapping[level].match_len_limit, LLONG_MAX );
if( !encoder || LZ_compress_errno( encoder ) != LZ_ok )
{
@ -619,7 +630,8 @@ int encode( const std::string & archive_name, const Arg_parser & parser,
enum { bufsize = 2 * header_size };
uint8_t buf[bufsize];
std::memset( buf, 0, bufsize );
if( encoder && solidity == asolid && !archive_write( 0, 0 ) )
if( encoder && ( solidity == asolid || solidity == bsolid ) &&
!archive_write( 0, 0 ) )
{ show_error( "Error flushing encoder", errno ); retval = 1; }
else if( !archive_write( buf, bufsize ) ||
( encoder && !archive_write( 0, 0 ) ) ) // flush encoder