1
0
Fork 0

Merging upstream version 0.13.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-02-17 21:12:49 +01:00
parent dea1c6852b
commit ab79495362
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
21 changed files with 280 additions and 225 deletions

View file

@ -1,3 +1,10 @@
2019-02-27 Antonio Diaz Diaz <antonio@gnu.org>
* Version 0.13 released.
* create_lz.cc (cworker): Fix skipping of unreadable files.
* list_lz.cc: Fix listing of archives containing empty lzip members.
* create.cc (fill_headers): Store negative mtime as cero.
2019-02-22 Antonio Diaz Diaz <antonio@gnu.org> 2019-02-22 Antonio Diaz Diaz <antonio@gnu.org>
* Version 0.12 released. * Version 0.12 released.

View file

@ -136,10 +136,12 @@ dist : doc
$(DISTNAME)/testsuite/test_bad[12].txt.tar.lz \ $(DISTNAME)/testsuite/test_bad[12].txt.tar.lz \
$(DISTNAME)/testsuite/test3.tar.lz \ $(DISTNAME)/testsuite/test3.tar.lz \
$(DISTNAME)/testsuite/test3_eof[123].tar.lz \ $(DISTNAME)/testsuite/test3_eof[123].tar.lz \
$(DISTNAME)/testsuite/test3_em[1-6].tar.lz \
$(DISTNAME)/testsuite/tlz_in_tar[12].tar \ $(DISTNAME)/testsuite/tlz_in_tar[12].tar \
$(DISTNAME)/testsuite/tar_in_tlz[12].tar.lz \ $(DISTNAME)/testsuite/tar_in_tlz[12].tar.lz \
$(DISTNAME)/testsuite/test3_dir.tar.lz \ $(DISTNAME)/testsuite/test3_dir.tar.lz \
$(DISTNAME)/testsuite/test3_dot.tar.lz \ $(DISTNAME)/testsuite/test3_dot.tar.lz \
$(DISTNAME)/testsuite/ts_in_link.tar.lz \
$(DISTNAME)/testsuite/t155.tar.lz \ $(DISTNAME)/testsuite/t155.tar.lz \
$(DISTNAME)/testsuite/test3_bad[1-6].tar.lz \ $(DISTNAME)/testsuite/test3_bad[1-6].tar.lz \
$(DISTNAME)/testsuite/dotdot[1-5].tar.lz \ $(DISTNAME)/testsuite/dotdot[1-5].tar.lz \

28
NEWS
View file

@ -1,23 +1,11 @@
Changes in version 0.12: Changes in version 0.13:
When dumping a character special file or a block special file, the devmajor Skipping of unreadable files during multi-threaded archive creation with
and devminor fields were incorrectly filled with the values corresponding to per-file compression has been fixed. Tarlz did produce empty lzip members,
the device containing the special file instead of the values corresponding and sometines left the last files out of the archive.
to the special file itself.
If when creating an archive tarlz can't find a user or group name in the Multi-threaded listing of tar.lz archives containing empty lzip members has
database, it now saves just the numerical uid/gid instead of exiting with been fixed. It listed members out of order and sometimes hung.
error status.
When listing verbosely a character special file or a block special file, the When creating an archive, negative modification times are now stored as cero
devmajor and devminor values are now shown. (1970-01-01 00:00:00 UTC). Negative times are not portable.
The new option '-d, --diff', which reports differences between archive and
file system, has been added.
The new option '--ignore-ids', which tells '-d, --diff' to ignore
differences in owner and group IDs, has been added. This option is useful
when comparing an --anonymous archive.
Listing of large seekable uncompressed archives is now much faster because
tarlz now skips over member data instead of reading it.

2
configure vendored
View file

@ -6,7 +6,7 @@
# to copy, distribute and modify it. # to copy, distribute and modify it.
pkgname=tarlz pkgname=tarlz
pkgversion=0.12 pkgversion=0.13
progname=tarlz progname=tarlz
srctrigger=doc/${pkgname}.texi srctrigger=doc/${pkgname}.texi

View file

@ -92,15 +92,6 @@ bool option_C_after_relative_filename( const Arg_parser & parser )
} }
bool writeblock_wrapper( const int outfd, const uint8_t * const buffer,
const int size )
{
if( writeblock( outfd, buffer, size ) != size )
{ show_file_error( archive_namep, "Write error", errno ); return false; }
return true;
}
// infd and outfd can refer to the same file if copying to a lower file // infd and outfd can refer to the same file if copying to a lower file
// position or if source and destination blocks don't overlap. // position or if source and destination blocks don't overlap.
// max_size < 0 means no size limit. // max_size < 0 means no size limit.
@ -310,6 +301,15 @@ int add_member( const char * const filename, const struct stat *,
} // end namespace } // end namespace
bool writeblock_wrapper( const int outfd, const uint8_t * const buffer,
const int size )
{
if( writeblock( outfd, buffer, size ) != size )
{ show_file_error( archive_namep, "Write error", errno ); return false; }
return true;
}
/* Removes any amount of leading "./" and '/' strings from filename. /* Removes any amount of leading "./" and '/' strings from filename.
Optionally also removes prefixes containing a ".." component. */ Optionally also removes prefixes containing a ".." component. */
const char * remove_leading_dotslash( const char * const filename, const char * remove_leading_dotslash( const char * const filename,
@ -322,10 +322,7 @@ const char * remove_leading_dotslash( const char * const filename,
if( dotdot ) if( dotdot )
for( int i = 0; filename[i]; ++i ) for( int i = 0; filename[i]; ++i )
if( filename[i] == '.' && filename[i+1] == '.' && if( dotdot_at_i( filename, i ) ) p = filename + i + 2;
( i == 0 || filename[i-1] == '/' ) &&
( filename[i+2] == 0 || filename[i+2] == '/' ) )
p = filename + i + 2;
while( *p == '/' || ( *p == '.' && p[1] == '/' ) ) ++p; while( *p == '/' || ( *p == '.' && p[1] == '/' ) ) ++p;
if( p != filename ) if( p != filename )
{ {
@ -371,8 +368,8 @@ bool fill_headers( const char * const filename, Extended & extended,
set_error_status( 1 ); return false; } set_error_status( 1 ); return false; }
print_octal( header + uid_o, uid_l - 1, uid ); print_octal( header + uid_o, uid_l - 1, uid );
print_octal( header + gid_o, gid_l - 1, gid ); print_octal( header + gid_o, gid_l - 1, gid );
const long long mtime = st.st_mtime; // shut up gcc about time_t const unsigned long long mtime = (st.st_mtime >= 0) ? st.st_mtime : 0;
if( mtime < 0 || mtime >= 1LL << 33 ) if( mtime >= 1ULL << 33 )
{ show_file_error( filename, "mtime is out of ustar range [0, 8_589_934_591]." ); { show_file_error( filename, "mtime is out of ustar range [0, 8_589_934_591]." );
set_error_status( 1 ); return false; } set_error_status( 1 ); return false; }
print_octal( header + mtime_o, mtime_l - 1, mtime ); print_octal( header + mtime_o, mtime_l - 1, mtime );
@ -388,25 +385,35 @@ bool fill_headers( const char * const filename, Extended & extended,
else if( S_ISLNK( mode ) ) else if( S_ISLNK( mode ) )
{ {
typeflag = tf_symlink; typeflag = tf_symlink;
long len; long len, sz;
if( st.st_size <= linkname_l ) if( st.st_size <= linkname_l )
len = readlink( filename, (char *)header + linkname_o, linkname_l ); {
len = sz = readlink( filename, (char *)header + linkname_o, linkname_l );
while( len > 1 && header[linkname_o+len-1] == '/' ) // trailing '/'
{ --len; header[linkname_o+len] = 0; }
}
else else
{ {
char * const buf = new char[st.st_size+1]; char * const buf = new char[st.st_size+1];
len = readlink( filename, buf, st.st_size ); len = sz = readlink( filename, buf, st.st_size );
if( len == st.st_size ) if( sz == st.st_size )
{ buf[len] = 0; extended.linkpath( buf ); force_extended_name = true; } {
while( len > 1 && buf[len-1] == '/' ) --len; // trailing '/'
if( len <= linkname_l ) std::memcpy( header + linkname_o, buf, len );
else { buf[len] = 0; extended.linkpath( buf );
force_extended_name = true; }
}
delete[] buf; delete[] buf;
} }
if( len != st.st_size ) if( sz != st.st_size )
{ show_file_error( filename, "Error reading link", (len < 0) ? errno : 0 ); { show_file_error( filename, "Error reading link", (sz < 0) ? errno : 0 );
set_error_status( 1 ); return false; } set_error_status( 1 ); return false; }
} }
else if( S_ISCHR( mode ) || S_ISBLK( mode ) ) else if( S_ISCHR( mode ) || S_ISBLK( mode ) )
{ {
typeflag = S_ISCHR( mode ) ? tf_chardev : tf_blockdev; typeflag = S_ISCHR( mode ) ? tf_chardev : tf_blockdev;
if( major( st.st_rdev ) >= 2 << 20 || minor( st.st_rdev ) >= 2 << 20 ) if( (unsigned)major( st.st_rdev ) >= 2 << 20 ||
(unsigned)minor( st.st_rdev ) >= 2 << 20 )
{ show_file_error( filename, "devmajor or devminor is larger than 2_097_151." ); { show_file_error( filename, "devmajor or devminor is larger than 2_097_151." );
set_error_status( 1 ); return false; } set_error_status( 1 ); return false; }
print_octal( header + devmajor_o, devmajor_l - 1, major( st.st_rdev ) ); print_octal( header + devmajor_o, devmajor_l - 1, major( st.st_rdev ) );
@ -593,7 +600,7 @@ int encode( const std::string & archive_name, const Arg_parser & parser,
!option_C_after_relative_filename( parser ) ) !option_C_after_relative_filename( parser ) )
{ {
// show_file_error( archive_namep, "Multi-threaded --create" ); // show_file_error( archive_namep, "Multi-threaded --create" );
return encode_lz( archive_namep, parser, dictionary_size, return encode_lz( parser, dictionary_size,
option_mapping[level].match_len_limit, num_workers, option_mapping[level].match_len_limit, num_workers,
goutfd, debug_level ); goutfd, debug_level );
} }

View file

@ -368,7 +368,11 @@ void loop_encode( const uint8_t * const ibuf, const int isize,
courier.collect_packet( new Opacket( obuf, opos ), worker_id ); courier.collect_packet( new Opacket( obuf, opos ), worker_id );
opos = 0; obuf = new( std::nothrow ) uint8_t[max_packet_size]; opos = 0; obuf = new( std::nothrow ) uint8_t[max_packet_size];
if( !obuf ) { show_error( mem_msg2 ); cleanup_and_fail(); } if( !obuf ) { show_error( mem_msg2 ); cleanup_and_fail(); }
if( LZ_compress_finished( encoder ) == 1 ) break; if( LZ_compress_finished( encoder ) == 1 )
{
if( LZ_compress_restart_member( encoder, LLONG_MAX ) >= 0 ) break;
show_error( "LZ_compress_restart_member failed." ); cleanup_and_fail();
}
} }
} }
if( ipos > isize ) internal_error( "ipacket size exceeded in worker." ); if( ipos > isize ) internal_error( "ipacket size exceeded in worker." );
@ -401,27 +405,27 @@ extern "C" void * cworker( void * arg )
if( !rbuf.size() ) { show_error( mem_msg2 ); cleanup_and_fail(); } if( !rbuf.size() ) { show_error( mem_msg2 ); cleanup_and_fail(); }
int opos = 0; int opos = 0;
bool flushed = true; // avoid producing empty lzip members
while( true ) while( true )
{ {
const Ipacket * const ipacket = courier.distribute_packet( worker_id ); const Ipacket * const ipacket = courier.distribute_packet( worker_id );
if( !ipacket ) break; // no more packets to process if( !ipacket ) break; // no more packets to process
if( ipacket->filename.empty() ) // end of group, flush encoder if( ipacket->filename.empty() ) // end of group
{ {
if( !encoder ) { delete ipacket; continue; } // nothing to flush if( !flushed ) // this lzip member is not empty
loop_encode( 0, 0, data, opos, courier, encoder, worker_id, true ); loop_encode( 0, 0, data, opos, courier, encoder, worker_id, true );
courier.collect_packet( new Opacket, worker_id ); // end of member token courier.collect_packet( new Opacket, worker_id ); // end of member token
if( LZ_compress_restart_member( encoder, LLONG_MAX ) < 0 ) flushed = true; delete ipacket; continue;
{ show_error( "LZ_compress_restart_member failed." ); cleanup_and_fail(); }
delete ipacket; continue;
} }
const int infd = const int infd =
ipacket->file_size ? open_instream( ipacket->filename.c_str() ) : -1; ipacket->file_size ? open_instream( ipacket->filename.c_str() ) : -1;
if( ipacket->file_size && infd < 0 ) if( ipacket->file_size && infd < 0 ) // can't read file data
{ delete[] ipacket->header; delete ipacket->extended; delete ipacket; { delete[] ipacket->header; delete ipacket->extended; delete ipacket;
set_error_status( 1 ); continue; } set_error_status( 1 ); continue; } // skip file
if( !encoder ) flushed = false;
if( !encoder ) // init encoder just before using it
{ {
data = new( std::nothrow ) uint8_t[max_packet_size]; data = new( std::nothrow ) uint8_t[max_packet_size];
encoder = LZ_compress_open( dictionary_size, match_len_limit, LLONG_MAX ); encoder = LZ_compress_open( dictionary_size, match_len_limit, LLONG_MAX );
@ -494,17 +498,15 @@ extern "C" void * cworker( void * arg )
/* Get from courier the processed and sorted packets, and write /* Get from courier the processed and sorted packets, and write
their contents to the output archive. */ their contents to the output archive. */
void muxer( Packet_courier & courier, const char * const archive_name, void muxer( Packet_courier & courier, const int outfd )
const int outfd )
{ {
while( true ) while( true )
{ {
const Opacket * const opacket = courier.deliver_packet(); const Opacket * const opacket = courier.deliver_packet();
if( !opacket ) break; // queue is empty. all workers exited if( !opacket ) break; // queue is empty. all workers exited
if( writeblock( outfd, opacket->data, opacket->size ) != opacket->size ) if( !writeblock_wrapper( outfd, opacket->data, opacket->size ) )
{ show_file_error( archive_name, "Write error", errno ); cleanup_and_fail();
cleanup_and_fail(); }
delete[] opacket->data; delete[] opacket->data;
delete opacket; delete opacket;
} }
@ -514,9 +516,9 @@ void muxer( Packet_courier & courier, const char * const archive_name,
// init the courier, then start the grouper and the workers and call the muxer // init the courier, then start the grouper and the workers and call the muxer
int encode_lz( const char * const archive_name, const Arg_parser & parser, int encode_lz( const Arg_parser & parser, const int dictionary_size,
const int dictionary_size, const int match_len_limit, const int match_len_limit, const int num_workers,
const int num_workers, const int outfd, const int debug_level ) const int outfd, const int debug_level )
{ {
const int in_slots = 65536; // max small files (<=512B) in 64 MiB const int in_slots = 65536; // max small files (<=512B) in 64 MiB
const int total_in_slots = ( INT_MAX / num_workers >= in_slots ) ? const int total_in_slots = ( INT_MAX / num_workers >= in_slots ) ?
@ -552,7 +554,7 @@ int encode_lz( const char * const archive_name, const Arg_parser & parser,
{ show_error( "Can't create worker threads", errcode ); cleanup_and_fail(); } { show_error( "Can't create worker threads", errcode ); cleanup_and_fail(); }
} }
muxer( courier, archive_name, outfd ); muxer( courier, outfd );
for( int i = num_workers - 1; i >= 0; --i ) for( int i = num_workers - 1; i >= 0; --i )
{ {
@ -575,9 +577,7 @@ int encode_lz( const char * const archive_name, const Arg_parser & parser,
0xA3, 0xB7, 0x80, 0x0C, 0x82, 0xDB, 0xFF, 0xFF, 0x9F, 0xF0, 0x00, 0x00, 0xA3, 0xB7, 0x80, 0x0C, 0x82, 0xDB, 0xFF, 0xFF, 0x9F, 0xF0, 0x00, 0x00,
0x2E, 0xAF, 0xB5, 0xEF, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2E, 0xAF, 0xB5, 0xEF, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x2C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; 0x2C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
if( writeblock( outfd, eof_member, eof_member_size ) != eof_member_size ) if( !writeblock_wrapper( outfd, eof_member, eof_member_size ) ) retval = 1;
{ show_error( "Error writing end-of-archive blocks", errno );
retval = 1; }
if( close( outfd ) != 0 && !retval ) if( close( outfd ) != 0 && !retval )
{ show_error( "Error closing archive", errno ); retval = 1; } { show_error( "Error closing archive", errno ); retval = 1; }

View file

@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.46.1. .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.46.1.
.TH TARLZ "1" "February 2019" "tarlz 0.12" "User Commands" .TH TARLZ "1" "February 2019" "tarlz 0.13" "User Commands"
.SH NAME .SH NAME
tarlz \- creates tar archives with multimember lzip compression tarlz \- creates tar archives with multimember lzip compression
.SH SYNOPSIS .SH SYNOPSIS

View file

@ -11,7 +11,7 @@ File: tarlz.info, Node: Top, Next: Introduction, Up: (dir)
Tarlz Manual Tarlz Manual
************ ************
This manual is for Tarlz (version 0.12, 22 February 2019). This manual is for Tarlz (version 0.13, 27 February 2019).
* Menu: * Menu:

View file

@ -6,8 +6,8 @@
@finalout @finalout
@c %**end of header @c %**end of header
@set UPDATED 22 February 2019 @set UPDATED 27 February 2019
@set VERSION 0.12 @set VERSION 0.13
@dircategory Data Compression @dircategory Data Compression
@direntry @direntry

View file

@ -86,23 +86,6 @@ uint32_t parse_record_crc( const char * const ptr )
return crc; return crc;
} }
} // end namespace
const std::string Extended::crc_record( "22 GNU.crc32=00000000\n" );
void Extended::calculate_sizes() const
{
linkpath_recsize_ = linkpath_.size() ? record_size( 8, linkpath_.size() ) : 0;
path_recsize_ = path_.size() ? record_size( 4, path_.size() ) : 0;
file_size_recsize_ =
( file_size_ > 0 ) ? record_size( 4, decimal_digits( file_size_ ) ) : 0;
edsize_ = linkpath_recsize_ + path_recsize_ + file_size_recsize_ +
crc_record.size();
padded_edsize_ = round_up( edsize_ );
full_size_ = header_size + padded_edsize_;
}
unsigned char xdigit( const unsigned value ) unsigned char xdigit( const unsigned value )
{ {
@ -144,6 +127,23 @@ bool print_record( char * const buf, const int size,
return pos == size; return pos == size;
} }
} // end namespace
const std::string Extended::crc_record( "22 GNU.crc32=00000000\n" );
void Extended::calculate_sizes() const
{
linkpath_recsize_ = linkpath_.size() ? record_size( 8, linkpath_.size() ) : 0;
path_recsize_ = path_.size() ? record_size( 4, path_.size() ) : 0;
file_size_recsize_ =
( file_size_ > 0 ) ? record_size( 4, decimal_digits( file_size_ ) ) : 0;
edsize_ = linkpath_recsize_ + path_recsize_ + file_size_recsize_ +
crc_record.size();
padded_edsize_ = round_up( edsize_ );
full_size_ = header_size + padded_edsize_;
}
// Returns the extended block size, or -1 if error. // Returns the extended block size, or -1 if error.
long long Extended::format_block( Resizable_buffer & rbuf ) const long long Extended::format_block( Resizable_buffer & rbuf ) const
@ -206,8 +206,12 @@ bool Extended::parse( const char * const buf, const unsigned long long edsize,
path_.assign( remove_leading_dotslash( path_.c_str() ) ); path_.assign( remove_leading_dotslash( path_.c_str() ) );
} }
else if( rest > 9 && std::memcmp( tail, "linkpath=", 9 ) == 0 ) else if( rest > 9 && std::memcmp( tail, "linkpath=", 9 ) == 0 )
{ if( linkpath_.size() && !permissive ) return false; {
linkpath_.assign( tail + 9, rest - 9 ); } if( linkpath_.size() && !permissive ) return false;
unsigned long long len = rest - 9;
while( len > 1 && tail[9+len-1] == '/' ) --len; // trailing '/'
linkpath_.assign( tail + 9, len );
}
else if( rest > 5 && std::memcmp( tail, "size=", 5 ) == 0 ) else if( rest > 5 && std::memcmp( tail, "size=", 5 ) == 0 )
{ {
if( file_size_ != 0 && !permissive ) return false; if( file_size_ != 0 && !permissive ) return false;
@ -235,3 +239,39 @@ bool Extended::parse( const char * const buf, const unsigned long long edsize,
} }
return true; return true;
} }
// if needed, copy linkpath, path and file_size from ustar header
void Extended::fill_from_ustar( const Tar_header header )
{
if( linkpath_.empty() ) // copy linkpath from ustar header
{
int len = 0;
while( len < linkname_l && header[linkname_o+len] ) ++len;
while( len > 1 && header[linkname_o+len-1] == '/' ) --len; // trailing '/'
if( len > 0 )
{
linkpath_.assign( (const char *)header + linkname_o, len );
full_size_ = -1;
}
}
if( path_.empty() ) // copy path from ustar header
{
char stored_name[prefix_l+1+name_l+1];
int len = 0;
while( len < prefix_l && header[prefix_o+len] )
{ stored_name[len] = header[prefix_o+len]; ++len; }
if( len && header[name_o] ) stored_name[len++] = '/';
for( int i = 0; i < name_l && header[name_o+i]; ++i )
{ stored_name[len] = header[name_o+i]; ++len; }
while( len > 0 && stored_name[len-1] == '/' ) --len; // trailing '/'
stored_name[len] = 0;
path( remove_leading_dotslash( stored_name ) );
}
const Typeflag typeflag = (Typeflag)header[typeflag_o];
if( file_size_ == 0 && // copy file_size from ustar header
( typeflag == tf_regular || typeflag == tf_hiperf ) )
file_size( parse_octal( header + size_o, size_l ) );
}

View file

@ -372,16 +372,21 @@ int compare_member( const int infd1, const Extended & extended,
{ show_file_diff( filename, "Gid differs" ); diff = true; } { show_file_diff( filename, "Gid differs" ); diff = true; }
} }
if( typeflag != tf_symlink ) if( typeflag != tf_symlink )
{
if( typeflag != tf_directory )
{ {
const time_t mtime = parse_octal( header + mtime_o, mtime_l ); // 33 bits const time_t mtime = parse_octal( header + mtime_o, mtime_l ); // 33 bits
if( mtime != st.st_mtime ) if( mtime != st.st_mtime )
{ show_file_diff( filename, "Mod time differs" ); diff = true; } { show_file_diff( filename, "Mod time differs" ); diff = true; }
}
if( ( typeflag == tf_regular || typeflag == tf_hiperf ) && if( ( typeflag == tf_regular || typeflag == tf_hiperf ) &&
(off_t)rest != st.st_size ) // don't compare contents (off_t)rest != st.st_size ) // don't compare contents
{ show_file_diff( filename, "Size differs" ); size_differs = true; } { show_file_diff( filename, "Size differs" ); size_differs = true; }
if( ( typeflag == tf_chardev || typeflag == tf_blockdev ) && if( ( typeflag == tf_chardev || typeflag == tf_blockdev ) &&
( parse_octal( header + devmajor_o, devmajor_l ) != major( st.st_rdev ) || ( parse_octal( header + devmajor_o, devmajor_l ) !=
parse_octal( header + devminor_o, devminor_l ) != minor( st.st_rdev ) ) ) (unsigned)major( st.st_rdev ) ||
parse_octal( header + devminor_o, devminor_l ) !=
(unsigned)minor( st.st_rdev ) ) )
{ show_file_diff( filename, "Device number differs" ); diff = true; } { show_file_diff( filename, "Device number differs" ); diff = true; }
} }
else else
@ -389,7 +394,12 @@ int compare_member( const int infd1, const Extended & extended,
char * const buf = new char[st.st_size+1]; char * const buf = new char[st.st_size+1];
long len = readlink( filename, buf, st.st_size ); long len = readlink( filename, buf, st.st_size );
bool e = ( len != st.st_size ); bool e = ( len != st.st_size );
if( !e ) { buf[len] = 0; if( extended.linkpath() != buf ) e = true; } if( !e )
{
while( len > 1 && buf[len-1] == '/' ) --len; // trailing '/'
buf[len] = 0;
if( extended.linkpath() != buf ) e = true;
}
delete[] buf; delete[] buf;
if( e ) { show_file_diff( filename, "Symlink differs" ); diff = true; } if( e ) { show_file_diff( filename, "Symlink differs" ); diff = true; }
} }
@ -451,9 +461,7 @@ int list_member( const int infd, const Extended & extended,
bool contains_dotdot( const char * const filename ) bool contains_dotdot( const char * const filename )
{ {
for( int i = 0; filename[i]; ++i ) for( int i = 0; filename[i]; ++i )
if( filename[i] == '.' && filename[i+1] == '.' && if( dotdot_at_i( filename, i ) ) return true;
( i == 0 || filename[i-1] == '/' ) &&
( filename[i+2] == 0 || filename[i+2] == '/' ) ) return true;
return false; return false;
} }
@ -763,50 +771,10 @@ int decode( const std::string & archive_name, const Arg_parser & parser,
} }
prev_extended = false; prev_extended = false;
if( extended.linkpath().empty() ) // copy linkpath from ustar header extended.fill_from_ustar( header ); // copy metadata from header
{
int len = 0;
while( len < linkname_l && header[linkname_o+len] ) ++len;
while( len > 1 && header[linkname_o+len-1] == '/' ) --len; // trailing '/'
if( len > 0 )
{
const uint8_t c = header[linkname_o+len]; header[linkname_o+len] = 0;
extended.linkpath( (const char *)header + linkname_o );
header[linkname_o+len] = c;
}
}
if( extended.path().empty() ) // copy path from ustar header
{
char stored_name[prefix_l+1+name_l+1];
int len = 0;
while( len < prefix_l && header[prefix_o+len] )
{ stored_name[len] = header[prefix_o+len]; ++len; }
if( len && header[name_o] ) stored_name[len++] = '/';
for( int i = 0; i < name_l && header[name_o+i]; ++i )
{ stored_name[len] = header[name_o+i]; ++len; }
while( len > 0 && stored_name[len-1] == '/' ) --len; // trailing '/'
stored_name[len] = 0;
extended.path( remove_leading_dotslash( stored_name ) );
}
const char * const filename = extended.path().c_str();
bool skip = filenames > 0;
if( skip )
for( int i = 0; i < parser.arguments(); ++i )
if( !parser.code( i ) && parser.argument( i ).size() )
{
const char * const name =
remove_leading_dotslash( parser.argument( i ).c_str() );
if( compare_prefix_dir( name, filename ) ||
compare_tslash( name, filename ) )
{ skip = false; name_pending[i] = false; break; }
}
if( extended.file_size() == 0 &&
( typeflag == tf_regular || typeflag == tf_hiperf ) )
extended.file_size( parse_octal( header + size_o, size_l ) );
const bool skip = check_skip_filename( parser, name_pending,
extended.path().c_str(), filenames );
if( skip ) if( skip )
retval = skip_member( infd, extended ); retval = skip_member( infd, extended );
else if( program_mode == m_list ) else if( program_mode == m_list )

View file

@ -145,13 +145,32 @@ void xbroadcast( pthread_cond_t * const cond )
} }
bool check_skip_filename( const Arg_parser & parser,
std::vector< char > & name_pending,
const char * const filename, const int filenames )
{
bool skip = filenames > 0;
if( skip )
for( int i = 0; i < parser.arguments(); ++i )
if( !parser.code( i ) && parser.argument( i ).size() )
{
const char * const name =
remove_leading_dotslash( parser.argument( i ).c_str() );
if( compare_prefix_dir( name, filename ) ||
compare_tslash( name, filename ) )
{ skip = false; name_pending[i] = false; break; }
}
return skip;
}
namespace { namespace {
struct Packet // member name and metadata or error message struct Packet // member name and metadata or error message
{ {
enum Status { ok, member_done, error }; enum Status { ok, member_done, error };
long member_id; // lzip member containing the header of this tar member long member_id; // lzip member containing the header of this tar member
std::string line; // member name and metadata ready to print std::string line; // member name and metadata ready to print, if any
Status status; Status status;
Packet( const long i, const char * const msg, const Status s = ok ) Packet( const long i, const char * const msg, const Status s = ok )
: member_id( i ), line( msg ), status( s ) {} : member_id( i ), line( msg ), status( s ) {}
@ -167,7 +186,7 @@ private:
long error_member_id; // first lzip member with error/misalign/eof long error_member_id; // first lzip member with error/misalign/eof
int deliver_worker_id; // worker queue currently delivering packets int deliver_worker_id; // worker queue currently delivering packets
int master_worker_id; // worker in charge if error/misalignment/eof int master_worker_id; // worker in charge if error/misalignment/eof
std::vector< std::queue< Packet * > > opacket_queues; std::vector< std::queue< const Packet * > > opacket_queues;
int num_working; // number of workers still running int num_working; // number of workers still running
const int num_workers; // number of workers const int num_workers; // number of workers
const unsigned out_slots; // max output packets per queue const unsigned out_slots; // max output packets per queue
@ -233,8 +252,9 @@ public:
xunlock( &omutex ); xunlock( &omutex );
} }
// collect a packet from a worker /* Collect a packet from a worker.
bool collect_packet( Packet * const opacket, const int worker_id ) If a packet is rejected, the worker must terminate. */
bool collect_packet( const Packet * const opacket, const int worker_id )
{ {
xlock( &omutex ); xlock( &omutex );
if( ( mastership_granted() && master_worker_id != worker_id ) || if( ( mastership_granted() && master_worker_id != worker_id ) ||
@ -249,12 +269,15 @@ public:
} }
/* Deliver a packet to muxer. /* Deliver a packet to muxer.
If packet.status == Packet::member_done, move to next queue. */ If packet.status == Packet::member_done, move to next queue.
Packet * deliver_packet() If packet.line.empty(), wait again (empty lzip member). */
const Packet * deliver_packet()
{ {
Packet * opacket = 0; const Packet * opacket = 0;
xlock( &omutex ); xlock( &omutex );
++ocheck_counter; ++ocheck_counter;
while( true )
{
while( opacket_queues[deliver_worker_id].empty() && num_working > 0 ) while( opacket_queues[deliver_worker_id].empty() && num_working > 0 )
{ {
++owait_counter; ++owait_counter;
@ -262,14 +285,15 @@ public:
xbroadcast( &check_master ); // mastership requested not yet granted xbroadcast( &check_master ); // mastership requested not yet granted
xwait( &oav_or_exit, &omutex ); xwait( &oav_or_exit, &omutex );
} }
if( !opacket_queues[deliver_worker_id].empty() ) if( opacket_queues[deliver_worker_id].empty() ) break;
{
opacket = opacket_queues[deliver_worker_id].front(); opacket = opacket_queues[deliver_worker_id].front();
opacket_queues[deliver_worker_id].pop(); opacket_queues[deliver_worker_id].pop();
if( opacket_queues[deliver_worker_id].size() + 1 == out_slots ) if( opacket_queues[deliver_worker_id].size() + 1 == out_slots )
xsignal( &slot_av[deliver_worker_id] ); xsignal( &slot_av[deliver_worker_id] );
if( opacket->status == Packet::member_done && !mastership_granted() ) if( opacket->status == Packet::member_done && !mastership_granted() )
{ if( ++deliver_worker_id >= num_workers ) deliver_worker_id = 0; } { if( ++deliver_worker_id >= num_workers ) deliver_worker_id = 0; }
if( !opacket->line.empty() ) break;
delete opacket; opacket = 0;
} }
xunlock( &omutex ); xunlock( &omutex );
return opacket; return opacket;
@ -349,14 +373,15 @@ int list_member_lz( LZ_Decoder * const decoder, const int infd,
if( data_rest < 0 ) // tar member exceeds lzip member end if( data_rest < 0 ) // tar member exceeds lzip member end
{ {
if( courier.request_mastership( member_id, worker_id ) ) master = true; if( courier.request_mastership( member_id, worker_id ) ) master = true;
else return 2; else { *msg = "tar member exceeds lzip member end"; return 2; }
} }
if( verbosity < 0 || skip ) rbuf()[0] = 0; if( verbosity < 0 || skip ) rbuf()[0] = 0;
else format_member_name( extended, header, rbuf, verbosity > 0 ); else format_member_name( extended, header, rbuf, verbosity > 0 );
Packet * const opacket = new Packet( member_id, rbuf(), const Packet * const opacket = new Packet( member_id, rbuf(),
data_rest ? Packet::ok : Packet::member_done ); data_rest ? Packet::ok : Packet::member_done );
courier.collect_packet( opacket, worker_id ); if( !courier.collect_packet( opacket, worker_id ) )
{ *msg = "other worker found an error"; return 1; }
if( !data_rest ) { data_pos = mdata_end; return 0; } if( !data_rest ) { data_pos = mdata_end; return 0; }
const unsigned bufsize = 32 * header_size; const unsigned bufsize = 32 * header_size;
@ -441,6 +466,12 @@ extern "C" void * tworker( void * arg )
long long data_end = mdata_end; long long data_end = mdata_end;
long long file_pos = lzip_index.mblock( i ).pos(); long long file_pos = lzip_index.mblock( i ).pos();
long long member_end = lzip_index.mblock( i ).end(); long long member_end = lzip_index.mblock( i ).end();
if( data_pos >= data_end ) // empty lzip member
{
const Packet * const opacket = new Packet( i, "", Packet::member_done );
if( !courier.collect_packet( opacket, worker_id ) ) goto done;
continue;
}
Extended extended; // metadata from extended records Extended extended; // metadata from extended records
int retval = 0; int retval = 0;
@ -459,7 +490,7 @@ extern "C" void * tworker( void * arg )
master = true; master = true;
if( ret > 0 ) if( ret > 0 )
{ {
Packet * const opacket = new Packet( i, msg, Packet::error ); const Packet * const opacket = new Packet( i, msg, Packet::error );
courier.collect_packet( opacket, worker_id ); courier.collect_packet( opacket, worker_id );
goto done; goto done;
} }
@ -472,7 +503,7 @@ extern "C" void * tworker( void * arg )
if( !courier.request_mastership( i, worker_id ) ) goto done; if( !courier.request_mastership( i, worker_id ) ) goto done;
master = true; master = true;
if( block_is_zero( header, header_size ) ) break; // EOF if( block_is_zero( header, header_size ) ) break; // EOF
Packet * const opacket = new Packet( i, const Packet * const opacket = new Packet( i,
( data_pos > header_size ) ? "Corrupt or invalid header." : ( data_pos > header_size ) ? "Corrupt or invalid header." :
"This does not look like a POSIX tar.lz archive.", Packet::error ); "This does not look like a POSIX tar.lz archive.", Packet::error );
courier.collect_packet( opacket, worker_id ); courier.collect_packet( opacket, worker_id );
@ -495,9 +526,9 @@ extern "C" void * tworker( void * arg )
if( ret > 0 ) if( ret > 0 )
{ {
if( !msg ) msg = "Error in global extended records."; if( !msg ) msg = "Error in global extended records.";
Packet * const opacket = new Packet( i, msg, Packet::error ); const Packet * const opacket = new Packet( i, msg, Packet::error );
courier.collect_packet( opacket, worker_id ); courier.collect_packet( opacket, worker_id );
if( ret == 2 ) goto done; goto done;
} }
// member_end exceeded, process rest of file // member_end exceeded, process rest of file
else { data_end = lzip_index.udata_size(); member_end = cdata_size; } else { data_end = lzip_index.udata_size(); member_end = cdata_size; }
@ -521,10 +552,9 @@ extern "C" void * tworker( void * arg )
if( ret > 0 ) if( ret > 0 )
{ {
if( !msg ) msg = "Error in extended records."; if( !msg ) msg = "Error in extended records.";
Packet * const opacket = new Packet( i, msg, Packet::error ); const Packet * const opacket = new Packet( i, msg, Packet::error );
courier.collect_packet( opacket, worker_id ); courier.collect_packet( opacket, worker_id );
extended.reset(); goto done;
if( ret == 2 ) goto done;
} }
// member_end exceeded, process rest of file // member_end exceeded, process rest of file
else { data_end = lzip_index.udata_size(); member_end = cdata_size; } else { data_end = lzip_index.udata_size(); member_end = cdata_size; }
@ -534,70 +564,33 @@ extern "C" void * tworker( void * arg )
} }
prev_extended = false; prev_extended = false;
if( extended.linkpath().empty() ) // copy linkpath from ustar header extended.fill_from_ustar( header ); // copy metadata from header
{
int len = 0;
while( len < linkname_l && header[linkname_o+len] ) ++len;
while( len > 1 && header[linkname_o+len-1] == '/' ) --len; // trailing '/'
if( len > 0 )
{
const uint8_t c = header[linkname_o+len]; header[linkname_o+len] = 0;
extended.linkpath( (const char *)header + linkname_o );
header[linkname_o+len] = c;
}
}
if( extended.path().empty() ) // copy path from ustar header const bool skip = check_skip_filename( parser, name_pending,
{ extended.path().c_str(), filenames );
char stored_name[prefix_l+1+name_l+1];
int len = 0;
while( len < prefix_l && header[prefix_o+len] )
{ stored_name[len] = header[prefix_o+len]; ++len; }
if( len && header[name_o] ) stored_name[len++] = '/';
for( int i = 0; i < name_l && header[name_o+i]; ++i )
{ stored_name[len] = header[name_o+i]; ++len; }
while( len > 0 && stored_name[len-1] == '/' ) --len; // trailing '/'
stored_name[len] = 0;
extended.path( remove_leading_dotslash( stored_name ) );
}
const char * const filename = extended.path().c_str();
bool skip = filenames > 0; retval = list_member_lz( decoder, infd, file_pos, member_end, cdata_size,
if( skip ) data_pos, mdata_end, courier, extended,
for( int i = 0; i < parser.arguments(); ++i ) header, rbuf, i, worker_id, &msg, skip );
if( !parser.code( i ) && parser.argument( i ).size() )
{
const char * const name =
remove_leading_dotslash( parser.argument( i ).c_str() );
if( compare_prefix_dir( name, filename ) ||
compare_tslash( name, filename ) )
{ skip = false; name_pending[i] = false; break; }
}
if( extended.file_size() == 0 &&
( typeflag == tf_regular || typeflag == tf_hiperf ) )
extended.file_size( parse_octal( header + size_o, size_l ) );
retval = list_member_lz( decoder, infd, file_pos, member_end,
cdata_size, data_pos, mdata_end, courier,
extended, header, rbuf, i, worker_id, &msg, skip );
extended.reset(); extended.reset();
if( retval < 0 ) // member_end exceeded, process rest of file if( retval < 0 ) // member_end exceeded, process rest of file
{ master = true; { master = true;
data_end = lzip_index.udata_size(); member_end = cdata_size; } data_end = lzip_index.udata_size(); member_end = cdata_size; }
else if( retval > 0 ) else if( retval > 0 )
{ show_error( msg ); {
show_error( "Error is not recoverable: exiting now." ); const Packet * const opacket = new Packet( i, msg, Packet::error );
cleanup_and_fail( 2 ); } courier.collect_packet( opacket, worker_id );
goto done;
} }
} }
}
done:
if( LZ_decompress_close( decoder ) < 0 ) if( LZ_decompress_close( decoder ) < 0 )
{ {
Packet * const opacket = new Packet( lzip_index.members(), const Packet * const opacket = new Packet( lzip_index.members(),
"LZ_decompress_close failed.", Packet::error ); "LZ_decompress_close failed.", Packet::error );
courier.collect_packet( opacket, worker_id ); courier.collect_packet( opacket, worker_id );
} }
done:
courier.worker_finished(); courier.worker_finished();
return 0; return 0;
} }
@ -609,14 +602,13 @@ void muxer( Packet_courier & courier )
{ {
while( true ) while( true )
{ {
Packet * const opacket = courier.deliver_packet(); const Packet * const opacket = courier.deliver_packet();
if( !opacket ) break; // queue is empty. all workers exited if( !opacket ) break; // queue is empty. all workers exited
if( opacket->status == Packet::error ) if( opacket->status == Packet::error )
{ show_error( opacket->line.c_str() ); cleanup_and_fail( 2 ); } { show_error( opacket->line.c_str() ); cleanup_and_fail( 2 ); }
if( opacket->line.size() ) if( opacket->line.size() )
{ std::fputs( opacket->line.c_str(), stdout ); { std::fputs( opacket->line.c_str(), stdout ); std::fflush( stdout ); }
std::fflush( stdout ); }
delete opacket; delete opacket;
} }
if( !courier.mastership_granted() ) // no worker found EOF blocks if( !courier.mastership_granted() ) // no worker found EOF blocks

20
tarlz.h
View file

@ -62,6 +62,14 @@ inline unsigned long long round_up( const unsigned long long size )
} }
inline bool dotdot_at_i( const char * const filename, const int i )
{
return ( filename[i] == '.' && filename[i+1] == '.' &&
( i == 0 || filename[i-1] == '/' ) &&
( filename[i+2] == 0 || filename[i+2] == '/' ) );
}
enum { initial_line_length = 1000 }; // must be >= 87 for format_member_name enum { initial_line_length = 1000 }; // must be >= 87 for format_member_name
class Resizable_buffer class Resizable_buffer
@ -141,6 +149,7 @@ public:
long long format_block( Resizable_buffer & rbuf ) const; long long format_block( Resizable_buffer & rbuf ) const;
bool parse( const char * const buf, const unsigned long long edsize, bool parse( const char * const buf, const unsigned long long edsize,
const bool permissive ); const bool permissive );
void fill_from_ustar( const Tar_header header );
}; };
@ -300,6 +309,8 @@ extern int cl_owner;
extern int cl_group; extern int cl_group;
extern int cl_data_size; extern int cl_data_size;
extern Solidity solidity; extern Solidity solidity;
bool writeblock_wrapper( const int outfd, const uint8_t * const buffer,
const int size );
const char * remove_leading_dotslash( const char * const filename, const char * remove_leading_dotslash( const char * const filename,
const bool dotdot = false ); const bool dotdot = false );
bool fill_headers( const char * const filename, Extended & extended, bool fill_headers( const char * const filename, Extended & extended,
@ -320,9 +331,9 @@ int encode( const std::string & archive_name, const Arg_parser & parser,
const int debug_level, const bool append ); const int debug_level, const bool append );
// defined in create_lz.cc // defined in create_lz.cc
int encode_lz( const char * const archive_name, const Arg_parser & parser, int encode_lz( const Arg_parser & parser, const int dictionary_size,
const int dictionary_size, const int match_len_limit, const int match_len_limit, const int num_workers,
const int num_workers, const int outfd, const int debug_level ); const int outfd, const int debug_level );
// defined in extract.cc // defined in extract.cc
enum Program_mode { m_none, m_append, m_concatenate, m_create, m_diff, enum Program_mode { m_none, m_append, m_concatenate, m_create, m_diff,
@ -351,6 +362,9 @@ void xunlock( pthread_mutex_t * const mutex );
void xwait( pthread_cond_t * const cond, pthread_mutex_t * const mutex ); void xwait( pthread_cond_t * const cond, pthread_mutex_t * const mutex );
void xsignal( pthread_cond_t * const cond ); void xsignal( pthread_cond_t * const cond );
void xbroadcast( pthread_cond_t * const cond ); void xbroadcast( pthread_cond_t * const cond );
bool check_skip_filename( const Arg_parser & parser,
std::vector< char > & name_pending,
const char * const filename, const int filenames );
class Lzip_index; class Lzip_index;
int list_lz( const Arg_parser & parser, std::vector< char > & name_pending, int list_lz( const Arg_parser & parser, std::vector< char > & name_pending,
const Lzip_index & lzip_index, const int filenames, const Lzip_index & lzip_index, const int filenames,

View file

@ -68,6 +68,7 @@ lzlib_1_11() { [ ${lwarn} = 0 ] &&
# t155.tar[.lz]: directory + links + file + eof, all with 155 char names # t155.tar[.lz]: directory + links + file + eof, all with 155 char names
# tar_in_tlz1.tar.lz 2 members (test.txt.tar test3.tar) 3 lzip members # tar_in_tlz1.tar.lz 2 members (test.txt.tar test3.tar) 3 lzip members
# tar_in_tlz2.tar.lz 2 members (test.txt.tar test3.tar) 5 lzip members # tar_in_tlz2.tar.lz 2 members (test.txt.tar test3.tar) 5 lzip members
# ts_in_link.tar.lz: 4 symbolic links (link[1-4]) to / /dir/ dir/ dir(107/)
# test_bad1.tar.lz: truncated at offset 6000 (of 7495) # test_bad1.tar.lz: truncated at offset 6000 (of 7495)
# test_bad2.tar.lz: byte at offset 6000 changed from 0x56 to 0x46 # test_bad2.tar.lz: byte at offset 6000 changed from 0x56 to 0x46
# test3.tar: 3 members (foo bar baz) + 2 zeroed 512-byte blocks # test3.tar: 3 members (foo bar baz) + 2 zeroed 512-byte blocks
@ -89,6 +90,8 @@ lzlib_1_11() { [ ${lwarn} = 0 ] &&
# test3_eof1.tar.lz: test3.tar.lz without eof blocks # test3_eof1.tar.lz: test3.tar.lz without eof blocks
# test3_eof2.tar.lz: test3.tar.lz with only one eof block # test3_eof2.tar.lz: test3.tar.lz with only one eof block
# test3_eof3.tar.lz: test3.tar.lz with one zeroed block between foo and bar # test3_eof3.tar.lz: test3.tar.lz with one zeroed block between foo and bar
# test3_em?.tar.lz: test3.tar.lz with one empty lzip member at each position
# test3_em6.tar.lz: test3.tar.lz preceded by four empty lzip members
# tlz_in_tar1.tar: 1 member (test3.tar.lz) first magic damaged # tlz_in_tar1.tar: 1 member (test3.tar.lz) first magic damaged
# tlz_in_tar2.tar: 2 members (foo test3.tar.lz) first magic damaged # tlz_in_tar2.tar: 2 members (foo test3.tar.lz) first magic damaged
# ug32chars.tar.lz: 1 member (foo) with 32-character owner and group names # ug32chars.tar.lz: 1 member (foo) with 32-character owner and group names
@ -263,7 +266,8 @@ cmp cfoo foo || test_failed $LINENO
[ ! -e bar ] || test_failed $LINENO [ ! -e bar ] || test_failed $LINENO
[ ! -e baz ] || test_failed $LINENO [ ! -e baz ] || test_failed $LINENO
rm -f foo bar baz || framework_failure rm -f foo bar baz || framework_failure
#
# test --list and --extract tar in tar.lz
for i in "${tarint1_lz}" "${tarint2_lz}" ; do for i in "${tarint1_lz}" "${tarint2_lz}" ; do
for j in 0 2 6 ; do for j in 0 2 6 ; do
"${TARLZ}" -tf "$i" --threads=$j > out$j || "${TARLZ}" -tf "$i" --threads=$j > out$j ||
@ -284,6 +288,31 @@ for i in "${tarint1_lz}" "${tarint2_lz}" ; do
rm -f test.txt.tar test3.tar || framework_failure rm -f test.txt.tar test3.tar || framework_failure
done done
# test --list and --extract with empty lzip members
for i in 1 2 3 4 5 6 ; do
for j in 0 2 6 ; do
"${TARLZ}" -tf "${testdir}"/test3_em${i}.tar.lz --threads=$j \
> out$j || test_failed $LINENO "$i $j"
"${TARLZ}" -tvf "${testdir}"/test3_em${i}.tar.lz --threads=$j \
> outv$j || test_failed $LINENO "$i $j"
done
diff -u out0 out2 || test_failed $LINENO $i
diff -u out0 out6 || test_failed $LINENO $i
diff -u out2 out6 || test_failed $LINENO $i
diff -u outv0 outv2 || test_failed $LINENO $i
diff -u outv0 outv6 || test_failed $LINENO $i
diff -u outv2 outv6 || test_failed $LINENO $i
rm -f out0 out2 out6 outv0 outv2 outv6 || framework_failure
for j in 0 2 6 ; do
"${TARLZ}" -xf "${testdir}"/test3_em${i}.tar.lz --threads=$j ||
test_failed $LINENO "$i $j"
cmp cfoo foo || test_failed $LINENO "$i $j"
cmp cbar bar || test_failed $LINENO "$i $j"
cmp cbaz baz || test_failed $LINENO "$i $j"
rm -f foo bar baz || framework_failure
done
done
# test --concatenate # test --concatenate
cat "${in_tar_lz}" > out.tar.lz || framework_failure cat "${in_tar_lz}" > out.tar.lz || framework_failure
"${TARLZ}" -Af out.tar.lz "${test3_lz}" || test_failed $LINENO "${TARLZ}" -Af out.tar.lz "${test3_lz}" || test_failed $LINENO
@ -490,12 +519,16 @@ if ln dummy_file dummy_link 2> /dev/null &&
ln dir1/dir2/dir3/in dir1/dir2/dir3/"${name_100}" || framework_failure ln dir1/dir2/dir3/in dir1/dir2/dir3/"${name_100}" || framework_failure
ln dir1/dir2/dir3/in "${path_100}" || framework_failure ln dir1/dir2/dir3/in "${path_100}" || framework_failure
ln dir1/dir2/dir3/in "${path_106}" || framework_failure ln dir1/dir2/dir3/in "${path_106}" || framework_failure
ln -s dir2/ dir1/dir2_link || framework_failure
ln -s in dir1/dir2/dir3/link || framework_failure ln -s in dir1/dir2/dir3/link || framework_failure
ln -s "${name_100}" dir1/dir2/dir3/link_100 || framework_failure ln -s "${name_100}" dir1/dir2/dir3/link_100 || framework_failure
"${TARLZ}" -0 -cf out.tar.lz dir1 || test_failed $LINENO "${TARLZ}" -0 -cf out.tar.lz dir1 || test_failed $LINENO
"${TARLZ}" -df out.tar.lz || test_failed $LINENO
rm -rf dir1 || framework_failure rm -rf dir1 || framework_failure
"${TARLZ}" -xf out.tar.lz || test_failed $LINENO "${TARLZ}" -xf out.tar.lz || test_failed $LINENO
"${TARLZ}" -df out.tar.lz || test_failed $LINENO
cmp "${in}" dir1/dir2/dir3/in || test_failed $LINENO cmp "${in}" dir1/dir2/dir3/in || test_failed $LINENO
cmp "${in}" dir1/dir2_link/dir3/in || test_failed $LINENO
cmp "${in}" dir1/dir2/dir3/"${name_100}" || test_failed $LINENO cmp "${in}" dir1/dir2/dir3/"${name_100}" || test_failed $LINENO
cmp "${in}" "${path_100}" || test_failed $LINENO cmp "${in}" "${path_100}" || test_failed $LINENO
cmp "${in}" "${path_106}" || test_failed $LINENO cmp "${in}" "${path_106}" || test_failed $LINENO
@ -511,6 +544,10 @@ if ln dummy_file dummy_link 2> /dev/null &&
"${TARLZ}" -0 -q -c ../tmp/dir1 | "${TARLZ}" -x || test_failed $LINENO "${TARLZ}" -0 -q -c ../tmp/dir1 | "${TARLZ}" -x || test_failed $LINENO
diff -ru tmp/dir1 dir1 || test_failed $LINENO diff -ru tmp/dir1 dir1 || test_failed $LINENO
rm -rf tmp/dir1 dir1 || framework_failure rm -rf tmp/dir1 dir1 || framework_failure
"${TARLZ}" -xf "${testdir}"/ts_in_link.tar.lz || test_failed $LINENO
"${TARLZ}" -df "${testdir}"/ts_in_link.tar.lz --ignore-ids ||
test_failed $LINENO
rm -f link1 link2 link3 link4 || framework_failure
else else
printf "\nwarning: skipping link test: 'ln' does not work on your system." printf "\nwarning: skipping link test: 'ln' does not work on your system."
fi fi

BIN
testsuite/test3_em1.tar.lz Normal file

Binary file not shown.

BIN
testsuite/test3_em2.tar.lz Normal file

Binary file not shown.

BIN
testsuite/test3_em3.tar.lz Normal file

Binary file not shown.

BIN
testsuite/test3_em4.tar.lz Normal file

Binary file not shown.

BIN
testsuite/test3_em5.tar.lz Normal file

Binary file not shown.

BIN
testsuite/test3_em6.tar.lz Normal file

Binary file not shown.

BIN
testsuite/ts_in_link.tar.lz Normal file

Binary file not shown.