From 5e422e043e36fcd7ecbb3a27c9bd594ef5251c89 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Tue, 4 Mar 2025 07:39:30 +0100 Subject: [PATCH] Merging upstream version 0.27. Signed-off-by: Daniel Baumann --- ChangeLog | 45 +- INSTALL | 12 +- Makefile.in | 18 +- NEWS | 36 +- README | 4 +- archive_reader.cc | 20 +- archive_reader.h | 11 +- arg_parser.cc | 2 +- arg_parser.h | 2 +- common.cc | 2 +- common_decode.cc | 110 +-- common_mutex.cc | 6 +- common_mutex.h | 2 +- compress.cc | 10 +- configure | 6 +- create.cc | 64 +- create.h | 7 +- create_lz.cc | 28 +- decode.cc | 41 +- decode.h | 6 +- decode_lz.cc | 81 ++- delete.cc | 2 +- delete_lz.cc | 2 +- doc/tarlz.1 | 8 +- doc/tarlz.info | 240 ++++--- doc/tarlz.texi | 174 +++-- exclude.cc | 2 +- extended.cc | 41 +- lzip_index.cc | 2 +- lzip_index.h | 2 +- main.cc | 42 +- tarlz.h | 20 +- testsuite/check.sh | 658 ++++++++++-------- .../{eoa_blocks.tar.lz => eoa_blocks.lz} | Bin testsuite/eoa_blocks.tar | Bin 1024 -> 0 bytes testsuite/t155.tar | Bin 9216 -> 0 bytes testsuite/t155_fv1.tar | Bin 10240 -> 0 bytes testsuite/t155_fv2.tar | Bin 10240 -> 0 bytes testsuite/t155_fv3.tar | Bin 10240 -> 0 bytes testsuite/t155_fv7.tar.lz | Bin 0 -> 917 bytes testsuite/test.txt.lz | Bin 7341 -> 0 bytes testsuite/test.txt.tar | Bin 37888 -> 0 bytes testsuite/test.txt.tar.lz | Bin 7459 -> 7456 bytes testsuite/test3.tar | Bin 4096 -> 0 bytes testsuite/test3.tar.lz | Bin 356 -> 343 bytes testsuite/test3_bad1.tar.lz | Bin 356 -> 343 bytes testsuite/test3_bad2.tar.lz | Bin 356 -> 343 bytes testsuite/test3_bad3.tar.lz | Bin 356 -> 343 bytes testsuite/test3_bad4.tar.lz | Bin 356 -> 343 bytes testsuite/test3_bad5.tar.lz | Bin 356 -> 343 bytes testsuite/test3_bad6.tar.lz | Bin 866 -> 853 bytes testsuite/test3_crc.tar.lz | Bin 0 -> 866 bytes testsuite/test3_dir.tar | Bin 4096 -> 0 bytes testsuite/test3_eoa1.tar | Bin 3072 -> 0 bytes testsuite/test3_eoa1.tar.lz | Bin 312 -> 299 bytes testsuite/test3_eoa2.tar | Bin 3584 -> 0 bytes testsuite/test3_eoa2.tar.lz | Bin 352 -> 339 bytes testsuite/test3_eoa3.tar | Bin 4608 -> 0 bytes testsuite/test3_eoa3.tar.lz | Bin 396 -> 383 bytes testsuite/test3_eoa4.tar | Bin 4096 -> 0 bytes testsuite/test3_eoa4.tar.lz | Bin 535 -> 522 bytes testsuite/test3_eoa5.tar.lz | Bin 535 -> 522 bytes testsuite/test3_gh1.tar | Bin 5120 -> 0 bytes testsuite/test3_gh1.tar.lz | Bin 574 -> 561 bytes testsuite/test3_gh2.tar | Bin 5120 -> 0 bytes testsuite/test3_gh2.tar.lz | Bin 607 -> 561 bytes testsuite/test3_gh3.tar | Bin 5120 -> 0 bytes testsuite/test3_gh3.tar.lz | Bin 645 -> 561 bytes testsuite/test3_gh4.tar | Bin 5120 -> 0 bytes testsuite/test3_gh4.tar.lz | Bin 795 -> 561 bytes testsuite/test3_gh5.tar.lz | Bin 574 -> 512 bytes testsuite/test3_gh6.tar.lz | Bin 521 -> 598 bytes testsuite/test3_gh7.tar.lz | Bin 0 -> 636 bytes testsuite/test3_gh8.tar.lz | Bin 0 -> 786 bytes testsuite/test3_nn.tar | Bin 4096 -> 0 bytes testsuite/test3_nn.tar.lz | Bin 350 -> 341 bytes testsuite/test3_sm1.tar.lz | Bin 579 -> 566 bytes testsuite/test3_sm2.tar.lz | Bin 612 -> 603 bytes testsuite/test3_sm3.tar.lz | Bin 650 -> 641 bytes testsuite/test3_sm4.tar.lz | Bin 798 -> 789 bytes testsuite/test3_uk.tar.lz | Bin 0 -> 938 bytes testsuite/tlz_in_tar1.tar | Bin 2048 -> 2048 bytes testsuite/tlz_in_tar2.tar | Bin 3072 -> 3072 bytes 83 files changed, 980 insertions(+), 726 deletions(-) rename testsuite/{eoa_blocks.tar.lz => eoa_blocks.lz} (100%) delete mode 100644 testsuite/eoa_blocks.tar delete mode 100644 testsuite/t155.tar delete mode 100644 testsuite/t155_fv1.tar delete mode 100644 testsuite/t155_fv2.tar delete mode 100644 testsuite/t155_fv3.tar create mode 100644 testsuite/t155_fv7.tar.lz delete mode 100644 testsuite/test.txt.lz delete mode 100644 testsuite/test.txt.tar delete mode 100644 testsuite/test3.tar create mode 100644 testsuite/test3_crc.tar.lz delete mode 100644 testsuite/test3_dir.tar delete mode 100644 testsuite/test3_eoa1.tar delete mode 100644 testsuite/test3_eoa2.tar delete mode 100644 testsuite/test3_eoa3.tar delete mode 100644 testsuite/test3_eoa4.tar delete mode 100644 testsuite/test3_gh1.tar delete mode 100644 testsuite/test3_gh2.tar delete mode 100644 testsuite/test3_gh3.tar delete mode 100644 testsuite/test3_gh4.tar create mode 100644 testsuite/test3_gh7.tar.lz create mode 100644 testsuite/test3_gh8.tar.lz delete mode 100644 testsuite/test3_nn.tar create mode 100644 testsuite/test3_uk.tar.lz diff --git a/ChangeLog b/ChangeLog index 541a369..2249712 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,20 @@ +2025-02-28 Antonio Diaz Diaz + + * Version 0.27 released. + * common_decode.cc (format_member_name): Print seconds since epoch + if date is out of range. Use at least 4 digits to print years. + Print typeflag after the member name if unknown file type. + (make_dirs): stat last dir before trying to create directories. + * decode.cc (skip_warn): Diagnose a corrupt tar header. + * extended.cc (Extended::parse): Diagnose a CRC mismatch. + New argument 'msg_vecp' for multi-threaded diagnostics. + * Many small fixes and improvements to the code and the manual. + * tarlz.texi: New chapter 'Creating backups safely'. + (Suggested by Aren Tyr). + * check.sh: Require lzip. Create .tar files from .tar.lz files. + Limit '--mtime' test to safe dates. (Reported by Aren Tyr). + * testsuite: Add 5 new test files. + 2024-12-07 Antonio Diaz Diaz * Version 0.26 released. @@ -73,8 +90,7 @@ * Lzlib 1.12 or newer is now required. * decode.cc (decode): Skip members without name except when listing. decode_lz.cc (dworker): Likewise. (Reported by Florian Schmaus). - * New options '-z, --compress' and '-o, --output'. - * New option '--warn-newer'. + * New options '-z, --compress', '-o, --output', and '--warn-newer'. * tarlz.texi (Invoking tarlz): Document concatenation to stdout. * check.sh: Fix the '--diff' test on OS/2. (Reported by Elbert Pol). @@ -97,10 +113,10 @@ 2020-07-30 Antonio Diaz Diaz * Version 0.17 released. - * New option '--mtime'. - * New option '-p, --preserve-permissions'. + * New options '--mtime' and '-p, --preserve-permissions'. * Implement multi-threaded '-d, --diff'. * list_lz.cc: Rename to decode_lz.cc. + (decode_lz): Limit num_workers to number of members. * main.cc (main): Report an error if a file name is empty or if the archive is specified more than once. * lzip_index.cc: Improve messages for corruption in last header. @@ -125,8 +141,7 @@ 2019-03-12 Antonio Diaz Diaz * Version 0.14 released. - * New option '--exclude'. - * New option '-h, --dereference'. + * New options '--exclude' and '-h, --dereference'. * Short option name '-h' no longer means '--help'. * create.cc: Implement '-A, --concatenate' and '-r, --append' to uncompressed archives and to standard output. @@ -145,8 +160,7 @@ * create.cc (fill_headers): Fix use of st_rdev instead of st_dev. * Save just numerical uid/gid if user or group not in database. * extract.cc (format_member_name): Print devmajor and devminor. - * New option '-d, --diff'. - * New option '--ignore-ids'. + * New options '-d, --diff' and '--ignore-ids'. * extract.cc: Fast '-t, --list' on seekable uncompressed archives. 2019-02-13 Antonio Diaz Diaz @@ -161,8 +175,7 @@ 2019-01-31 Antonio Diaz Diaz * Version 0.10 released. - * New option '--bsolid'. - * New option '-B, --data-size'. + * New options '--bsolid' and '-B, --data-size'. * create.cc: Set ustar name to zero if extended header is used. 2019-01-22 Antonio Diaz Diaz @@ -185,8 +198,7 @@ 2018-11-23 Antonio Diaz Diaz * Version 0.7 released. - * New option '--keep-damaged'. - * New option '--no-solid'. + * New options '--keep-damaged' and '--no-solid'. * create.cc (archive_write): Minimize dictionary size. Detect and skip archive in '-A', '-c', and '-r'. * main.cc (show_version): Show the version of lzlib being used. @@ -195,7 +207,7 @@ * Version 0.6 released. * New option '-A, --concatenate'. - * Option '--ignore-crc' replaced with '--missing-crc'. + * Replace option '--ignore-crc' with '--missing-crc'. * create.cc (add_member): Check that uid, gid, mtime, devmajor, and devminor are in ustar range. * configure: Accept appending to CXXFLAGS; 'CXXFLAGS+=OPTIONS'. @@ -220,11 +232,10 @@ * Version 0.3 released. * Rename project to 'tarlz' from 'pmtar' (Poor Man's Tar). - * New option '-C, --directory'. - * Implement lzip compression of members at archive creation. - * New option '-r, --append'. + * New options '-C, --directory' and '-r, --append'. * New options '--owner' and '--group'. * New options '--asolid', '--dsolid', and '--solid'. + * Implement lzip compression of members at archive creation. * Implement file appending to compressed archive. * Implement transparent decompression of the archive. * Implement skipping over damaged (un)compressed members. @@ -242,7 +253,7 @@ * Version 0.1 released. -Copyright (C) 2013-2024 Antonio Diaz Diaz. +Copyright (C) 2013-2025 Antonio Diaz Diaz. This file is a collection of facts, and thus it is not copyrightable, but just in case, you have unlimited permission to copy, distribute, and modify it. diff --git a/INSTALL b/INSTALL index 4de87a7..24f9e51 100644 --- a/INSTALL +++ b/INSTALL @@ -4,10 +4,12 @@ You will need a C++98 compiler with support for 'long long', and the compression library lzlib installed. (gcc 3.3.6 or newer is recommended). I use gcc 6.1.0 and 3.3.6, but the code should compile with any standards compliant compiler. -Gcc is available at http://gcc.gnu.org. -Lzlib is available at http://www.nongnu.org/lzip/lzlib.html. - Lzlib must be version 1.12 or newer. +Gcc is available at http://gcc.gnu.org +Lzlib is available at http://www.nongnu.org/lzip/lzlib.html + +Lzip is required to run the tests. +Lzip is available at http://www.nongnu.org/lzip/lzip.html The operating system must allow signal handlers read access to objects with static storage duration so that the cleanup handler for Control-C can delete @@ -18,6 +20,8 @@ Procedure --------- 1. Unpack the archive if you have not done so already: + tarlz -xf tarlz[version].tar.lz +or tar -xf tarlz[version].tar.lz or lzip -cd tarlz[version].tar.lz | tar -xf - @@ -74,7 +78,7 @@ After running 'configure', you can run 'make' and 'make install' as explained above. -Copyright (C) 2013-2024 Antonio Diaz Diaz. +Copyright (C) 2013-2025 Antonio Diaz Diaz. This file is free documentation: you have unlimited permission to copy, distribute, and modify it. diff --git a/Makefile.in b/Makefile.in index a9f27c4..75183d5 100644 --- a/Makefile.in +++ b/Makefile.in @@ -135,42 +135,34 @@ dist : doc $(DISTNAME)/*.cc \ $(DISTNAME)/testsuite/check.sh \ $(DISTNAME)/testsuite/test.txt \ - $(DISTNAME)/testsuite/test.txt.tar \ $(DISTNAME)/testsuite/test_bad1.txt.tar \ $(DISTNAME)/testsuite/test_bad[12].txt \ $(DISTNAME)/testsuite/rfoo \ $(DISTNAME)/testsuite/rbar \ $(DISTNAME)/testsuite/rbaz \ - $(DISTNAME)/testsuite/test3.tar \ - $(DISTNAME)/testsuite/test3_nn.tar \ - $(DISTNAME)/testsuite/test3_eoa[1-4].tar \ - $(DISTNAME)/testsuite/test3_gh[1-4].tar \ $(DISTNAME)/testsuite/test3_bad[1-5].tar \ - $(DISTNAME)/testsuite/test3_dir.tar \ - $(DISTNAME)/testsuite/t155.tar \ - $(DISTNAME)/testsuite/t155_fv[1-3].tar \ - $(DISTNAME)/testsuite/eoa_blocks.tar \ $(DISTNAME)/testsuite/em.lz \ - $(DISTNAME)/testsuite/test.txt.lz \ $(DISTNAME)/testsuite/test.txt.tar.lz \ $(DISTNAME)/testsuite/test_bad[12].txt.tar.lz \ $(DISTNAME)/testsuite/test3.tar.lz \ $(DISTNAME)/testsuite/test3_eoa[1-5].tar.lz \ - $(DISTNAME)/testsuite/test3_gh[1-6].tar.lz \ + $(DISTNAME)/testsuite/test3_gh[1-8].tar.lz \ $(DISTNAME)/testsuite/test3_nn.tar.lz \ $(DISTNAME)/testsuite/test3_sm[1-4].tar.lz \ $(DISTNAME)/testsuite/test3_bad[1-6].tar.lz \ + $(DISTNAME)/testsuite/test3_crc.tar.lz \ + $(DISTNAME)/testsuite/test3_uk.tar.lz \ $(DISTNAME)/testsuite/test3_dir.tar.lz \ $(DISTNAME)/testsuite/test3_dot.tar.lz \ $(DISTNAME)/testsuite/tar_in_tlz[12].tar.lz \ $(DISTNAME)/testsuite/tlz_in_tar[12].tar \ $(DISTNAME)/testsuite/ts_in_link.tar.lz \ $(DISTNAME)/testsuite/t155.tar.lz \ - $(DISTNAME)/testsuite/t155_fv[1-6].tar.lz \ + $(DISTNAME)/testsuite/t155_fv[1-7].tar.lz \ $(DISTNAME)/testsuite/dotdot[1-5].tar.lz \ $(DISTNAME)/testsuite/ug32767.tar.lz \ $(DISTNAME)/testsuite/ug32chars.tar.lz \ - $(DISTNAME)/testsuite/eoa_blocks.tar.lz + $(DISTNAME)/testsuite/eoa_blocks.lz rm -f $(DISTNAME) clean : diff --git a/NEWS b/NEWS index e2ff75a..de882ae 100644 --- a/NEWS +++ b/NEWS @@ -1,13 +1,33 @@ -Changes in version 0.26: +Changes in version 0.27: -tarlz now exits with error status 2 if any empty lzip member is found in a -multimember compressed archive. +tarlz now prints seconds since epoch if a file date is out of range. -Scalability of parallel compressed creation and decoding has been increased. +tarlz now uses at least 4 digits to print years. -A diagnostic message for read error has been improved. +'tarlz -tv' now prints the value of typeflag after the member name for +unknown file types. -The chapter 'Syntax of command-line arguments' has been added to the manual. +tarlz now prints a diagnostic when it finds a corrupt tar header (or random +data where a tar header is expected). -'make check' now skips time stamps out of range or not recognized by system -tools. (Reported by J Dean). +tarlz now diagnoses CRC mismatches in extended records separately. + +Multi-threaded decoding now prints diagnostics about CRC mismatches and +unknown keywords in extended records in the correct order. + +Many small fixes and improvements have been made to the code and the manual. + +The chapter 'Creating backups safely' has been added to the manual. +(Suggested by Aren Tyr). + +Lzip is now required to run the tests because I have not found any other +portable and reliable way to tell compressed archives from non-compressed. + +Where possible, .tar archives for the testsuite are now decompressed from +their .tar.lz versions instead of distributed. + +'make check' no longer tests '--mtime' with extreme dates to avoid test +failures caused by differences with the system tool 'touch'. +(Reported by Aren Tyr). + +5 new test files have been added to the testsuite. diff --git a/README b/README index 8d2701c..08f135c 100644 --- a/README +++ b/README @@ -15,7 +15,7 @@ compressed archives. Keeping the alignment between tar members and lzip members has two advantages. It adds an indexed lzip layer on top of the tar archive, making -it possible to decode the archive safely in parallel. It also minimizes the +it possible to decode the archive safely in parallel. It also reduces the amount of data lost in case of corruption. Compressing a tar archive with plzip may even double the amount of files lost for each lzip member damaged because it does not keep the members aligned. @@ -93,7 +93,7 @@ Tarlz uses Arg_parser for command-line argument parsing: http://www.nongnu.org/arg-parser/arg_parser.html -Copyright (C) 2013-2024 Antonio Diaz Diaz. +Copyright (C) 2013-2025 Antonio Diaz Diaz. This file is free documentation: you have unlimited permission to copy, distribute, and modify it. diff --git a/archive_reader.cc b/archive_reader.cc index 2da8f56..fbdaba7 100644 --- a/archive_reader.cc +++ b/archive_reader.cc @@ -1,5 +1,5 @@ /* Tarlz - Archiver with multimember lzip compression - Copyright (C) 2013-2024 Antonio Diaz Diaz. + Copyright (C) 2013-2025 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 @@ -82,10 +82,9 @@ Archive_descriptor::Archive_descriptor( const std::string & archive_name ) int Archive_reader_base::parse_records( Extended & extended, - const Tar_header header, - Resizable_buffer & rbuf, - const char * const default_msg, - const bool permissive ) + const Tar_header header, Resizable_buffer & rbuf, + const char * const default_msg, const bool permissive, + std::vector< std::string > * const msg_vecp ) { const long long edsize = parse_octal( header + size_o, size_l ); const long long bufsize = round_up( edsize ); @@ -95,7 +94,7 @@ int Archive_reader_base::parse_records( Extended & extended, if( !rbuf.resize( bufsize ) ) return err( -1, mem_msg ); e_msg_ = ""; e_code_ = 0; int retval = read( rbuf.u8(), bufsize ); // extended records buffer - if( retval == 0 && !extended.parse( rbuf(), edsize, permissive ) ) + if( retval == 0 && !extended.parse( rbuf(), edsize, permissive, msg_vecp ) ) retval = 2; if( retval && !*e_msg_ ) e_msg_ = default_msg; return retval; @@ -156,16 +155,9 @@ int Archive_reader::read( uint8_t * const buf, const int size ) const int rd = LZ_decompress_read( decoder, buf + sz, size - sz ); if( rd < 0 ) { - const unsigned long long old_pos = LZ_decompress_total_in_size( decoder ); if( LZ_decompress_sync_to_member( decoder ) < 0 ) internal_error( "library error (LZ_decompress_sync_to_member)." ); - e_skip_ = true; set_error_status( 2 ); - const unsigned long long new_pos = LZ_decompress_total_in_size( decoder ); - // lzlib < 1.8 does not update total_in_size when syncing to member - if( new_pos >= old_pos && new_pos < LLONG_MAX ) - return err( 2, "", 0, sz, true ); - return err( -1, "Skipping to next header failed. " - "Lzlib 1.8 or newer required.", 0, sz ); + e_skip_ = true; set_error_status( 2 ); return err( 2, "", 0, sz, true ); } if( rd == 0 && LZ_decompress_finished( decoder ) == 1 ) { return err( -2, end_msg, 0, sz ); } diff --git a/archive_reader.h b/archive_reader.h index a68372a..d313e2e 100644 --- a/archive_reader.h +++ b/archive_reader.h @@ -1,5 +1,5 @@ /* Tarlz - Archiver with multimember lzip compression - Copyright (C) 2013-2024 Antonio Diaz Diaz. + Copyright (C) 2013-2025 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 @@ -65,9 +65,10 @@ public: If !OK, fills all the e_* variables. */ virtual int read( uint8_t * const buf, const int size ) = 0; - int parse_records( Extended & extended, const Tar_header header, - Resizable_buffer & rbuf, const char * const default_msg, - const bool permissive ); + int parse_records( Extended & extended, + const Tar_header header, Resizable_buffer & rbuf, + const char * const default_msg, const bool permissive, + std::vector< std::string > * const msg_vecp = 0 ); }; @@ -112,7 +113,7 @@ public: long long mdata_end() const { return mdata_end_; } bool at_member_end() const { return data_pos_ == mdata_end_; } - // Resets decoder and sets position to the start of the member. + // Reset decoder and set position to the start of the member. void set_member( const long i ); int read( uint8_t * const buf, const int size ); diff --git a/arg_parser.cc b/arg_parser.cc index 0c528b2..9275846 100644 --- a/arg_parser.cc +++ b/arg_parser.cc @@ -1,5 +1,5 @@ /* Arg_parser - POSIX/GNU command-line argument parser. (C++ version) - Copyright (C) 2006-2024 Antonio Diaz Diaz. + Copyright (C) 2006-2025 Antonio Diaz Diaz. This library is free software. Redistribution and use in source and binary forms, with or without modification, are permitted provided diff --git a/arg_parser.h b/arg_parser.h index ab77fc5..2fe5a61 100644 --- a/arg_parser.h +++ b/arg_parser.h @@ -1,5 +1,5 @@ /* Arg_parser - POSIX/GNU command-line argument parser. (C++ version) - Copyright (C) 2006-2024 Antonio Diaz Diaz. + Copyright (C) 2006-2025 Antonio Diaz Diaz. This library is free software. Redistribution and use in source and binary forms, with or without modification, are permitted provided diff --git a/common.cc b/common.cc index b653e01..834d421 100644 --- a/common.cc +++ b/common.cc @@ -1,5 +1,5 @@ /* Tarlz - Archiver with multimember lzip compression - Copyright (C) 2013-2024 Antonio Diaz Diaz. + Copyright (C) 2013-2025 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 diff --git a/common_decode.cc b/common_decode.cc index 632ad3f..36f6a43 100644 --- a/common_decode.cc +++ b/common_decode.cc @@ -1,5 +1,5 @@ /* Tarlz - Archiver with multimember lzip compression - Copyright (C) 2013-2024 Antonio Diaz Diaz. + Copyright (C) 2013-2025 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 @@ -117,53 +117,56 @@ bool block_is_zero( const uint8_t * const buf, const int size ) bool format_member_name( const Extended & extended, const Tar_header header, Resizable_buffer & rbuf, const bool long_format ) { - if( long_format ) + if( !long_format ) { - format_mode_string( header, rbuf() ); - const int group_string_len = - format_user_group_string( extended, header, rbuf() + mode_string_size ); - int offset = mode_string_size + group_string_len; - const time_t mtime = extended.mtime().sec(); - struct tm t; - if( !localtime_r( &mtime, &t ) ) // if local time fails - { time_t z = 0; if( !gmtime_r( &z, &t ) ) // use UTC, the epoch - { t.tm_year = 70; t.tm_mon = t.tm_hour = t.tm_min = 0; t.tm_mday = 1; } } - const Typeflag typeflag = (Typeflag)header[typeflag_o]; - const bool islink = typeflag == tf_link || typeflag == tf_symlink; - const char * const link_string = !islink ? "" : - ( ( typeflag == tf_link ) ? " link to " : " -> " ); - // print "user/group size" in a field of width 19 with 8 or more for size - if( typeflag == tf_chardev || typeflag == tf_blockdev ) - { - const unsigned devmajor = parse_octal( header + devmajor_o, devmajor_l ); - const unsigned devminor = parse_octal( header + devminor_o, devminor_l ); - const int width = std::max( 1, - std::max( 8, 19 - group_string_len ) - 1 - decimal_digits( devminor ) ); - offset += snprintf( rbuf() + offset, rbuf.size() - offset, " %*u,%u", - width, devmajor, devminor ); - } - else - { - const int width = std::max( 8, 19 - group_string_len ); - offset += snprintf( rbuf() + offset, rbuf.size() - offset, " %*llu", - width, extended.file_size() ); - } - for( int i = 0; i < 2; ++i ) // resize rbuf if not large enough - { - const int len = snprintf( rbuf() + offset, rbuf.size() - offset, - " %4d-%02u-%02u %02u:%02u %s%s%s\n", - 1900 + t.tm_year, 1 + t.tm_mon, t.tm_mday, t.tm_hour, - t.tm_min, extended.path().c_str(), link_string, - islink ? extended.linkpath().c_str() : "" ); - if( len + offset < (int)rbuf.size() ) break; - if( !rbuf.resize( len + offset + 1 ) ) return false; - } + if( !rbuf.resize( extended.path().size() + 2 ) ) return false; + snprintf( rbuf(), rbuf.size(), "%s\n", extended.path().c_str() ); + return true; + } + format_mode_string( header, rbuf() ); + const int group_string_len = + format_user_group_string( extended, header, rbuf() + mode_string_size ); + int offset = mode_string_size + group_string_len; + const time_t mtime = extended.mtime().sec(); + struct tm t; + char buf[32]; // if local time and UTC fail, use seconds since epoch + if( localtime_r( &mtime, &t ) || gmtime_r( &mtime, &t ) ) + snprintf( buf, sizeof buf, "%04d-%02u-%02u %02u:%02u", 1900 + t.tm_year, + 1 + t.tm_mon, t.tm_mday, t.tm_hour, t.tm_min ); + else snprintf( buf, sizeof buf, "%lld", extended.mtime().sec() ); + const Typeflag typeflag = (Typeflag)header[typeflag_o]; + const bool islink = typeflag == tf_link || typeflag == tf_symlink; + const char * const link_string = !islink ? "" : + ( ( typeflag == tf_link ) ? " link to " : " -> " ); + // print "user/group size" in a field of width 19 with 8 or more for size + if( typeflag == tf_chardev || typeflag == tf_blockdev ) + { + const unsigned devmajor = parse_octal( header + devmajor_o, devmajor_l ); + const unsigned devminor = parse_octal( header + devminor_o, devminor_l ); + const int width = std::max( 1, + std::max( 8, 19 - group_string_len ) - 1 - decimal_digits( devminor ) ); + offset += snprintf( rbuf() + offset, rbuf.size() - offset, " %*u,%u", + width, devmajor, devminor ); } else { - if( rbuf.size() < extended.path().size() + 2 && - !rbuf.resize( extended.path().size() + 2 ) ) return false; - snprintf( rbuf(), rbuf.size(), "%s\n", extended.path().c_str() ); + const int width = std::max( 8, 19 - group_string_len ); + offset += snprintf( rbuf() + offset, rbuf.size() - offset, " %*llu", + width, extended.file_size() ); + } + for( int i = 0; i < 2; ++i ) // resize rbuf if not large enough + { + const int len = snprintf( rbuf() + offset, rbuf.size() - offset, + " %s %s%s%s\n", buf, extended.path().c_str(), link_string, + islink ? extended.linkpath().c_str() : "" ); + if( len + offset < (int)rbuf.size() ) { offset += len; break; } + if( !rbuf.resize( len + offset + 1 ) ) return false; + } + if( rbuf()[0] == '?' ) + { + if( !rbuf.resize( offset + 25 + 1 ) ) return false; + snprintf( rbuf() + offset - 1, rbuf.size() - offset, + ": Unknown file type 0x%02X\n", typeflag ); } return true; } @@ -183,9 +186,12 @@ bool show_member_name( const Extended & extended, const Tar_header header, } +/* Return true if file must be skipped. + Execute -C options if cwd_fd >= 0 (diff or extract). */ bool check_skip_filename( const Cl_options & cl_opts, std::vector< char > & name_pending, - const char * const filename, const int chdir_fd ) + const char * const filename, const int cwd_fd, + std::string * const msgp ) { static int c_idx = -1; // parser index of last -C executed if( Exclude::excluded( filename ) ) return true; // skip excluded files @@ -197,18 +203,19 @@ bool check_skip_filename( const Cl_options & cl_opts, { if( cl_opts.parser.code( i ) == 'C' ) { chdir_pending = true; continue; } if( !nonempty_arg( cl_opts.parser, i ) ) continue; // skip opts, empty names - std::string removed_prefix; + std::string removed_prefix; // prefix of cl argument const char * const name = remove_leading_dotslash( cl_opts.parser.argument( i ).c_str(), &removed_prefix ); if( compare_prefix_dir( name, filename ) || compare_tslash( name, filename ) ) { - print_removed_prefix( removed_prefix ); + print_removed_prefix( removed_prefix, msgp ); skip = false; name_pending[i] = false; - if( chdir_pending && chdir_fd >= 0 ) + // only serial decoder sets cwd_fd >= 0 to process -C options + if( chdir_pending && cwd_fd >= 0 ) { if( c_idx > i ) - { if( fchdir( chdir_fd ) != 0 ) + { if( fchdir( cwd_fd ) != 0 ) { show_error( "Error changing to initial working directory", errno ); throw Chdir_error(); } c_idx = -1; } for( int j = c_idx + 1; j < i; ++j ) @@ -234,7 +241,11 @@ bool make_dirs( const std::string & name ) while( i > 0 && name[i-1] != '/' ) --i; // remove last component while( i > 0 && name[i-1] == '/' ) --i; // remove more slashes const int dirsize = i; // first slash before last component + struct stat st; + if( dirsize > 0 && lstat( std::string( name, 0, dirsize ).c_str(), &st ) == 0 ) + { if( !S_ISDIR( st.st_mode ) ) { errno = ENOTDIR; return false; } + return true; } for( i = 0; i < dirsize; ) // if dirsize == 0, dirname is '/' or empty { while( i < dirsize && name[i] == '/' ) ++i; @@ -244,7 +255,6 @@ bool make_dirs( const std::string & name ) { const std::string partial( name, 0, i ); const mode_t mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; - struct stat st; if( lstat( partial.c_str(), &st ) == 0 ) { if( !S_ISDIR( st.st_mode ) ) { errno = ENOTDIR; return false; } } else if( mkdir( partial.c_str(), mode ) != 0 && errno != EEXIST ) diff --git a/common_mutex.cc b/common_mutex.cc index fb253ed..f7770d7 100644 --- a/common_mutex.cc +++ b/common_mutex.cc @@ -1,5 +1,5 @@ /* Tarlz - Archiver with multimember lzip compression - Copyright (C) 2013-2024 Antonio Diaz Diaz. + Copyright (C) 2013-2025 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 @@ -131,10 +131,10 @@ bool print_removed_prefix( const std::string & prefix, if( prefixes[i] == prefix ) { xunlock( &mutex ); if( msgp ) msgp->clear(); return false; } prefixes.push_back( prefix ); + xunlock( &mutex ); std::string msg( "Removing leading '" ); msg += prefix; - msg += "' from member names."; + msg += "' from member names."; // from archive or command line if( msgp ) *msgp = msg; else show_error( msg.c_str() ); - xunlock( &mutex ); // put here to prevent mixing calls to show_error return true; } diff --git a/common_mutex.h b/common_mutex.h index ed3999c..158cb7b 100644 --- a/common_mutex.h +++ b/common_mutex.h @@ -1,5 +1,5 @@ /* Tarlz - Archiver with multimember lzip compression - Copyright (C) 2013-2024 Antonio Diaz Diaz. + Copyright (C) 2013-2025 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 diff --git a/compress.cc b/compress.cc index e421fa4..78ec227 100644 --- a/compress.cc +++ b/compress.cc @@ -1,5 +1,5 @@ /* Tarlz - Archiver with multimember lzip compression - Copyright (C) 2013-2024 Antonio Diaz Diaz. + Copyright (C) 2013-2025 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 @@ -212,7 +212,6 @@ int compress_archive( const Cl_options & cl_opts, Extended extended; // metadata from extended records Resizable_buffer rbuf; // headers and extended records buffer if( !rbuf.size() ) { show_error( mem_msg ); return 1; } - const char * const rderr_msg = "Read error"; bool first_header = true; while( true ) // process one tar member per iteration @@ -224,7 +223,7 @@ int compress_archive( const Cl_options & cl_opts, show_file_error( filename, "Archive is empty." ); close( infd ); return 2; } if( rd != header_size ) - { show_file_error( filename, rderr_msg, errno ); close( infd ); return 1; } + { show_file_error( filename, rd_err_msg, errno ); close( infd ); return 1; } first_header = false; const bool is_header = check_ustar_chksum( rbuf.u8() ); @@ -259,7 +258,8 @@ int compress_archive( const Cl_options & cl_opts, if( !rbuf.resize( total_header_size + bufsize ) ) { show_file_error( filename, mem_msg ); close( infd ); return 1; } if( readblock( infd, rbuf.u8() + total_header_size, bufsize ) != bufsize ) - { show_file_error( filename, rderr_msg, errno ); close( infd ); return 1; } + { show_file_error( filename, rd_err_msg, errno ); + close( infd ); return 1; } total_header_size += bufsize; if( typeflag == tf_extended ) // do not parse global headers { @@ -269,7 +269,7 @@ int compress_archive( const Cl_options & cl_opts, if( !rbuf.resize( total_header_size + header_size ) ) { show_file_error( filename, mem_msg ); close( infd ); return 1; } if( readblock( infd, rbuf.u8() + total_header_size, header_size ) != header_size ) - { show_file_error( filename, errno ? rderr_msg : end_msg, errno ); + { show_file_error( filename, errno ? rd_err_msg : end_msg, errno ); close( infd ); return errno ? 1 : 2; } if( !check_ustar_chksum( rbuf.u8() ) ) { show_file_error( filename, bad_hdr_msg ); close( infd ); return 2; } diff --git a/configure b/configure index c8ebc5b..fc65649 100755 --- a/configure +++ b/configure @@ -1,12 +1,12 @@ #! /bin/sh # configure script for Tarlz - Archiver with multimember lzip compression -# Copyright (C) 2013-2024 Antonio Diaz Diaz. +# Copyright (C) 2013-2025 Antonio Diaz Diaz. # # This configure script is free software: you have unlimited permission # to copy, distribute, and modify it. pkgname=tarlz -pkgversion=0.26 +pkgversion=0.27 progname=tarlz srctrigger=doc/${pkgname}.texi @@ -175,7 +175,7 @@ echo "MAKEINFO = ${MAKEINFO}" rm -f Makefile cat > Makefile << EOF # Makefile for Tarlz - Archiver with multimember lzip compression -# Copyright (C) 2013-2024 Antonio Diaz Diaz. +# Copyright (C) 2013-2025 Antonio Diaz Diaz. # This file was generated automatically by configure. Don't edit. # # This Makefile is free software: you have unlimited permission diff --git a/create.cc b/create.cc index 09f5797..aeb1b23 100644 --- a/create.cc +++ b/create.cc @@ -1,5 +1,5 @@ /* Tarlz - Archiver with multimember lzip compression - Copyright (C) 2013-2024 Antonio Diaz Diaz. + Copyright (C) 2013-2025 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 @@ -231,7 +231,7 @@ bool store_name( const char * const filename, Extended & extended, } -// add one tar member to the archive +// add one tar member to the archive and print filename int add_member( const char * const filename, const struct stat *, const int flag, struct FTW * ) { @@ -323,7 +323,7 @@ bool copy_file( const int infd, const int outfd, const char * const filename, if( max_size >= 0 ) rest -= size; const int rd = readblock( infd, buffer, size ); if( rd != size && errno ) - { show_file_error( filename, "Read error", errno ); error = true; break; } + { show_file_error( filename, rd_err_msg, errno ); error = true; break; } if( rd > 0 ) { if( !writeblock_wrapper( outfd, buffer, rd ) ) { error = true; break; } @@ -455,7 +455,7 @@ bool fill_headers( const char * const filename, Extended & extended, show_file_error( filename, "Wrong size reading symbolic link.\n" "Please, send a bug report to the maintainers of your filesystem, " "mentioning\n'wrong st_size of symbolic link'.\nSee " - "http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_stat.h.html" ); + "http://pubs.opengroup.org/onlinepubs/9799919799/basedefs/sys_stat.h.html" ); set_error_status( 1 ); return false; } } @@ -607,7 +607,7 @@ int concatenate( const Cl_options & cl_opts ) "Not an appendable tar archive." ); close( infd ); retval = 2; break; } if( !copy_file( infd, outfd, filename, size ) || close( infd ) != 0 ) - { show_file_error( filename, "Error copying archive", errno ); + { show_file_error( filename, "Error concatenating archive", errno ); eoa_pending = false; retval = 1; break; } eoa_pending = true; if( verbosity >= 1 ) std::fprintf( stderr, "%s\n", filename ); @@ -621,6 +621,34 @@ int concatenate( const Cl_options & cl_opts ) } +// Return value: 0 = skip arg, 1 = error, 2 = arg done +int parse_cl_arg( const Cl_options & cl_opts, const int i, + int (* add_memberp)( const char * const filename, + const struct stat *, const int flag, struct FTW * ) ) + { + const int code = cl_opts.parser.code( i ); + const std::string & arg = cl_opts.parser.argument( i ); + const char * filename = arg.c_str(); // filename from command line + if( code == 'C' && chdir( filename ) != 0 ) + { show_file_error( filename, chdir_msg, errno ); return 1; } + if( code ) return 0; // skip options + if( cl_opts.parser.argument( i ).empty() ) return 0; // skip empty names + std::string deslashed; // filename without trailing slashes + unsigned len = arg.size(); + while( len > 1 && arg[len-1] == '/' ) --len; + if( len < arg.size() ) + { deslashed.assign( arg, 0, len ); filename = deslashed.c_str(); } + if( Exclude::excluded( filename ) ) return 0; // skip excluded files + struct stat st; + if( lstat( filename, &st ) != 0 ) + { show_file_error( filename, cant_stat, errno ); + set_error_status( 1 ); return 0; } + if( nftw( filename, add_memberp, 16, cl_opts.dereference ? 0 : FTW_PHYS ) ) + return 1; // write error or OOM + return 2; + } + + int encode( const Cl_options & cl_opts ) { if( !grbuf.size() ) { show_error( mem_msg ); return 1; } @@ -698,27 +726,11 @@ int encode( const Cl_options & cl_opts ) int retval = 0; for( int i = 0; i < cl_opts.parser.arguments(); ++i ) // parse command line { - const int code = cl_opts.parser.code( i ); - const std::string & arg = cl_opts.parser.argument( i ); - const char * filename = arg.c_str(); - if( code == 'C' && chdir( filename ) != 0 ) - { show_file_error( filename, chdir_msg, errno ); retval = 1; break; } - if( code ) continue; // skip options - if( cl_opts.parser.argument( i ).empty() ) continue; // skip empty names - std::string deslashed; // arg without trailing slashes - unsigned len = arg.size(); - while( len > 1 && arg[len-1] == '/' ) --len; - if( len < arg.size() ) - { deslashed.assign( arg, 0, len ); filename = deslashed.c_str(); } - if( Exclude::excluded( filename ) ) continue; // skip excluded files - struct stat st; - if( lstat( filename, &st ) != 0 ) // filename from command line - { show_file_error( filename, cant_stat, errno ); set_error_status( 1 ); } - else if( ( retval = nftw( filename, add_member, 16, - cl_opts.dereference ? 0 : FTW_PHYS ) ) != 0 ) - break; // write error - else if( encoder && cl_opts.solidity == dsolid && !archive_write( 0, 0 ) ) - { retval = 1; break; } + const int ret = parse_cl_arg( cl_opts, i, add_member ); + if( ret == 0 ) continue; // skip arg + if( ret == 1 ) { retval = 1; break; } // error + if( encoder && cl_opts.solidity == dsolid && // end of group + !archive_write( 0, 0 ) ) { retval = 1; break; } } if( retval == 0 ) // write End-Of-Archive records diff --git a/create.h b/create.h index d5ef7bc..bbbf4c6 100644 --- a/create.h +++ b/create.h @@ -1,5 +1,5 @@ /* Tarlz - Archiver with multimember lzip compression - Copyright (C) 2013-2024 Antonio Diaz Diaz. + Copyright (C) 2013-2025 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 @@ -45,3 +45,8 @@ public: extern Archive_attrs archive_attrs; const char * const cant_stat = "Can't stat input file"; + +// defined in create.cc +int parse_cl_arg( const Cl_options & cl_opts, const int i, + int (* add_memberp)( const char * const filename, + const struct stat *, const int flag, struct FTW * ) ); diff --git a/create_lz.cc b/create_lz.cc index b390290..7afff16 100644 --- a/create_lz.cc +++ b/create_lz.cc @@ -1,5 +1,5 @@ /* Tarlz - Archiver with multimember lzip compression - Copyright (C) 2013-2024 Antonio Diaz Diaz. + Copyright (C) 2013-2025 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 @@ -250,7 +250,7 @@ public: }; -// send one ipacket with tar member metadata to courier +// send one ipacket with tar member metadata to courier and print filename int add_member_lz( const char * const filename, const struct stat *, const int flag, struct FTW * ) { @@ -300,26 +300,10 @@ extern "C" void * grouper( void * arg ) for( int i = 0; i < cl_opts.parser.arguments(); ++i ) // parse command line { - const int code = cl_opts.parser.code( i ); - const std::string & arg = cl_opts.parser.argument( i ); - const char * filename = arg.c_str(); - if( code == 'C' && chdir( filename ) != 0 ) - { show_file_error( filename, chdir_msg, errno ); exit_fail_mt(); } - if( code ) continue; // skip options - if( cl_opts.parser.argument( i ).empty() ) continue; // skip empty names - std::string deslashed; // arg without trailing slashes - unsigned len = arg.size(); - while( len > 1 && arg[len-1] == '/' ) --len; - if( len < arg.size() ) - { deslashed.assign( arg, 0, len ); filename = deslashed.c_str(); } - if( Exclude::excluded( filename ) ) continue; // skip excluded files - struct stat st; - if( lstat( filename, &st ) != 0 ) // filename from command line - { show_file_error( filename, cant_stat, errno ); set_error_status( 1 ); } - else if( nftw( filename, add_member_lz, 16, - cl_opts.dereference ? 0 : FTW_PHYS ) != 0 ) - exit_fail_mt(); // write error or OOM - else if( cl_opts.solidity == dsolid ) // end of group + const int ret = parse_cl_arg( cl_opts, i, add_member_lz ); + if( ret == 0 ) continue; // skip arg + if( ret == 1 ) exit_fail_mt(); // error + if( cl_opts.solidity == dsolid ) // end of group courier.receive_packet( new Ipacket ); } diff --git a/decode.cc b/decode.cc index f2ffa52..98f6c1d 100644 --- a/decode.cc +++ b/decode.cc @@ -1,5 +1,5 @@ /* Tarlz - Archiver with multimember lzip compression - Copyright (C) 2013-2024 Antonio Diaz Diaz. + Copyright (C) 2013-2025 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 @@ -48,14 +48,18 @@ namespace { Resizable_buffer grbuf; -bool skip_warn( const bool reset = false ) // avoid duplicate warnings +bool skip_warn( const bool reset = false, const unsigned chksum = 0 ) { - static bool skipping = false; + static bool skipping = false; // avoid duplicate warnings - if( reset ) skipping = false; - else if( !skipping ) - { skipping = true; show_error( "Skipping to next header." ); return true; } - return false; + if( reset ) { skipping = false; return false; } + if( skipping ) return false; + skipping = true; + if( chksum != 0 ) + { if( verbosity < 1 ) show_error( "Corrupt header." ); + else std::fprintf( stderr, "%s: Corrupt header: ustar chksum = %06o\n", + program_name, chksum ); } + show_error( "Skipping to next header." ); return true; } @@ -127,20 +131,20 @@ int extract_member( const Cl_options & cl_opts, Archive_reader & ar, int outfd = -1; if( !show_member_name( extended, header, 1, grbuf ) ) return 1; - // remove file (or empty dir) before extraction to prevent following links - std::remove( filename ); if( !make_dirs( filename ) ) { show_file_error( filename, intdir_msg, errno ); set_error_status( 1 ); return skip_member( ar, extended, typeflag ); } + // remove file or empty dir before extraction to prevent following links + std::remove( filename ); switch( typeflag ) { case tf_regular: case tf_hiperf: - outfd = open_outstream( filename ); + outfd = open_outstream( filename, true, 0, false ); if( outfd < 0 ) { set_error_status( 1 ); return skip_member( ar, extended, typeflag ); } break; @@ -223,7 +227,7 @@ int extract_member( const Cl_options & cl_opts, Archive_reader & ar, if( cl_opts.keep_damaged ) { writeblock( outfd, buf, std::min( rest, (long long)ar.e_size() ) ); close( outfd ); } - else { close( outfd ); std::remove( filename ); } + else { close( outfd ); unlink( filename ); } } if( ar.fatal() ) return ret; else return 0; } @@ -386,7 +390,7 @@ bool compare_file_contents( std::string & estr, std::string & ostr, const int rd = readblock( infd2, buf2, rsize2 ); if( rd != rsize2 ) { - if( errno ) format_file_error( estr, filename, "Read error", errno ); + if( errno ) format_file_error( estr, filename, rd_err_msg, errno ); else format_file_diff( ostr, filename, "EOF found in file" ); diff = true; } @@ -420,8 +424,8 @@ int decode( const Cl_options & cl_opts ) const bool c_after_name = c_present && option_C_after_filename( cl_opts.parser ); // save current working directory for sequential decoding - const int chdir_fd = c_after_name ? open( ".", O_RDONLY | O_DIRECTORY ) : -1; - if( c_after_name && chdir_fd < 0 ) + const int cwd_fd = c_after_name ? open( ".", O_RDONLY | O_DIRECTORY ) : -1; + if( c_after_name && cwd_fd < 0 ) { show_error( "Can't save current working directory", errno ); return 1; } if( c_present && !c_after_name ) // execute all -C options for( int i = 0; i < cl_opts.parser.arguments(); ++i ) @@ -464,8 +468,7 @@ int decode( const Cl_options & cl_opts ) show_file_error( ad.namep, fv_msg1 ); retval = 2; break; } - if( skip_warn() && verbosity >= 2 ) - std::fprintf( stderr, "ustar chksum = %07o\n", ustar_chksum( header ) ); + skip_warn( false, ustar_chksum( header ) ); set_error_status( 2 ); continue; } skip_warn( true ); // reset warning @@ -480,7 +483,7 @@ int decode( const Cl_options & cl_opts ) if( ret != 0 ) { show_file_error( ad.namep, ar.e_msg(), ar.e_code() ); if( ar.fatal() ) { retval = ret; break; } - skip_warn(); set_error_status( ret ); } + set_error_status( ret ); } continue; } if( typeflag == tf_extended ) @@ -492,7 +495,7 @@ int decode( const Cl_options & cl_opts ) if( ret != 0 ) { show_file_error( ad.namep, ar.e_msg(), ar.e_code() ); if( ar.fatal() ) { retval = ret; break; } - skip_warn(); extended.reset(); set_error_status( ret ); } + extended.reset(); set_error_status( ret ); } else if( !extended.crc_present() && cl_opts.missing_crc ) { show_file_error( ad.namep, miscrc_msg ); retval = 2; break; } prev_extended = true; continue; @@ -504,7 +507,7 @@ int decode( const Cl_options & cl_opts ) try { // members without name are skipped except when listing if( check_skip_filename( cl_opts, name_pending, extended.path().c_str(), - chdir_fd ) ) retval = skip_member( ar, extended, typeflag ); + cwd_fd ) ) retval = skip_member( ar, extended, typeflag ); else { print_removed_prefix( extended.removed_prefix ); diff --git a/decode.h b/decode.h index 05d3072..867e3e9 100644 --- a/decode.h +++ b/decode.h @@ -1,5 +1,5 @@ /* Tarlz - Archiver with multimember lzip compression - Copyright (C) 2013-2024 Antonio Diaz Diaz. + Copyright (C) 2013-2025 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 @@ -16,7 +16,7 @@ */ inline bool data_may_follow( const Typeflag typeflag ) - { return typeflag <= 0 || typeflag >= 7; } + { return typeflag == tf_regular || typeflag == tf_hiperf; } inline bool uid_gid_in_range( const long long uid, const long long gid ) { return uid == (long long)( (uid_t)uid ) && @@ -27,7 +27,7 @@ const char * const cantln_msg = "Can't %slink '%s' to '%s'"; const char * const mkdir_msg = "Can't create directory"; const char * const mknod_msg = "Can't create device node"; const char * const mkfifo_msg = "Can't create FIFO file"; -const char * const uftype_msg = "%s: Unknown file type '%c', skipping."; +const char * const uftype_msg = "%s: Unknown file type 0x%02X, skipping."; const char * const chown_msg = "Can't change file owner"; mode_t get_umask(); diff --git a/decode_lz.cc b/decode_lz.cc index b8763f3..8190186 100644 --- a/decode_lz.cc +++ b/decode_lz.cc @@ -1,5 +1,5 @@ /* Tarlz - Archiver with multimember lzip compression - Copyright (C) 2013-2024 Antonio Diaz Diaz. + Copyright (C) 2013-2025 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 @@ -41,7 +41,8 @@ #include "common_mutex.h" #include "decode.h" -/* When a problem is detected by any worker: +/* Parallel decode does not skip; it exits at the first error. + When a problem is detected by any worker: - the worker requests mastership and returns. - the courier discards new packets received or collected. - the other workers return. @@ -49,9 +50,9 @@ namespace { -const char * const other_msg = "Other worker found an error."; +const char * const other_msg = "Another worker found an error."; -/* line is preformatted and newline terminated except for prefix, error. +/* line is preformatted and newline terminated except for prefix and errors. ok with an empty line is a no-op. */ struct Packet // member name and metadata or error message { @@ -60,7 +61,7 @@ struct Packet // member name and metadata or error message long member_id; // lzip member containing the header of this tar member std::string line; // member name and metadata ready to print, if any Status status; // diagnostics and errors go to stderr - int errcode; // for error + int errcode; // for errors Packet( const long i, const char * const msg, const Status s, const int e ) : member_id( i ), line( msg ), status( s ), errcode( e ) {} }; @@ -119,11 +120,12 @@ public: { xunlock( &omutex ); return master_id == worker_id; } if( error_member_id < 0 || error_member_id > member_id ) error_member_id = member_id; - while( !mastership_granted() && ( worker_id != deliver_id || - !opacket_queues[deliver_id].empty() ) ) + while( !mastership_granted() && + ( worker_id != deliver_id || !opacket_queues[deliver_id].empty() ) ) xwait( &check_master, &omutex ); - if( !mastership_granted() && worker_id == deliver_id && - opacket_queues[deliver_id].empty() ) + if( !mastership_granted() && + // redundant conditions useful for the compiler + worker_id == deliver_id && opacket_queues[deliver_id].empty() ) { master_id = worker_id; // grant mastership for( int i = 0; i < num_workers; ++i ) // delete all packets @@ -356,10 +358,9 @@ Trival extract_member_lz( const Cl_options & cl_opts, if( !courier.collect_packet( member_id, worker_id, rbuf(), Packet::ok ) ) return Trival( other_msg, 0, 1 ); } - /* Remove file before extraction to prevent following links. - Don't remove an empty dir because other thread may need it. */ - if( typeflag != tf_directory ) std::remove( filename ); - if( !make_dirs( filename ) ) + struct stat st; + bool exists = lstat( filename, &st ) == 0; + if( !exists && !make_dirs( filename ) ) { if( format_file_error( rbuf, filename, intdir_msg, errno ) && !courier.collect_packet( member_id, worker_id, rbuf(), Packet::diag ) ) @@ -367,12 +368,16 @@ Trival extract_member_lz( const Cl_options & cl_opts, set_error_status( 1 ); return skip_member_lz( ar, courier, extended, member_id, worker_id, typeflag ); } + /* Remove file before extraction to prevent following links. + Don't remove an empty dir; another thread may need it. */ + if( exists && ( typeflag != tf_directory || !S_ISDIR( st.st_mode ) ) ) + { exists = false; std::remove( filename ); } switch( typeflag ) { case tf_regular: case tf_hiperf: - outfd = open_outstream( filename, true, &rbuf ); + outfd = open_outstream( filename, true, &rbuf, false ); if( outfd < 0 ) { if( verbosity >= 0 && @@ -399,11 +404,6 @@ Trival extract_member_lz( const Cl_options & cl_opts, } } break; case tf_directory: - { - struct stat st; - bool exists = stat( filename, &st ) == 0; - if( exists && !S_ISDIR( st.st_mode ) ) - { exists = false; std::remove( filename ); } if( !exists && mkdir( filename, mode ) != 0 && errno != EEXIST ) { if( format_file_error( rbuf, filename, mkdir_msg, errno ) && @@ -411,7 +411,7 @@ Trival extract_member_lz( const Cl_options & cl_opts, return Trival( other_msg, 0, 1 ); set_error_status( 1 ); } - } break; + break; case tf_chardev: case tf_blockdev: { @@ -483,7 +483,7 @@ Trival extract_member_lz( const Cl_options & cl_opts, if( cl_opts.keep_damaged ) { writeblock( outfd, buf, std::min( rest, (long long)ar.e_size() ) ); close( outfd ); } - else { close( outfd ); std::remove( filename ); } + else { close( outfd ); unlink( filename ); } } return Trival( ar.e_msg(), ar.e_code(), ret ); } @@ -614,13 +614,18 @@ extern "C" void * dworker( void * arg ) } if( typeflag == tf_extended ) { - const char * msg = 0; int ret = 2; + std::vector< std::string > msg_vec; + const char * msg = 0; int ret = 2; bool good = false; if( prev_extended && !cl_opts.permissive ) msg = fv_msg3; else if( ( ret = ar.parse_records( extended, header, rbuf, extrec_msg, - cl_opts.permissive ) ) != 0 ) msg = ar.e_msg(); + cl_opts.permissive, &msg_vec ) ) != 0 ) msg = ar.e_msg(); else if( !extended.crc_present() && cl_opts.missing_crc ) { msg = miscrc_msg; ret = 2; } - else { prev_extended = true; continue; } + else { prev_extended = true; good = true; } + for( unsigned j = 0; j < msg_vec.size(); ++j ) + if( !courier.collect_packet( i, worker_id, msg_vec[j].c_str(), + Packet::diag ) ) { good = false; break; } + if( good ) continue; if( courier.request_mastership( i, worker_id ) ) courier.collect_packet( i, worker_id, msg, ( ret == 1 ) ? Packet::error1 : Packet::error2 ); @@ -632,13 +637,18 @@ extern "C" void * dworker( void * arg ) /* Skip members with an empty name in the ustar header. If there is an extended header in a previous lzip member, its worker will request - mastership. Else the ustar-only unnamed member will be ignored. */ + mastership and the skip may fail here. Else the ustar-only unnamed + member will be ignored. */ + std::string rpmsg; // removed prefix Trival trival; - if( check_skip_filename( cl_opts, name_pending, extended.path().c_str() ) ) + if( check_skip_filename( cl_opts, name_pending, extended.path().c_str(), + -1, &rpmsg ) ) trival = skip_member_lz( ar, courier, extended, i, worker_id, typeflag ); else { - std::string rpmsg; + if( verbosity >= 0 && rpmsg.size() && + !courier.collect_packet( i, worker_id, rpmsg.c_str(), Packet::prefix ) ) + { trival = Trival( other_msg, 0, 1 ); goto fatal; } if( print_removed_prefix( extended.removed_prefix, &rpmsg ) && !courier.collect_packet( i, worker_id, rpmsg.c_str(), Packet::prefix ) ) { trival = Trival( other_msg, 0, 1 ); goto fatal; } @@ -667,14 +677,14 @@ done: } -/* Get from courier the processed and sorted packets, and print - the member lines on stdout or the diagnostics and errors on stderr. +/* Get from courier the processed and sorted packets. + Print the member lines on stdout and the diagnostics and errors on stderr. */ -void muxer( const char * const archive_namep, Packet_courier & courier ) +int muxer( const char * const archive_namep, Packet_courier & courier ) { std::vector< const Packet * > opacket_vector; int retval = 0; - while( retval == 0 ) + while( retval == 0 ) // exit loop at first error packet { courier.deliver_packets( opacket_vector ); if( opacket_vector.empty() ) break; // queue is empty. all workers exited @@ -698,7 +708,7 @@ void muxer( const char * const archive_namep, Packet_courier & courier ) } if( retval == 0 && !courier.eoa_found() ) // no worker found EOA blocks { show_file_error( archive_namep, end_msg ); retval = 2; } - if( retval ) exit_fail_mt( retval ); + return retval; } } // end namespace @@ -715,8 +725,6 @@ int decode_lz( const Cl_options & cl_opts, const Archive_descriptor & ad, Name_monitor name_monitor( ( cl_opts.program_mode == m_extract ) ? num_workers : 0 ); - /* If an error happens after any threads have been started, exit must be - called before courier goes out of scope. */ Packet_courier courier( num_workers, out_slots ); Worker_arg * worker_args = new( std::nothrow ) Worker_arg[num_workers]; @@ -737,7 +745,7 @@ int decode_lz( const Cl_options & cl_opts, const Archive_descriptor & ad, { show_error( "Can't create worker threads", errcode ); exit_fail_mt(); } } - muxer( ad.namep, courier ); + int retval = muxer( ad.namep, courier ); for( int i = num_workers - 1; i >= 0; --i ) { @@ -748,9 +756,8 @@ int decode_lz( const Cl_options & cl_opts, const Archive_descriptor & ad, delete[] worker_threads; delete[] worker_args; - int retval = 0; if( close( ad.infd ) != 0 ) - { show_file_error( ad.namep, eclosa_msg, errno ); retval = 1; } + { show_file_error( ad.namep, eclosa_msg, errno ); set_retval( retval, 1 ); } if( retval == 0 ) for( int i = 0; i < cl_opts.parser.arguments(); ++i ) diff --git a/delete.cc b/delete.cc index 8a4a40f..de9fe1e 100644 --- a/delete.cc +++ b/delete.cc @@ -1,5 +1,5 @@ /* Tarlz - Archiver with multimember lzip compression - Copyright (C) 2013-2024 Antonio Diaz Diaz. + Copyright (C) 2013-2025 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 diff --git a/delete_lz.cc b/delete_lz.cc index a9c31ad..b32350e 100644 --- a/delete_lz.cc +++ b/delete_lz.cc @@ -1,5 +1,5 @@ /* Tarlz - Archiver with multimember lzip compression - Copyright (C) 2013-2024 Antonio Diaz Diaz. + Copyright (C) 2013-2025 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 diff --git a/doc/tarlz.1 b/doc/tarlz.1 index c1e6dd6..45bcff9 100644 --- a/doc/tarlz.1 +++ b/doc/tarlz.1 @@ -1,5 +1,5 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.2. -.TH TARLZ "1" "December 2024" "tarlz 0.26" "User Commands" +.TH TARLZ "1" "March 2025" "tarlz 0.27" "User Commands" .SH NAME tarlz \- creates tar archives with multimember lzip compression .SH SYNOPSIS @@ -19,7 +19,7 @@ compressed archives. .PP Keeping the alignment between tar members and lzip members has two advantages. It adds an indexed lzip layer on top of the tar archive, making -it possible to decode the archive safely in parallel. It also minimizes the +it possible to decode the archive safely in parallel. It also reduces the amount of data lost in case of corruption. .PP The tarlz file format is a safe POSIX\-style backup format. In case of @@ -160,12 +160,12 @@ Report bugs to lzip\-bug@nongnu.org .br Tarlz home page: http://www.nongnu.org/lzip/tarlz.html .SH COPYRIGHT -Copyright \(co 2024 Antonio Diaz Diaz. +Copyright \(co 2025 Antonio Diaz Diaz. License GPLv2+: GNU GPL version 2 or later .br This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. -Using lzlib 1.15\-rc1 +Using lzlib 1.15 Using LZ_API_VERSION = 1015 .SH "SEE ALSO" The full documentation for diff --git a/doc/tarlz.info b/doc/tarlz.info index 46d5ab4..78b7729 100644 --- a/doc/tarlz.info +++ b/doc/tarlz.info @@ -11,13 +11,14 @@ File: tarlz.info, Node: Top, Next: Introduction, Up: (dir) Tarlz Manual ************ -This manual is for Tarlz (version 0.26, 7 December 2024). +This manual is for Tarlz (version 0.27, 28 February 2025). * Menu: * Introduction:: Purpose and features of tarlz * Invoking tarlz:: Command-line interface * Argument syntax:: By convention, options start with a hyphen +* Creating backups safely:: Checking integrity and accuracy of archives * Portable character set:: POSIX portable filename character set * File format:: Detailed format of the compressed archive * Amendments to pax format:: The reasons for the differences with pax @@ -29,7 +30,7 @@ This manual is for Tarlz (version 0.26, 7 December 2024). * Concept index:: Index of concepts - Copyright (C) 2013-2024 Antonio Diaz Diaz. + Copyright (C) 2013-2025 Antonio Diaz Diaz. This manual is free documentation: you have unlimited permission to copy, distribute, and modify it. @@ -53,7 +54,7 @@ compressed archives. Keeping the alignment between tar members and lzip members has two advantages. It adds an indexed lzip layer on top of the tar archive, making -it possible to decode the archive safely in parallel. It also minimizes the +it possible to decode the archive safely in parallel. It also reduces the amount of data lost in case of corruption. Compressing a tar archive with plzip may even double the amount of files lost for each lzip member damaged because it does not keep the members aligned. @@ -216,7 +217,7 @@ tarlz supports the following operations: '-t' '--list' List the contents of an archive. If FILES are given, list only the - FILES given. + FILES given. *Note mt-listing::. '-x' '--extract' @@ -227,20 +228,23 @@ tarlz supports the following operations: directories unconditionally before extracting over them. Other than that, it does not make any special effort to extract a file over an incompatible type of file. For example, extracting a file over a - non-empty directory usually fails. + non-empty directory usually fails. *Note mt-extraction::. '-z' '--compress' Compress existing POSIX tar archives aligning the lzip members to the tar members with choice of granularity ('--bsolid' by default, - '--dsolid' works like '--asolid'). Exit with error status 2 if any - input archive is an empty file. The input archives are kept unchanged. - Existing compressed archives are not overwritten. A hyphen '-' used as - the name of an input archive reads from standard input and writes to - standard output (unless the option '--output' is used). Tarlz can be - used as compressor for GNU tar by using a command like - 'tar -c -Hustar foo | tarlz -z -o foo.tar.lz'. Tarlz can be used as - compressor for zupdate (zutils) by using a command like + '--dsolid' works like '--asolid'). Each input archive is compressed to + a file with the extension '.lz' added unless the option '--output' is + used. If no archives are specified, or if a hyphen '-' is used as the + name of an archive, tarlz reads from standard input and writes to + standard output (unless the option '--output' is used). When + '--output' is used, only one input archive can be specified. Exit with + error status 2 if any input archive is an empty file. The input + archives are kept unchanged. Existing compressed archives are not + overwritten. Tarlz can be used as compressor for GNU tar by using a + command like 'tar -c -Hustar foo | tarlz -z -o foo.tar.lz'. Tarlz can + be used as compressor for zupdate (zutils) by using a command like 'zupdate --lz='tarlz -z' foo.tar.gz'. Note that tarlz only works reliably on archives without global headers, or with global headers whose content can be ignored. @@ -251,11 +255,8 @@ tarlz supports the following operations: archive. Unless solid compression is requested, the end-of-archive blocks are compressed in a lzip member separated from the preceding members and from any nonzero garbage following the end-of-archive - blocks. '--compress' implies plzip argument style, not tar style. Each - input archive is compressed to a file with the extension '.lz' added - unless the option '--output' is used. When '--output' is used, only - one input archive can be specified. '-f' can't be used with - '--compress'. + blocks. '--compress' implies plzip argument style, not tar style. '-f' + can't be used with '--compress'. '--check-lib' Compare the version of lzlib used to compile tarlz with the version @@ -276,7 +277,9 @@ tarlz supports the following options: *Note Argument syntax::. Set target size of input data blocks for the option '--bsolid'. *Note --bsolid::. Valid values range from 8 KiB to 1 GiB. Default value is two times the dictionary size, except for option '-0' where it - defaults to 1 MiB. *Note Minimum archive sizes::. + defaults to 1 MiB. *Note Minimum archive sizes::. Tarlz does not split + tar members. If a file is larger than BYTES, tarlz will create a lzip + member large enough to contain the file. '-C DIR' '--directory=DIR' @@ -424,12 +427,12 @@ tarlz supports the following options: *Note Argument syntax::. group ID. '--exclude=PATTERN' - Exclude files matching a shell pattern like '*.o'. A file is considered - to match if any component of the file name matches. For example, '*.o' - matches 'foo.o', 'foo.o/bar' and 'foo/bar.o'. If PATTERN contains a - '/', it matches a corresponding '/' in the file name. For example, - 'foo/*.o' matches 'foo/bar.o'. Multiple '--exclude' options can be - specified. + Exclude files matching a shell pattern like '*.o', even if the files + are specified in the command line. A file is considered to match if any + component of the file name matches. For example, '*.o' matches + 'foo.o', 'foo.o/bar' and 'foo/bar.o'. If PATTERN contains a '/', it + matches a corresponding '/' in the file name. For example, 'foo/*.o' + matches 'foo/bar.o'. Multiple '--exclude' options can be specified. '--ignore-ids' Make '--diff' ignore differences in owner and group IDs. This option is @@ -486,10 +489,10 @@ tarlz supports the following options: *Note Argument syntax::. During archive creation, warn if any file being archived has a modification time newer than the archive creation time. This option may slow archive creation somewhat because it makes an extra call to - 'stat' after archiving each file, but it guarantees that file contents - were not modified during the creation of the archive. Note that the - file must be at least one second newer than the archive for it to be - detected as newer. + 'stat' after archiving each file, but it nearly guarantees that file + contents were not modified during the creation of the archive. Note + that the file must be at least one second newer than the archive for + it to be detected as newer. Exit status: 0 for a normal exit, 1 for environmental problems (file not @@ -498,7 +501,7 @@ indicate a corrupt or invalid input file, 3 for an internal consistency error (e.g., bug) which caused tarlz to panic.  -File: tarlz.info, Node: Argument syntax, Next: Portable character set, Prev: Invoking tarlz, Up: Top +File: tarlz.info, Node: Argument syntax, Next: Creating backups safely, Prev: Invoking tarlz, Up: Top 3 Syntax of command-line arguments ********************************** @@ -541,9 +544,55 @@ GNU adds "long options" to these conventions: Thus, '--foo bar' and '--foo=bar' are equivalent.  -File: tarlz.info, Node: Portable character set, Next: File format, Prev: Argument syntax, Up: Top +File: tarlz.info, Node: Creating backups safely, Next: Portable character set, Prev: Argument syntax, Up: Top -4 POSIX portable filename character set +4 Checking the integrity and accuracy of tar.lz archives +******************************************************** + +Uncompressed tar archives do not offer any integrity checking for the files +they store. The pax format even fails to offer integrity checking for some +of the metadata. *Note crc32::. The integrity checking of tar archives is +usually provided by a compression layer or by an external hash. + + Lzip compression provides safe integrity checking to tar archives. But it +does not matter how safe is the archiving format if the archive is created +corrupt because of a concurrent modification of the files being archived, a +faulty RAM, or a bug in the archiving tool. The only way of guaranteeing +that a backup archive is correct is to check its integrity and accuracy +after creating it. + + Testing the integrity of the archive with 'lzip -tv' guarantees that the +compression layer of the archive is valid, but it does not guarantee that +the tar layer is valid nor that the files in the archive match the files in +the file system. For example, if the RAM is faulty and a bit flip happens +in the input buffer before tarlz compresses it, the archive will not match +the files. It is safer to check the archive with 'tarlz -d' just after +creation because it checks the compression layer and the tar layer, and it +compares the files in the archive with the files in the file system: + + tarlz -cf archive.tar.lz somedir # create the archive + tarlz -df archive.tar.lz # check the archive + + Once the integrity and accuracy of an archive have been verified as in +the example above, they can be verified again anywhere at any time with +'tarlz -t -n0'. It is important to disable multi-threading with '-n0' +because multi-threaded listing does not detect corruption in the tar member +data of multimember archives: *Note mt-listing::. + + tarlz -t -n0 -f archive.tar.lz > /dev/null + + 'lzip -tv' checks the integrity of the compression layer, and therefore +the integrity and accuracy of any archive created and verified as explained +above. This test is reliable for solidly compressed archives, but it does +not detect a truncated multimember archive if the truncation happens just +at a member boundary: + + lzip -tv archive.tar.lz + + +File: tarlz.info, Node: Portable character set, Next: File format, Prev: Creating backups safely, Up: Top + +5 POSIX portable filename character set *************************************** The set of characters from which portable file names are constructed. @@ -561,7 +610,7 @@ names use only the portable character set without spaces added.  File: tarlz.info, Node: File format, Next: Amendments to pax format, Prev: Portable character set, Up: Top -5 File format +6 File format ************* In the diagram below, a box like this: @@ -632,7 +681,7 @@ tar.lz | member | member | member | +===============+=================================================+========+ -5.1 Pax header block +6.1 Pax header block ==================== The pax header block is identical to the ustar header block described below @@ -676,7 +725,7 @@ space, equal-sign, and newline. previously archived. This record overrides the field 'linkname' in the following ustar header block. The following ustar header block determines the type of link created. If typeflag of the following - header block is 1, a hard link is created. If typeflag is 2, a + header block is '1', a hard link is created. If typeflag is '2', a symbolic link is created and the linkpath value is used as the contents of the symbolic link. The linkpath record is created only for links with a link name that does not fit in the space provided by the @@ -716,17 +765,17 @@ space, equal-sign, and newline. CRC32-C (Castagnoli) of the extended header data excluding the 8 bytes representing the CRC itself. The is represented as 8 hexadecimal digits in big endian order, '22 GNU.crc32=00000000\n'. The - keyword of the CRC record is protected by the CRC to guarantee that - corruption is always detected when using '--missing-crc' (except in - case of CRC collision). A CRC was chosen because a checksum is too - weak for a potentially large list of variable sized records. A + option '--missing-crc' guarantees that corruption is always detected + (except in case of CRC collision). A CRC was chosen because a checksum + is too weak for a potentially large list of variable sized records. A checksum can't detect simple errors like the swapping of two bytes. + *Note --missing-crc::. At verbosity level 1 or higher tarlz prints a diagnostic for each unknown extended header keyword found in an archive, once per keyword. -5.2 Ustar header block +6.2 Ustar header block ====================== The ustar header block has a length of 512 bytes and is structured as shown @@ -750,6 +799,7 @@ gname 297 32 devmajor 329 8 devminor 337 8 prefix 345 155 +padding 500 12 All characters in the header block are coded using the ISO/IEC 646:1991 (ASCII) standard, except in fields storing names for files, users, and @@ -839,7 +889,7 @@ file archived: ''7'' Reserved to represent a file to which an implementation has associated some high-performance attribute (contiguous file). Tarlz treats this - type of file as a regular file (type 0). + type of file as a regular file (type '0'). The field 'magic' contains the ASCII null-terminated string "ustar". The @@ -848,13 +898,13 @@ field 'version' contains the characters "00" (0x30,0x30). The fields characters in the array contain non-null characters including the last character. Each numeric field contains a leading space- or zero-filled, optionally null-terminated octal number using digits from the ISO/IEC -646:1991 (ASCII) standard. Tarlz is able to decode numeric fields 1 byte +646:1991 (ASCII) standard. Tarlz is able to decode numeric fields one byte longer than standard ustar by not requiring a terminating null character.  File: tarlz.info, Node: Amendments to pax format, Next: Program design, Prev: File format, Up: Top -6 The reasons for the differences with pax +7 The reasons for the differences with pax ****************************************** Tarlz creates safe archives that allow the reliable detection of invalid or @@ -865,7 +915,7 @@ achieve this goal and avoid some other flaws in the pax format, tarlz makes some changes to the variant of the pax format that it uses. This chapter describes these changes and the concrete reasons to implement them. -6.1 Add a CRC of the extended records +7.1 Add a CRC of the extended records ===================================== The POSIX pax format has a serious flaw. The metadata stored in pax extended @@ -892,7 +942,7 @@ place. Redundancy Check (CRC) in a way compatible with standard tar tools. *Note key_crc32::. -6.2 Remove flawed backward compatibility +7.2 Remove flawed backward compatibility ======================================== In order to allow the extraction of pax archives by a tar utility conforming @@ -925,7 +975,7 @@ trying to extract the file or link. This also makes easier during parallel decoding the detection of a tar member split between two lzip members at the boundary between the extended header and the ustar header. -6.3 As simple as possible (but not simpler) +7.3 As simple as possible (but not simpler) =========================================== The tarlz format is mainly ustar. Extended pax headers are used only when @@ -940,7 +990,7 @@ corruption. ignored. Some operations may not behave as expected if the archive contains global headers. -6.4 Improve reproducibility +7.4 Improve reproducibility =========================== Pax includes by default the process ID of the pax process in the ustar name @@ -952,7 +1002,7 @@ extended records, making it easier to produce reproducible archives. ten; '99<97_bytes>' or '100<97_bytes>'. Tarlz minimizes the length of the record and always produces a length of x-1 in these cases. -6.5 No data in hard links +7.5 No data in hard links ========================= Tarlz does not allow data in hard link members. The data (if any) must be in @@ -961,27 +1011,26 @@ the names of a file are stored as hard links, the type of the file is lost. Not allowing data in hard links also prevents invalid actions like extracting file data for a hard link to a symbolic link or to a directory. -6.6 Avoid misconversions to/from UTF-8 +7.6 Avoid misconversions to/from UTF-8 ====================================== There is no portable way to tell what charset a text string is coded into. Therefore, tarlz stores all fields representing text strings unmodified, without conversion to UTF-8 nor any other transformation. This prevents -accidental double UTF-8 conversions. If the need arises this behavior will -be adjusted with a command-line option in the future. +accidental double UTF-8 conversions.  File: tarlz.info, Node: Program design, Next: Multi-threaded decoding, Prev: Amendments to pax format, Up: Top -7 Internal structure of tarlz +8 Internal structure of tarlz ***************************** The parts of tarlz related to sequential processing of the archive are more or less similar to any other tar and won't be described here. The -interesting parts described here are those related to Multi-threaded +interesting parts described here are those related to multi-threaded processing. - The structure of the part of tarlz performing Multi-threaded archive + The structure of the part of tarlz performing multi-threaded archive creation is somewhat similar to that of plzip with the added complication of the solidity levels. *Note Program design: (plzip)Program design. A grouper thread and several worker threads are created, acting the main @@ -1053,7 +1102,7 @@ error be avoided.  File: tarlz.info, Node: Multi-threaded decoding, Next: Minimum archive sizes, Prev: Program design, Up: Top -8 Limitations of parallel tar decoding +9 Limitations of parallel tar decoding ************************************** Safely decoding a tar archive in parallel is only possible if one decodes @@ -1093,11 +1142,14 @@ tar.lz archives, keeping backwards compatibility. If tarlz finds a member misalignment during multi-threaded decoding, it switches to single-threaded mode and continues decoding the archive. - If the files in the archive are large, multi-threaded '--list' on a -regular (seekable) tar.lz archive can be hundreds of times faster than -sequential '--list' because, in addition to using several processors, it -only needs to decompress part of each lzip member. See the following -example listing the Silesia corpus on a dual core machine: +9.1 Multi-threaded listing +========================== + +If the files in the archive are large, multi-threaded '--list' on a regular +(seekable) tar.lz archive can be hundreds of times faster than sequential +'--list' because, in addition to using several processors, it only needs to +decompress part of each lzip member. See the following example listing the +Silesia corpus on a dual core machine: tarlz -9 --no-solid -cf silesia.tar.lz silesia time lzip -cd silesia.tar.lz | tar -tf - (5.032s) @@ -1106,10 +1158,12 @@ example listing the Silesia corpus on a dual core machine: On the other hand, multi-threaded '--list' won't detect corruption in the tar member data because it only decodes the part of each lzip member -corresponding to the tar member header. This is another reason why the tar -headers must provide their own integrity checking. +corresponding to the tar member header. Partial decoding of a lzip member +can't guarantee the integrity of the data decoded. This is another reason +why the tar headers (including the extended records) must provide their own +integrity checking. -8.1 Limitations of multi-threaded extraction +9.2 Limitations of multi-threaded extraction ============================================ Multi-threaded extraction may produce different output than single-threaded @@ -1139,8 +1193,8 @@ links to.  File: tarlz.info, Node: Minimum archive sizes, Next: Examples, Prev: Multi-threaded decoding, Up: Top -9 Minimum archive sizes required for multi-threaded block compression -********************************************************************* +10 Minimum archive sizes required for multi-threaded block compression +********************************************************************** When creating or appending to a compressed archive using multi-threaded block compression, tarlz puts tar members together in blocks and compresses @@ -1177,7 +1231,7 @@ Level  File: tarlz.info, Node: Examples, Next: Problems, Prev: Minimum archive sizes, Up: Top -10 A small tutorial with examples +11 A small tutorial with examples ********************************* Example 1: Create a multimember compressed archive 'archive.tar.lz' @@ -1233,10 +1287,12 @@ other members can still be extracted). tarlz -z --no-solid archive.tar -Example 10: Compress the archive 'archive.tar' and write the output to -'foo.tar.lz'. +Example 10: Recompress the archive 'archive.tar.lz' with different +solidity, write the output to 'archive-ns.tar.lz', and compare both +archives. - tarlz -z -o foo.tar.lz archive.tar + lzip -cd archive.tar.lz | tarlz -9z --no-solid -o archive-ns.tar.lz + zcmp archive.tar.lz archive-ns.tar.lz Example 11: Concatenate and compress two archives 'archive1.tar' and 'archive2.tar', and write the output to 'foo.tar.lz'. @@ -1246,7 +1302,7 @@ Example 11: Concatenate and compress two archives 'archive1.tar' and  File: tarlz.info, Node: Problems, Next: Concept index, Prev: Examples, Up: Top -11 Reporting bugs +12 Reporting bugs ***************** There are probably bugs in tarlz. There are certainly errors and omissions @@ -1270,6 +1326,7 @@ Concept index * Amendments to pax format: Amendments to pax format. (line 6) * argument syntax: Argument syntax. (line 6) * bugs: Problems. (line 6) +* creating backups: Creating backups safely. (line 6) * examples: Examples. (line 6) * file format: File format. (line 6) * getting help: Problems. (line 6) @@ -1287,26 +1344,29 @@ Concept index  Tag Table: Node: Top216 -Node: Introduction1281 -Node: Invoking tarlz4106 -Ref: --data-size13109 -Ref: --bsolid17626 -Node: Argument syntax23539 -Node: Portable character set25314 -Node: File format25958 -Ref: key_crc3233001 -Ref: ustar-uid-gid36305 -Ref: ustar-mtime37112 -Node: Amendments to pax format39115 -Ref: crc3239823 -Ref: flawed-compat41134 -Node: Program design45211 -Node: Multi-threaded decoding49138 -Ref: mt-extraction52407 -Node: Minimum archive sizes53713 -Node: Examples55840 -Node: Problems58199 -Node: Concept index58754 +Node: Introduction1356 +Node: Invoking tarlz4179 +Ref: --data-size13265 +Ref: --bsolid17924 +Ref: --missing-crc21532 +Node: Argument syntax23897 +Node: Creating backups safely25673 +Node: Portable character set28057 +Node: File format28709 +Ref: key_crc3235756 +Ref: ustar-uid-gid39052 +Ref: ustar-mtime39859 +Node: Amendments to pax format41866 +Ref: crc3242574 +Ref: flawed-compat43885 +Node: Program design47870 +Node: Multi-threaded decoding51797 +Ref: mt-listing54198 +Ref: mt-extraction55236 +Node: Minimum archive sizes56542 +Node: Examples58671 +Node: Problems61166 +Node: Concept index61721  End Tag Table diff --git a/doc/tarlz.texi b/doc/tarlz.texi index 79c145d..40ee137 100644 --- a/doc/tarlz.texi +++ b/doc/tarlz.texi @@ -6,8 +6,8 @@ @finalout @c %**end of header -@set UPDATED 7 December 2024 -@set VERSION 0.26 +@set UPDATED 28 February 2025 +@set VERSION 0.27 @dircategory Archiving @direntry @@ -39,6 +39,7 @@ This manual is for Tarlz (version @value{VERSION}, @value{UPDATED}). * Introduction:: Purpose and features of tarlz * Invoking tarlz:: Command-line interface * Argument syntax:: By convention, options start with a hyphen +* Creating backups safely:: Checking integrity and accuracy of archives * Portable character set:: POSIX portable filename character set * File format:: Detailed format of the compressed archive * Amendments to pax format:: The reasons for the differences with pax @@ -51,7 +52,7 @@ This manual is for Tarlz (version @value{VERSION}, @value{UPDATED}). @end menu @sp 1 -Copyright @copyright{} 2013-2024 Antonio Diaz Diaz. +Copyright @copyright{} 2013-2025 Antonio Diaz Diaz. This manual is free documentation: you have unlimited permission to copy, distribute, and modify it. @@ -76,7 +77,7 @@ compressed archives. Keeping the alignment between tar members and lzip members has two advantages. It adds an indexed lzip layer on top of the tar archive, making -it possible to decode the archive safely in parallel. It also minimizes the +it possible to decode the archive safely in parallel. It also reduces the amount of data lost in case of corruption. Compressing a tar archive with plzip may even double the amount of files lost for each lzip member damaged because it does not keep the members aligned. @@ -254,7 +255,7 @@ during multi-threaded extraction. @xref{mt-extraction}. @item -t @itemx --list List the contents of an archive. If @var{files} are given, list only the -@var{files} given. +@var{files} given. @xref{mt-listing}. @item -x @itemx --extract @@ -265,20 +266,23 @@ directory without extracting the files under it, use empty directories unconditionally before extracting over them. Other than that, it does not make any special effort to extract a file over an incompatible type of file. For example, extracting a file over a non-empty -directory usually fails. +directory usually fails. @xref{mt-extraction}. @item -z @itemx --compress Compress existing POSIX tar archives aligning the lzip members to the tar members with choice of granularity (@option{--bsolid} by default, -@option{--dsolid} works like @option{--asolid}). Exit with error status 2 if -any input archive is an empty file. The input archives are kept unchanged. -Existing compressed archives are not overwritten. A hyphen @samp{-} used as -the name of an input archive reads from standard input and writes to -standard output (unless the option @option{--output} is used). Tarlz can be -used as compressor for GNU tar by using a command like -@w{@samp{tar -c -Hustar foo | tarlz -z -o foo.tar.lz}}. Tarlz can be used as -compressor for zupdate (zutils) by using a command like +@option{--dsolid} works like @option{--asolid}). Each input archive is +compressed to a file with the extension @file{.lz} added unless the option +@option{--output} is used. If no archives are specified, or if a hyphen +@samp{-} is used as the name of an archive, tarlz reads from standard input +and writes to standard output (unless the option @option{--output} is used). +When @option{--output} is used, only one input archive can be specified. +Exit with error status 2 if any input archive is an empty file. The input +archives are kept unchanged. Existing compressed archives are not +overwritten. Tarlz can be used as compressor for GNU tar by using a command +like @w{@samp{tar -c -Hustar foo | tarlz -z -o foo.tar.lz}}. Tarlz can be +used as compressor for zupdate (zutils) by using a command like @w{@samp{zupdate --lz='tarlz -z' foo.tar.gz}}. Note that tarlz only works reliably on archives without global headers, or with global headers whose content can be ignored. @@ -289,10 +293,8 @@ block is found, and then compresses the rest of the archive. Unless solid compression is requested, the end-of-archive blocks are compressed in a lzip member separated from the preceding members and from any nonzero garbage following the end-of-archive blocks. @option{--compress} implies plzip -argument style, not tar style. Each input archive is compressed to a file -with the extension @file{.lz} added unless the option @option{--output} is -used. When @option{--output} is used, only one input archive can be specified. -@option{-f} can't be used with @option{--compress}. +argument style, not tar style. @option{-f} can't be used with +@option{--compress}. @item --check-lib Compare the @@ -319,8 +321,10 @@ tarlz supports the following options: @xref{Argument syntax}. @itemx --data-size=@var{bytes} Set target size of input data blocks for the option @option{--bsolid}. @xref{--bsolid}. Valid values range from @w{8 KiB} to @w{1 GiB}. Default -value is two times the dictionary size, except for option @option{-0} where it -defaults to @w{1 MiB}. @xref{Minimum archive sizes}. +value is two times the dictionary size, except for option @option{-0} where +it defaults to @w{1 MiB}. @xref{Minimum archive sizes}. Tarlz does not split +tar members. If a file is larger than @var{bytes}, tarlz will create a lzip +member large enough to contain the file. @item -C @var{dir} @itemx --directory=@var{dir} @@ -465,12 +469,13 @@ If @var{group} is not a valid group name, it is decoded as a decimal numeric group ID. @item --exclude=@var{pattern} -Exclude files matching a shell pattern like @file{*.o}. A file is considered -to match if any component of the file name matches. For example, @file{*.o} -matches @file{foo.o}, @file{foo.o/bar} and @file{foo/bar.o}. If -@var{pattern} contains a @samp{/}, it matches a corresponding @samp{/} in -the file name. For example, @file{foo/*.o} matches @file{foo/bar.o}. -Multiple @option{--exclude} options can be specified. +Exclude files matching a shell pattern like @file{*.o}, even if the files +are specified in the command line. A file is considered to match if any +component of the file name matches. For example, @file{*.o} matches +@file{foo.o}, @file{foo.o/bar} and @file{foo/bar.o}. If @var{pattern} +contains a @samp{/}, it matches a corresponding @samp{/} in the file name. +For example, @file{foo/*.o} matches @file{foo/bar.o}. Multiple +@option{--exclude} options can be specified. @item --ignore-ids Make @option{--diff} ignore differences in owner and group IDs. This option is @@ -493,6 +498,7 @@ recover as much data as possible from each damaged member. It is recommended to run tarlz in single-threaded mode (@option{--threads=0}) when using this option. +@anchor{--missing-crc} @item --missing-crc Exit with error status 2 if the CRC of the extended records is missing. When this option is used, tarlz detects any corruption in the extended records @@ -525,9 +531,9 @@ values range from 1 to 1024. The default value is 64. During archive creation, warn if any file being archived has a modification time newer than the archive creation time. This option may slow archive creation somewhat because it makes an extra call to @samp{stat} after -archiving each file, but it guarantees that file contents were not modified -during the creation of the archive. Note that the file must be at least one -second newer than the archive for it to be detected as newer. +archiving each file, but it nearly guarantees that file contents were not +modified during the creation of the archive. Note that the file must be at +least one second newer than the archive for it to be detected as newer. @ignore @item --permissive @@ -591,6 +597,58 @@ Thus, @w{@option{--foo bar}} and @option{--foo=bar} are equivalent. @end itemize +@node Creating backups safely +@chapter Checking the integrity and accuracy of tar.lz archives +@cindex creating backups + +Uncompressed tar archives do not offer any integrity checking for the files +they store. The pax format even fails to offer integrity checking for some +of the metadata. @xref{crc32}. The integrity checking of tar archives is +usually provided by a compression layer or by an external hash. + +Lzip compression provides safe integrity checking to tar archives. But it +does not matter how safe is the archiving format if the archive is created +corrupt because of a concurrent modification of the files being archived, a +faulty RAM, or a bug in the archiving tool. The only way of guaranteeing +that a backup archive is correct is to check its integrity and accuracy +after creating it. + +Testing the integrity of the archive with @w{@samp{lzip -tv}} guarantees +that the compression layer of the archive is valid, but it does not +guarantee that the tar layer is valid nor that the files in the archive +match the files in the file system. For example, if the RAM is faulty and a +bit flip happens in the input buffer before tarlz compresses it, the archive +will not match the files. It is safer to check the archive with +@w{@samp{tarlz -d}} just after creation because it checks the compression +layer and the tar layer, and it compares the files in the archive with the +files in the file system: + +@example +tarlz -cf archive.tar.lz somedir # create the archive +tarlz -df archive.tar.lz # check the archive +@end example + +Once the integrity and accuracy of an archive have been verified as in the +example above, they can be verified again anywhere at any time with +@w{@samp{tarlz -t -n0}}. It is important to disable multi-threading with +@option{-n0} because multi-threaded listing does not detect corruption in +the tar member data of multimember archives: @xref{mt-listing}. + +@example +tarlz -t -n0 -f archive.tar.lz > /dev/null +@end example + +@w{@samp{lzip -tv}} checks the integrity of the compression layer, and +therefore the integrity and accuracy of any archive created and verified as +explained above. This test is reliable for solidly compressed archives, but +it does not detect a truncated multimember archive if the truncation happens +just at a member boundary: + +@example +lzip -tv archive.tar.lz +@end example + + @node Portable character set @chapter POSIX portable filename character set @cindex portable character set @@ -641,7 +699,7 @@ are not allowed in multimember files. Each lzip member contains one or more tar members in a simplified POSIX pax interchange format. The only pax typeflag value supported by tarlz (in -addition to the typeflag values defined by the ustar format) is @samp{x}. +addition to the typeflag values defined by the ustar format) is 'x'. The pax format is an extension on top of the ustar format that removes the size limitations of the ustar format. @@ -654,7 +712,7 @@ An optional extended header block followed by one or more blocks that contain the extended header records as if they were the contents of a file; i.e., the extended header records are included as the data for this header block. This header block is of the form described in pax header block, with -a typeflag value of @samp{x}. +a typeflag value of 'x'. @item A header block in ustar format that describes the file. Any fields defined @@ -713,7 +771,7 @@ An extended header just before the end-of-archive blocks. @section Pax header block The pax header block is identical to the ustar header block described below -except that the typeflag has the value @samp{x} (extended). The field +except that the typeflag has the value 'x' (extended). The field @samp{size} is the size of the extended header data in bytes. Most other fields in the pax header block are zeroed on archive creation to prevent trouble if the archive is read by a ustar tool, and are ignored by tarlz on @@ -752,8 +810,8 @@ greater than 2_097_151 @w{(octal 7_777_777)}. @xref{ustar-uid-gid}. The file name of a link being created to another file, of any type, previously archived. This record overrides the field @samp{linkname} in the following ustar header block. The following ustar header block determines -the type of link created. If typeflag of the following header block is 1, a -hard link is created. If typeflag is 2, a symbolic link is created and the +the type of link created. If typeflag of the following header block is '1', a +hard link is created. If typeflag is '2', a symbolic link is created and the linkpath value is used as the contents of the symbolic link. The linkpath record is created only for links with a link name that does not fit in the space provided by the ustar header. @@ -789,13 +847,12 @@ greater than 2_097_151 @w{(octal 7_777_777)}. @xref{ustar-uid-gid}. @item GNU.crc32 CRC32-C (Castagnoli) of the extended header data excluding the 8 bytes representing the CRC itself. The is represented as 8 -hexadecimal digits in big endian order, -@w{@samp{22 GNU.crc32=00000000\n}}. The keyword of the CRC record is -protected by the CRC to guarantee that corruption is always detected when -using @option{--missing-crc} (except in case of CRC collision). A CRC was -chosen because a checksum is too weak for a potentially large list of -variable sized records. A checksum can't detect simple errors like the -swapping of two bytes. +hexadecimal digits in big endian order, @w{@samp{22 GNU.crc32=00000000\n}}. +The option @option{--missing-crc} guarantees that corruption is always +detected (except in case of CRC collision). A CRC was chosen because a +checksum is too weak for a potentially large list of variable sized records. +A checksum can't detect simple errors like the swapping of two bytes. +@xref{--missing-crc}. @end table @@ -825,6 +882,7 @@ shown in the following table. All lengths and offsets are in decimal: @item devmajor @tab 329 @tab 8 @item devminor @tab 337 @tab 8 @item prefix @tab 345 @tab 155 +@item padding @tab 500 @tab 12 @end multitable All characters in the header block are coded using the ISO/IEC 646:1991 @@ -919,7 +977,7 @@ FIFO special file. @item '7' Reserved to represent a file to which an implementation has associated some high-performance attribute (contiguous file). Tarlz treats this type of file -as a regular file (type 0). +as a regular file (type '0'). @end table @@ -930,8 +988,8 @@ except when all characters in the array contain non-null characters including the last character. Each numeric field contains a leading space- or zero-filled, optionally null-terminated octal number using digits from the ISO/IEC 646:1991 (ASCII) standard. Tarlz is able to decode numeric -fields 1 byte longer than standard ustar by not requiring a terminating null -character. +fields one byte longer than standard ustar by not requiring a terminating +null character. @node Amendments to pax format @@ -1044,8 +1102,7 @@ extracting file data for a hard link to a symbolic link or to a directory. There is no portable way to tell what charset a text string is coded into. Therefore, tarlz stores all fields representing text strings unmodified, without conversion to UTF-8 nor any other transformation. This prevents -accidental double UTF-8 conversions. If the need arises this behavior will -be adjusted with a command-line option in the future. +accidental double UTF-8 conversions. @node Program design @@ -1054,12 +1111,12 @@ be adjusted with a command-line option in the future. The parts of tarlz related to sequential processing of the archive are more or less similar to any other tar and won't be described here. The interesting -parts described here are those related to Multi-threaded processing. +parts described here are those related to multi-threaded processing. -The structure of the part of tarlz performing Multi-threaded archive +The structure of the part of tarlz performing multi-threaded archive creation is somewhat similar to that of -@uref{http://www.nongnu.org/lzip/plzip.html#Program-design,,plzip} with the -added complication of the solidity levels. +@uref{http://www.nongnu.org/lzip/manual/plzip_manual.html#Program-design,,plzip} +with the added complication of the solidity levels. @ifnothtml @xref{Program design,,,plzip}. @end ifnothtml @@ -1174,6 +1231,9 @@ tar.lz archives, keeping backwards compatibility. If tarlz finds a member misalignment during multi-threaded decoding, it switches to single-threaded mode and continues decoding the archive. +@anchor{mt-listing} +@section Multi-threaded listing + If the files in the archive are large, multi-threaded @option{--list} on a regular (seekable) tar.lz archive can be hundreds of times faster than sequential @option{--list} because, in addition to using several processors, @@ -1189,8 +1249,10 @@ time tarlz -tf silesia.tar.lz (0.020s) On the other hand, multi-threaded @option{--list} won't detect corruption in the tar member data because it only decodes the part of each lzip member -corresponding to the tar member header. This is another reason why the tar -headers must provide their own integrity checking. +corresponding to the tar member header. Partial decoding of a lzip member +can't guarantee the integrity of the data decoded. This is another reason +why the tar headers (including the extended records) must provide their own +integrity checking. @anchor{mt-extraction} @section Limitations of multi-threaded extraction @@ -1344,11 +1406,13 @@ tarlz -z --no-solid archive.tar @end example @noindent -Example 10: Compress the archive @file{archive.tar} and write the output to -@file{foo.tar.lz}. +Example 10: Recompress the archive @file{archive.tar.lz} with different +solidity, write the output to @file{archive-ns.tar.lz}, and compare both +archives. @example -tarlz -z -o foo.tar.lz archive.tar +lzip -cd archive.tar.lz | tarlz -9z --no-solid -o archive-ns.tar.lz +zcmp archive.tar.lz archive-ns.tar.lz @end example @noindent diff --git a/exclude.cc b/exclude.cc index 44a53a5..8854e0e 100644 --- a/exclude.cc +++ b/exclude.cc @@ -1,5 +1,5 @@ /* Tarlz - Archiver with multimember lzip compression - Copyright (C) 2013-2024 Antonio Diaz Diaz. + Copyright (C) 2013-2025 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 diff --git a/extended.cc b/extended.cc index 0dfba9b..daccbbf 100644 --- a/extended.cc +++ b/extended.cc @@ -1,5 +1,5 @@ /* Tarlz - Archiver with multimember lzip compression - Copyright (C) 2013-2024 Antonio Diaz Diaz. + Copyright (C) 2013-2025 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 @@ -22,6 +22,7 @@ #include #include "tarlz.h" +#include "common_mutex.h" const CRC32 crc32c( true ); @@ -58,9 +59,9 @@ long long parse_decimal( const char * const ptr, const char ** const tailp, } -uint32_t parse_record_crc( const char * const ptr ) +unsigned parse_record_crc( const char * const ptr ) { - uint32_t crc = 0; + unsigned crc = 0; for( int i = 0; i < 8; ++i ) { crc <<= 4; @@ -201,16 +202,25 @@ void Extended::calculate_sizes() const // print a diagnostic for each unknown keyword once per keyword -void Extended::unknown_keyword( const char * const buf, const int size ) const +void Extended::unknown_keyword( const char * const buf, const int size, + std::vector< std::string > * const msg_vecp ) const { + // prevent two threads from modifying the list of keywords at the same time + static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; int eq_pos = 0; // position of '=' in buf + while( eq_pos < size && buf[eq_pos] != '=' ) ++eq_pos; const std::string keyword( buf, eq_pos ); + xlock( &mutex ); for( unsigned i = 0; i < unknown_keywords.size(); ++i ) - if( keyword == unknown_keywords[i] ) return; + if( keyword == unknown_keywords[i] ) { xunlock( &mutex ); return; } unknown_keywords.push_back( keyword ); - print_error( 0, "Ignoring unknown extended header keyword '%s'", - keyword.c_str() ); + xunlock( &mutex ); + const char * str = "Ignoring unknown extended header keyword '%s'"; + if( !msg_vecp ) print_error( 0, str, keyword.c_str() ); + else + { msg_vecp->push_back( std::string() ); + format_error( msg_vecp->back(), 0, str, keyword.c_str() ); } } @@ -281,7 +291,8 @@ const char * Extended::full_size_error() const bool Extended::parse( const char * const buf, const int edsize, - const bool permissive ) + const bool permissive, + std::vector< std::string > * const msg_vecp ) { reset(); full_size_ = -4; // invalidate cached sizes for( int pos = 0; pos < edsize; ) // parse records @@ -348,18 +359,22 @@ bool Extended::parse( const char * const buf, const int edsize, if( crc_present_ && !permissive ) return false; if( rsize != (int)crc_record.size() ) return false; crc_present_ = true; - const uint32_t stored_crc = parse_record_crc( tail + 10 ); - const uint32_t computed_crc = + const unsigned stored_crc = parse_record_crc( tail + 10 ); + const unsigned computed_crc = crc32c.windowed_crc( (const uint8_t *)buf, pos + rsize - 9, edsize ); if( stored_crc != computed_crc ) { - if( verbosity >= 2 ) - std::fprintf( stderr, "CRC32-C = %08X\n", (unsigned)computed_crc ); + if( verbosity < 1 ) return false; + const char * str = "CRC mismatch in extended records; stored %08X, computed %08X"; + if( !msg_vecp ) print_error( 0, str, stored_crc, computed_crc ); + else + { msg_vecp->push_back( std::string() ); + format_error( msg_vecp->back(), 0, str, stored_crc, computed_crc ); } return false; } } else if( ( rest < 8 || std::memcmp( tail, "comment=", 8 ) != 0 ) && - verbosity >= 1 ) unknown_keyword( tail, rest ); + verbosity >= 1 ) unknown_keyword( tail, rest, msg_vecp ); pos += rsize; } return true; diff --git a/lzip_index.cc b/lzip_index.cc index a8e7333..9d9c43a 100644 --- a/lzip_index.cc +++ b/lzip_index.cc @@ -1,5 +1,5 @@ /* Tarlz - Archiver with multimember lzip compression - Copyright (C) 2013-2024 Antonio Diaz Diaz. + Copyright (C) 2013-2025 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 diff --git a/lzip_index.h b/lzip_index.h index de8fe19..81ea3fd 100644 --- a/lzip_index.h +++ b/lzip_index.h @@ -1,5 +1,5 @@ /* Tarlz - Archiver with multimember lzip compression - Copyright (C) 2013-2024 Antonio Diaz Diaz. + Copyright (C) 2013-2025 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 diff --git a/main.cc b/main.cc index b77ad4d..013c43f 100644 --- a/main.cc +++ b/main.cc @@ -1,5 +1,5 @@ /* Tarlz - Archiver with multimember lzip compression - Copyright (C) 2013-2024 Antonio Diaz Diaz. + Copyright (C) 2013-2025 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 @@ -57,7 +57,7 @@ const char * const program_name = "tarlz"; namespace { -const char * const program_year = "2024"; +const char * const program_year = "2025"; const char * invocation_name = program_name; // default value @@ -74,7 +74,7 @@ void show_help( const long num_online ) "compressed archives.\n" "\nKeeping the alignment between tar members and lzip members has two\n" "advantages. It adds an indexed lzip layer on top of the tar archive, making\n" - "it possible to decode the archive safely in parallel. It also minimizes the\n" + "it possible to decode the archive safely in parallel. It also reduces the\n" "amount of data lost in case of corruption.\n" "\nThe tarlz file format is a safe POSIX-style backup format. In case of\n" "corruption, tarlz can extract all the undamaged members from the tar.lz\n" @@ -457,17 +457,43 @@ bool format_error( Resizable_buffer & rbuf, const int errcode, for( int i = 0; i < 2; ++i ) // resize rbuf if not large enough { int len = snprintf( rbuf(), rbuf.size(), "%s: ", program_name ); - if( len >= (int)rbuf.size() && !rbuf.resize( len + 1 ) ) break; + if( !rbuf.resize( len + 1 ) ) break; va_start( args, format ); len += vsnprintf( rbuf() + len, rbuf.size() - len, format, args ); va_end( args ); - if( len >= (int)rbuf.size() && !rbuf.resize( len + 1 ) ) break; - if( errcode <= 0 ) rbuf()[len++] = '\n'; + if( !rbuf.resize( len + 2 ) ) break; + if( errcode <= 0 ) { rbuf()[len++] = '\n'; rbuf()[len] = 0; } else len += snprintf( rbuf() + len, rbuf.size() - len, ": %s\n", std::strerror( errcode ) ); - if( len < (int)rbuf.size() || !rbuf.resize( len + 1 ) ) break; + if( len < (int)rbuf.size() ) return true; + if( i > 0 || !rbuf.resize( len + 1 ) ) break; } - return true; + return false; + } + + +bool format_error( std::string & msg, const int errcode, + const char * const format, ... ) + { + if( verbosity < 0 ) return false; + Resizable_buffer rbuf; + if( !rbuf.size() ) return false; + va_list args; + for( int i = 0; i < 2; ++i ) // resize rbuf if not large enough + { + int len = snprintf( rbuf(), rbuf.size(), "%s: ", program_name ); + if( !rbuf.resize( len + 1 ) ) break; + va_start( args, format ); + len += vsnprintf( rbuf() + len, rbuf.size() - len, format, args ); + va_end( args ); + if( !rbuf.resize( len + 2 ) ) break; + if( errcode <= 0 ) { rbuf()[len++] = '\n'; rbuf()[len] = 0; } + else len += snprintf( rbuf() + len, rbuf.size() - len, ": %s\n", + std::strerror( errcode ) ); + if( len < (int)rbuf.size() ) { msg.assign( rbuf(), len ); return true; } + if( i > 0 || !rbuf.resize( len + 1 ) ) break; + } + return false; } diff --git a/tarlz.h b/tarlz.h index 5c91cfa..c0c5007 100644 --- a/tarlz.h +++ b/tarlz.h @@ -1,5 +1,5 @@ /* Tarlz - Archiver with multimember lzip compression - Copyright (C) 2013-2024 Antonio Diaz Diaz. + Copyright (C) 2013-2025 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 @@ -120,7 +120,7 @@ public: } return true; } - char * operator()() { return p; } + char * operator()() { return p; } // no need for operator[] const char * operator()() const { return p; } uint8_t * u8() { return (uint8_t *)p; } const uint8_t * u8() const { return (const uint8_t *)p; } @@ -184,7 +184,8 @@ class Extended // stores metadata from/for extended records mutable bool crc_present_; void calculate_sizes() const; - void unknown_keyword( const char * const buf, const int size ) const; + void unknown_keyword( const char * const buf, const int size, + std::vector< std::string > * const msg_vecp = 0 ) const; public: static const std::string crc_record; @@ -234,7 +235,8 @@ public: bool crc_present() const { return crc_present_; } bool parse( const char * const buf, const int edsize, - const bool permissive ); + const bool permissive, + std::vector< std::string > * const msg_vecp = 0 ); void fill_from_ustar( const Tar_header header ); }; @@ -279,7 +281,7 @@ public: return crc ^ 0xFFFFFFFFU; } - // Calculates the crc of size bytes except a window of 8 bytes at pos + // compute the crc of size bytes except a window of 8 bytes at pos uint32_t windowed_crc( const uint8_t * const buffer, const int pos, const int size ) const { @@ -485,8 +487,9 @@ const char * const posix_lz_msg = "This does not look like a POSIX tar.lz archiv const char * const eclosa_msg = "Error closing archive"; const char * const eclosf_msg = "Error closing file"; const char * const nfound_msg = "Not found in archive."; -const char * const seek_msg = "Seek error"; +const char * const rd_err_msg = "Read error"; const char * const wr_err_msg = "Write error"; +const char * const seek_msg = "Seek error"; const char * const chdir_msg = "Error changing working directory"; const char * const intdir_msg = "Failed to create intermediate directory"; @@ -503,7 +506,8 @@ bool show_member_name( const Extended & extended, const Tar_header header, const int vlevel, Resizable_buffer & rbuf ); bool check_skip_filename( const Cl_options & cl_opts, std::vector< char > & name_pending, - const char * const filename, const int chdir_fd = -1 ); + const char * const filename, const int cwd_fd = -1, + std::string * const msgp = 0 ); bool make_dirs( const std::string & name ); // defined in common_mutex.cc @@ -597,6 +601,8 @@ void show_error( const char * const msg, const int errcode = 0, const bool help = false ); bool format_error( Resizable_buffer & rbuf, const int errcode, const char * const format, ... ); +bool format_error( std::string & msg, const int errcode, + const char * const format, ... ); void print_error( const int errcode, const char * const format, ... ); void format_file_error( std::string & estr, const char * const filename, const char * const msg, const int errcode = 0 ); diff --git a/testsuite/check.sh b/testsuite/check.sh index 3a7fef4..c3a8aa1 100755 --- a/testsuite/check.sh +++ b/testsuite/check.sh @@ -1,6 +1,6 @@ #! /bin/sh # check script for Tarlz - Archiver with multimember lzip compression -# Copyright (C) 2013-2024 Antonio Diaz Diaz. +# Copyright (C) 2013-2025 Antonio Diaz Diaz. # # This script is free software: you have unlimited permission # to copy, distribute, and modify it. @@ -23,97 +23,91 @@ fi echo "Try bash -c \"$0 $1 $2\"" exit 1 } +/bin/sh -c "lzip --version" > /dev/null 2>&1 || + { echo "$0: lzip is needed to run the tests" ; exit 1 ; } if [ -d tmp ] ; then rm -rf tmp ; fi mkdir tmp cd "${objdir}"/tmp || framework_failure in="${testdir}"/test.txt -in_lz="${testdir}"/test.txt.lz -in_tar="${testdir}"/test.txt.tar -in_tar_lz="${testdir}"/test.txt.tar.lz -inbad1="${testdir}"/test_bad1.txt -inbad2="${testdir}"/test_bad2.txt +lzip -cs18KiB "${in}" > in.lz || framework_failure +cp "${testdir}"/test.txt.tar.lz in.tar.lz || framework_failure +lzip -cd in.tar.lz > in.tar || framework_failure +test_bad1="${testdir}"/test_bad1.txt +test_bad2="${testdir}"/test_bad2.txt em_lz="${testdir}"/em.lz -test3="${testdir}"/test3.tar -test3_lz="${testdir}"/test3.tar.lz -test3dir="${testdir}"/test3_dir.tar -test3dir_lz="${testdir}"/test3_dir.tar.lz +cp "${testdir}"/test3.tar.lz t3.tar.lz || framework_failure +lzip -cd t3.tar.lz > t3.tar || framework_failure +cp "${testdir}"/test3_dir.tar.lz t3_dir.tar.lz || framework_failure +lzip -cd t3_dir.tar.lz > t3_dir.tar || framework_failure test3dot_lz="${testdir}"/test3_dot.tar.lz -t155="${testdir}"/t155.tar -t155_lz="${testdir}"/t155.tar.lz -tlzit1="${testdir}"/tlz_in_tar1.tar -tlzit2="${testdir}"/tlz_in_tar2.tar -bad1="${testdir}"/test3_bad1.tar -bad2="${testdir}"/test3_bad2.tar -bad3="${testdir}"/test3_bad3.tar -bad4="${testdir}"/test3_bad4.tar -bad5="${testdir}"/test3_bad5.tar -bad1_lz="${testdir}"/test3_bad1.tar.lz -bad2_lz="${testdir}"/test3_bad2.tar.lz -bad3_lz="${testdir}"/test3_bad3.tar.lz -bad4_lz="${testdir}"/test3_bad4.tar.lz -bad5_lz="${testdir}"/test3_bad5.tar.lz -bad6_lz="${testdir}"/test3_bad6.tar.lz -eoa="${testdir}"/eoa_blocks.tar -eoa_lz="${testdir}"/eoa_blocks.tar.lz +cp "${testdir}"/t155.tar.lz t155.tar.lz || framework_failure +lzip -cd t155.tar.lz > t155.tar || framework_failure +cp "${testdir}"/eoa_blocks.lz eoa.lz || framework_failure +lzip -cd eoa.lz > eoa || framework_failure +t3_bad1_lz="${testdir}"/test3_bad1.tar.lz +t3_bad2_lz="${testdir}"/test3_bad2.tar.lz +t3_bad3_lz="${testdir}"/test3_bad3.tar.lz +t3_bad4_lz="${testdir}"/test3_bad4.tar.lz +t3_bad5_lz="${testdir}"/test3_bad5.tar.lz fail=0 lwarnc=0 test_failed() { fail=1 ; printf " $1" ; [ -z "$2" ] || printf "($2)" ; } -is_compressed() { [ "`dd if="$1" bs=4 count=1 2> /dev/null`" = LZIP ] ; } -is_uncompressed() { [ "`dd if="$1" bs=4 count=1 2> /dev/null`" != LZIP ] ; } +is_compressed() { lzip -lq "$1" ; } +is_uncompressed() { lzip -lq "$1" ; [ $? = 2 ] ; } cyg_symlink() { [ ${lwarnc} = 0 ] && printf "\nwarning: your OS follows symbolic links to directories even when tarlz asks it not to\n$1" lwarnc=1 ; } -# Description of test files for tarlz: -# test.txt.tar.lz: 1 member (test.txt). -# t155.tar[.lz]: directory + 3 links + file + EOA, all with 155 char names -# t155_fv?.tar[.lz]: like t155.tar but with 3 kinds of format violations -# t155_fv1.tar[.lz]: extra extended header before EOA blocks -# t155_fv2.tar[.lz]: first extended header followed by global header -# t155_fv3.tar[.lz]: consecutive extended headers in last member -# t155_fv[456].tar.lz: like t155_fv[123].tar.lz but violation alone in member -# 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 -# ts_in_link.tar.lz: 4 symbolic links (link[1-4]) to / /dir/ dir/ dir(107/) +# Description of test files for tarlz (use level -6 or -9): +# test.txt.tar.lz: 1 member (test.txt) + EOA. +# test_bad1.txt.tar: truncated at offset 17082 (of 37888) # test_bad1.txt.tar.lz: truncated at offset 6000 (of 7459) # test_bad2.txt.tar.lz: byte at offset 6000 changed from 0x53 to 0x43 -# test3.tar[.lz]: 3 members (foo bar baz) + 2 zeroed 512-byte blocks -# test3_dir.tar[.lz] like test3.tar but members /dir/foo /dir/bar /dir/baz +# t155.tar.lz: directory + 3 links + file + EOA, all with 155 char names +# symlinks: long linkpath, long both, long path. +# t155_fv?.tar.lz: like t155.tar but with 4 kinds of format violations +# t155_fv1.tar.lz: extra extended header before EOA blocks +# t155_fv2.tar.lz: first extended header followed by global header +# t155_fv3.tar.lz: consecutive extended headers in last member +# t155_fv[456].tar.lz: like t155_fv[123].tar.lz but violation alone in member +# t155_fv7.tar.lz: 2 linkpath extended records in first symlink +# ts_in_link.tar.lz: 4 symbolic links (link[1-4]) to / /dir/ dir/ dir(107/) +# test3.tar.lz: 3 members (foo bar baz) + 2 zeroed 512-byte blocks +# test3_bad1.tar: byte at offset 259 changed from 't' to '0' (tar magic) +# test3_bad2.tar: byte at offset 1283 changed from 't' to '0' (tar magic) +# test3_bad3.tar: byte at offset 2559 changed from 0x00 to 0x20 (baz header padding) +# test3_bad4.tar: byte at offset 1283 changed from 't' to '0' (tar magic) +# byte at offset 2307 changed from 't' to '0' (tar magic) +# test3_bad5.tar: 510 zeros + "LZ" prepended to test3.tar (bogus lz header) +# test3_bad1.tar.lz: byte at offset 2 changed from 'I' to 'i' (lzip magic) +# test3_bad2.tar.lz: byte at offset 49 changed from 0x29 to 0x69 (foo header) +# test3_bad3.tar.lz: byte at offset 166 changed from 0x9A to 0x7A (bar data padding) +# test3_bad4.tar.lz: combined damage of test3_bad2.tar.lz and test3_bad3.tar.lz +# test3_bad5.tar.lz: [68-131] --> zeroed (foo data padding + bar) +# test3_bad6.tar.lz: 510 zeros prepended to test3.tar.lz (header in two blocks) +# test3_crc.tar.lz: test3.tar with extended headers and bad crc32 records +# test3_uk.tar.lz: test3.tar with extended headers and 15 unknownNN keywords +# test3_dir.tar.lz: like test3.tar but members /dir/foo /dir/bar /dir/baz # test3_dot.tar.lz: 3 times 3 members ./foo ././bar ./././baz # the 3 central members with filename in extended header -# test3_bad1.tar: byte at offset 259 changed from 't' to '0' (magic) -# test3_bad2.tar: byte at offset 1283 changed from 't' to '0' (magic) -# test3_bad3.tar: byte at offset 2559 changed from 0x00 to 0x20 (padding) -# test3_bad4.tar: byte at offset 1283 changed from 't' to '0' (magic) -# byte at offset 2307 changed from 't' to '0' (magic) -# test3_bad5.tar: 510 zeros + "LZ" prepended to test3.tar (bogus lz header) -# test3_bad1.tar.lz: byte at offset 2 changed from 'I' to 'i' (magic) -# test3_bad2.tar.lz: byte at offset 49 changed from 0x49 to 0x69 (mid stream) -# test3_bad3.tar.lz: byte at offset 176 changed from 0x7D to 0x6D (mid stream) -# test3_bad4.tar.lz: combined damage of test3_bad2.tar.lz and test3_bad3.tar.lz -# test3_bad5.tar.lz: [71-134] --> zeroed (first trailer + second header) -# test3_bad6.tar.lz: 510 zeros prepended to test3.tar.lz (header in two blocks) -# test3_eoa?.tar: like test3_eoa?.tar.lz but uncompressed -# test3_eoa1.tar.lz: test3.tar.lz without EOA blocks -# test3_eoa2.tar.lz: test3.tar.lz with only one EOA block -# test3_eoa3.tar.lz: test3.tar.lz with one zeroed block between foo and bar -# test3_eoa4.tar.lz: test3.tar.lz ended by extended header without EOA blocks -# test3_eoa5.tar.lz: test3.tar.lz split extended bar member, without EOA blocks -# test3_gh?.tar: test3.tar with global header at each position -# test3_gh?.tar.lz: test3.tar.lz with global before bar split in 4 ways -# test3_gh5.tar.lz: test3.tar.lz with global in lzip member before foo -# test3_gh6.tar.lz: test3.tar.lz with global before foo in same member -# test3_nn.tar[.lz]: test3.tar[.lz] with zeroed name (no name) in bar member -# test3_sm?.tar.lz: test3.tar.lz with extended bar member split in 4 ways -# 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 -# ug32chars.tar.lz: 1 member (foo) with 32-character owner and group names -# ug32767.tar.lz: 1 member (foo) with numerical-only owner and group - -# Note that multi-threaded --list succeeds with test_bad2.txt.tar.lz and -# test3_bad3.tar.lz because their headers are intact. +# test3_eoa1.tar.lz: test3.tar without EOA blocks +# test3_eoa2.tar.lz: test3.tar with only one EOA block +# test3_eoa3.tar.lz: test3.tar with one zeroed block between foo and bar +# test3_eoa4.tar.lz: test3.tar ended by extended header without EOA blocks +# test3_eoa5.tar.lz: test3.tar split extended bar member, without EOA blocks +# test3_gh[1-4].tar.lz: test3.tar with global header in own member at each pos +# test3_gh5.tar.lz: test3.tar with global before foo in same member +# test3_gh[678].tar.lz: test3.tar with global before (bar split in 3 ways) +# test3_nn.tar.lz: test3.tar with zeroed name (no name) in bar member +# test3_sm?.tar.lz: test3.tar with extended bar member split in 4 ways +# 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 +# tlz_in_tar1.tar: 1 member (test3.tar.lz) first tar magic damaged +# tlz_in_tar2.tar: 2 members (foo test3.tar.lz) first tar magic damaged +# ug32chars.tar.lz: 1 member (foo) with 32-character owner and group names +# ug32767.tar.lz: 1 member (foo) with numerical-only owner and group "${TARLZ}" --check-lib # just print warning [ $? != 2 ] || test_failed $LINENO # unless bad lzlib.h @@ -122,9 +116,9 @@ printf "testing tarlz-%s..." "$2" "${TARLZ}" -q -tf "${in}" [ $? = 2 ] || test_failed $LINENO -"${TARLZ}" -q -tf "${in_lz}" +"${TARLZ}" -q -tf in.lz [ $? = 2 ] || test_failed $LINENO -"${TARLZ}" -q -tf "${in_tar_lz}" -f "${in_tar_lz}" +"${TARLZ}" -q -tf in.tar.lz -f in.tar.lz [ $? = 1 ] || test_failed $LINENO "${TARLZ}" -q -tf nx_file [ $? = 1 ] || test_failed $LINENO @@ -149,9 +143,12 @@ printf "testing tarlz-%s..." "$2" [ ! -e out.tar ] || test_failed $LINENO "${TARLZ}" -q -c "${in}" nx_file > /dev/null [ $? = 1 ] || test_failed $LINENO -"${TARLZ}" -q -c -C nx_dir "${in}" +touch empty.tar || framework_failure +"${TARLZ}" -q -c -C nx_dir -f out.tar "${in}" [ $? = 1 ] || test_failed $LINENO -"${TARLZ}" -q -x -C nx_dir "${test3_lz}" +cmp empty.tar out.tar || test_failed $LINENO +rm -f out.tar || framework_failure +"${TARLZ}" -q -x -C nx_dir -f t3.tar.lz [ $? = 1 ] || test_failed $LINENO touch empty.tar.lz empty.tlz || framework_failure # list an empty lz file "${TARLZ}" -q -tf empty.tar.lz @@ -159,8 +156,7 @@ touch empty.tar.lz empty.tlz || framework_failure # list an empty lz file "${TARLZ}" -q -tf empty.tlz [ $? = 2 ] || test_failed $LINENO rm -f empty.tar.lz empty.tlz || framework_failure -touch empty.tar || framework_failure # compress an empty archive -"${TARLZ}" -q -z empty.tar +"${TARLZ}" -q -z empty.tar # compress an empty archive [ $? = 2 ] || test_failed $LINENO [ ! -e empty.tar.lz ] || test_failed $LINENO rm -f empty.tar empty.tar.lz || framework_failure @@ -184,15 +180,13 @@ done [ $? = 1 ] || test_failed $LINENO "${TARLZ}" -q -z . [ $? = 1 ] || test_failed $LINENO -"${TARLZ}" -z -o - --uncompressed "${test3}" > /dev/null 2>&1 +"${TARLZ}" -z -o - --uncompressed t3.tar > /dev/null 2>&1 [ $? = 1 ] || test_failed $LINENO -"${TARLZ}" -q -tf "${in_tar_lz}" "" # empty non-option argument +"${TARLZ}" -q -tf in.tar.lz "" # empty non-option argument [ $? = 1 ] || test_failed $LINENO "${TARLZ}" --help > /dev/null || test_failed $LINENO "${TARLZ}" -V > /dev/null || test_failed $LINENO -"${TARLZ}" --bad_option -tf "${test3_lz}" 2> /dev/null -[ $? = 1 ] || test_failed $LINENO -"${TARLZ}" -tf 2> /dev/null +"${TARLZ}" --bad_option -tf t3.tar.lz 2> /dev/null [ $? = 1 ] || test_failed $LINENO bad_dates='@-9223372036854775809 @9223372036854775808 -2147481749-01-01T00:00:00 2147483648-01-01T00:00:00 @@ -201,34 +195,34 @@ for i in ${bad_dates} ; do "${TARLZ}" -c --mtime="$i" "${in}" > /dev/null 2>&1 [ $? = 1 ] || test_failed $LINENO "$i" done -"${TARLZ}" --owner=invalid_owner_name -tf "${test3_lz}" 2> /dev/null +"${TARLZ}" --owner=invalid_owner_name -tf t3.tar.lz 2> /dev/null [ $? = 1 ] || test_failed $LINENO -"${TARLZ}" --group=invalid_group_name -tf "${test3_lz}" 2> /dev/null +"${TARLZ}" --group=invalid_group_name -tf t3.tar.lz 2> /dev/null [ $? = 1 ] || test_failed $LINENO printf "\ntesting --list and --extract..." # test --list and --extract -"${TARLZ}" -tf "${eoa_lz}" --missing-crc || test_failed $LINENO -"${TARLZ}" -xf "${eoa_lz}" --missing-crc || test_failed $LINENO -"${TARLZ}" -C nx_dir -tf "${in_tar}" > /dev/null || test_failed $LINENO -"${TARLZ}" -xf "${in_tar}" --missing-crc || test_failed $LINENO +"${TARLZ}" -tf eoa.lz --missing-crc || test_failed $LINENO +"${TARLZ}" -xf eoa.lz --missing-crc || test_failed $LINENO +"${TARLZ}" -C nx_dir -tf in.tar > /dev/null || test_failed $LINENO +"${TARLZ}" -xf in.tar --missing-crc || test_failed $LINENO cmp "${in}" test.txt || test_failed $LINENO rm -f test.txt || framework_failure -"${TARLZ}" -tf "${in_tar_lz}" --missing-crc > /dev/null || test_failed $LINENO +"${TARLZ}" -tf in.tar.lz --missing-crc > /dev/null || test_failed $LINENO for i in 0 2 6 ; do - "${TARLZ}" -n$i -xf "${in_tar_lz}" --missing-crc || test_failed $LINENO $i + "${TARLZ}" -n$i -xf in.tar.lz --missing-crc || test_failed $LINENO $i cmp "${in}" test.txt || test_failed $LINENO $i rm -f test.txt || framework_failure done # test3 reference files for -t and -tv (list3, vlist3) -"${TARLZ}" -tf "${test3}" > list3 || test_failed $LINENO -"${TARLZ}" -tvf "${test3}" > vlist3 || test_failed $LINENO +"${TARLZ}" -tf t3.tar > list3 || test_failed $LINENO +"${TARLZ}" -tvf t3.tar > vlist3 || test_failed $LINENO for i in 0 2 6 ; do - "${TARLZ}" -n$i -tf "${test3_lz}" > out || test_failed $LINENO $i + "${TARLZ}" -n$i -tf t3.tar.lz > out || test_failed $LINENO $i diff -u list3 out || test_failed $LINENO $i - "${TARLZ}" -n$i -tvf "${test3_lz}" > out || test_failed $LINENO $i + "${TARLZ}" -n$i -tvf t3.tar.lz > out || test_failed $LINENO $i diff -u vlist3 out || test_failed $LINENO $i done rm -f out || framework_failure @@ -240,31 +234,31 @@ cp "${testdir}"/rbaz cbaz || framework_failure # test --list and --extract test3 rm -f foo bar baz || framework_failure -"${TARLZ}" -xf "${test3}" --missing-crc || test_failed $LINENO +"${TARLZ}" -xf t3.tar --missing-crc || test_failed $LINENO cmp cfoo foo || test_failed $LINENO cmp cbar bar || test_failed $LINENO cmp cbaz baz || test_failed $LINENO # time and mode comparison always fails on OS/2 -if "${TARLZ}" -df "${test3}" --ignore-ids ; then d_works=yes -else printf "warning: some '--diff' tests will be skipped.\n" +if "${TARLZ}" -q -df t3.tar --ignore-ids ; then d_works=yes +else printf "\nwarning: some '--diff' tests will be skipped." fi rm -f foo bar baz || framework_failure for i in 0 2 6 ; do - "${TARLZ}" -n$i -xf "${test3_lz}" --missing-crc || test_failed $LINENO $i + "${TARLZ}" -n$i -xf t3.tar.lz --missing-crc || test_failed $LINENO $i cmp cfoo foo || test_failed $LINENO $i cmp cbar bar || test_failed $LINENO $i cmp cbaz baz || test_failed $LINENO $i rm -f foo bar baz || framework_failure - "${TARLZ}" -n$i -tvf "${test3_lz}" ./foo ./bar ./baz > out 2> /dev/null || + "${TARLZ}" -n$i -tvf t3.tar.lz ./foo ./bar ./baz > out 2> /dev/null || test_failed $LINENO $i diff -u vlist3 out || test_failed $LINENO $i rm -f out || framework_failure - "${TARLZ}" -q -n$i -xf "${test3_lz}" ./foo ./bar ./baz || test_failed $LINENO $i + "${TARLZ}" -q -n$i -xf t3.tar.lz ./foo ./bar ./baz || test_failed $LINENO $i cmp cfoo foo || test_failed $LINENO $i cmp cbar bar || test_failed $LINENO $i cmp cbaz baz || test_failed $LINENO $i rm -f foo bar baz || framework_failure - "${TARLZ}" -n$i -xf "${test3_lz}" foo/ bar// baz/// || test_failed $LINENO $i + "${TARLZ}" -n$i -xf t3.tar.lz foo/ bar// baz/// || test_failed $LINENO $i cmp cfoo foo || test_failed $LINENO $i cmp cbar bar || test_failed $LINENO $i cmp cbaz baz || test_failed $LINENO $i @@ -280,10 +274,23 @@ for i in 0 2 6 ; do cmp cbar bar || test_failed $LINENO $i cmp cbaz baz || test_failed $LINENO $i rm -f foo bar baz || framework_failure + "${TARLZ}" -q -n$i -tf "${testdir}"/test3_uk.tar.lz || test_failed $LINENO $i + "${TARLZ}" -q -n$i -xf "${testdir}"/test3_uk.tar.lz || test_failed $LINENO $i + cmp cfoo foo || test_failed $LINENO $i + cmp cbar bar || test_failed $LINENO $i + cmp cbaz baz || test_failed $LINENO $i + rm -f foo bar baz || framework_failure done +lzip -cd "${testdir}"/test3_uk.tar.lz | "${TARLZ}" -q -t || test_failed $LINENO +lzip -cd "${testdir}"/test3_uk.tar.lz | "${TARLZ}" -q -x foo bar baz || + test_failed $LINENO +cmp cfoo foo || test_failed $LINENO +cmp cbar bar || test_failed $LINENO +cmp cbaz baz || test_failed $LINENO +rm -f foo bar baz || framework_failure # test -C in --diff and --extract -for i in "${test3}" "${test3_lz}" ; do +for i in t3.tar t3.tar.lz ; do mkdir dir1 dir2 dir3 || framework_failure "${TARLZ}" -q -xf "$i" -C dir1 foo -C ../dir2 bar -C ../dir3 baz || test_failed $LINENO "$i" @@ -298,7 +305,7 @@ for i in "${test3}" "${test3_lz}" ; do fi rm -rf dir1 dir2 dir3 || framework_failure done -for i in "${test3dir}" "${test3dir_lz}" ; do +for i in t3_dir.tar t3_dir.tar.lz ; do mkdir dir1 dir2 dir3 || framework_failure "${TARLZ}" -q -xf "$i" -C dir2 dir/bar -C ../dir1 dir/foo \ -C ../dir3 dir/baz || test_failed $LINENO "$i" @@ -308,13 +315,13 @@ for i in "${test3dir}" "${test3dir_lz}" ; do if [ "${d_works}" = yes ] ; then "${TARLZ}" -q -df "$i" --ignore-ids -C dir1 dir/foo -C ../dir2 dir/bar \ -C ../dir3 dir/baz || test_failed $LINENO "$i" - "${TARLZ}" -q -df "${test3}" -C dir1/dir foo -C ../../dir2/dir bar \ + "${TARLZ}" -q -df t3.tar -C dir1/dir foo -C ../../dir2/dir bar \ --ignore-ids -C ../../dir3/dir baz || test_failed $LINENO "$i" fi rm -rf dir1 dir2 dir3 || framework_failure done -for i in "${test3dir}" "${test3dir_lz}" ; do +for i in t3_dir.tar t3_dir.tar.lz ; do "${TARLZ}" -q -tf "$i" --missing-crc || test_failed $LINENO "$i" "${TARLZ}" -q -xf "$i" --missing-crc || test_failed $LINENO "$i" cmp cfoo dir/foo || test_failed $LINENO "$i" @@ -336,51 +343,55 @@ for i in "${test3dir}" "${test3dir_lz}" ; do done # test --extract --exclude -"${TARLZ}" -xf "${test3}" --exclude='f*o' --exclude=baz || test_failed $LINENO +"${TARLZ}" -xf t3.tar --exclude='f*o' --exclude=baz || test_failed $LINENO [ ! -e foo ] || test_failed $LINENO cmp cbar bar || test_failed $LINENO [ ! -e baz ] || test_failed $LINENO rm -f foo bar baz || framework_failure for i in 0 2 6 ; do - "${TARLZ}" -n$i -xf "${test3_lz}" --exclude=bar || test_failed $LINENO $i + "${TARLZ}" -n$i -xf t3.tar.lz --exclude=bar || test_failed $LINENO $i cmp cfoo foo || test_failed $LINENO $i [ ! -e bar ] || test_failed $LINENO $i cmp cbaz baz || test_failed $LINENO $i rm -f foo bar baz || framework_failure - "${TARLZ}" -q -n$i -xf "${test3dir_lz}" --exclude='?ar' || test_failed $LINENO $i + "${TARLZ}" -q -n$i -xf t3_dir.tar.lz --exclude='?ar' || test_failed $LINENO $i cmp cfoo dir/foo || test_failed $LINENO $i [ ! -e dir/bar ] || test_failed $LINENO $i cmp cbaz dir/baz || test_failed $LINENO $i rm -rf dir || framework_failure - "${TARLZ}" -q -n$i -xf "${test3dir_lz}" --exclude=dir/bar || test_failed $LINENO $i + "${TARLZ}" -q -n$i -xf t3_dir.tar.lz --exclude=dir/bar || test_failed $LINENO $i cmp cfoo dir/foo || test_failed $LINENO $i [ ! -e dir/bar ] || test_failed $LINENO $i cmp cbaz dir/baz || test_failed $LINENO $i rm -rf dir || framework_failure - "${TARLZ}" -q -n$i -xf "${test3dir_lz}" --exclude=dir || test_failed $LINENO $i + "${TARLZ}" -q -n$i -xf t3_dir.tar.lz --exclude=dir || test_failed $LINENO $i [ ! -e dir ] || test_failed $LINENO $i rm -rf dir || framework_failure - "${TARLZ}" -q -n$i -xf "${test3dir_lz}" --exclude='dir/*' || test_failed $LINENO $i + "${TARLZ}" -q -n$i -xf t3_dir.tar.lz --exclude='dir/*' || test_failed $LINENO $i [ ! -e dir ] || test_failed $LINENO $i rm -rf dir || framework_failure - "${TARLZ}" -q -n$i -xf "${test3dir_lz}" --exclude='[bf][ao][orz]' || + "${TARLZ}" -q -n$i -xf t3_dir.tar.lz --exclude='[bf][ao][orz]' || test_failed $LINENO $i [ ! -e dir ] || test_failed $LINENO $i rm -rf dir || framework_failure - "${TARLZ}" -q -n$i -xf "${test3dir_lz}" --exclude='*o' dir/foo || + "${TARLZ}" -q -n$i -xf t3_dir.tar.lz --exclude='*o' dir/foo || test_failed $LINENO $i [ ! -e dir ] || test_failed $LINENO $i rm -rf dir || framework_failure done # test --list and --extract EOA -"${TARLZ}" -tvf "${testdir}"/test3_eoa1.tar > out 2> /dev/null +lzip -cd "${testdir}"/test3_eoa1.tar.lz > test3_eoa1.tar || framework_failure +lzip -cd "${testdir}"/test3_eoa2.tar.lz > test3_eoa2.tar || framework_failure +lzip -cd "${testdir}"/test3_eoa3.tar.lz > test3_eoa3.tar || framework_failure +lzip -cd "${testdir}"/test3_eoa4.tar.lz > test3_eoa4.tar || framework_failure +"${TARLZ}" -tvf test3_eoa1.tar > out 2> /dev/null [ $? = 2 ] || test_failed $LINENO diff -u vlist3 out || test_failed $LINENO -"${TARLZ}" -tvf "${testdir}"/test3_eoa2.tar > out || test_failed $LINENO +"${TARLZ}" -tvf test3_eoa2.tar > out || test_failed $LINENO diff -u vlist3 out || test_failed $LINENO -"${TARLZ}" -q -tf "${testdir}"/test3_eoa3.tar || test_failed $LINENO -"${TARLZ}" -tvf "${testdir}"/test3_eoa4.tar > out 2> /dev/null +"${TARLZ}" -q -tf test3_eoa3.tar || test_failed $LINENO +"${TARLZ}" -tvf test3_eoa4.tar > out 2> /dev/null [ $? = 2 ] || test_failed $LINENO diff -u vlist3 out || test_failed $LINENO for i in 0 2 6 ; do @@ -401,32 +412,41 @@ for i in 0 2 6 ; do done rm -f out || framework_failure # -"${TARLZ}" -q -xf "${testdir}"/test3_eoa1.tar +"${TARLZ}" -q -xf test3_eoa1.tar [ $? = 2 ] || test_failed $LINENO +if [ "${d_works}" = yes ] ; then + "${TARLZ}" -q -df test3_eoa1.tar --ignore-ids + [ $? = 2 ] || test_failed $LINENO +fi cmp cfoo foo || test_failed $LINENO cmp cbar bar || test_failed $LINENO cmp cbaz baz || test_failed $LINENO rm -f foo bar baz || framework_failure -"${TARLZ}" -xf "${testdir}"/test3_eoa2.tar || test_failed $LINENO +"${TARLZ}" -xf test3_eoa2.tar || test_failed $LINENO cmp cfoo foo || test_failed $LINENO cmp cbar bar || test_failed $LINENO cmp cbaz baz || test_failed $LINENO rm -f foo bar baz || framework_failure -"${TARLZ}" -xf "${testdir}"/test3_eoa3.tar || test_failed $LINENO +"${TARLZ}" -xf test3_eoa3.tar || test_failed $LINENO cmp cfoo foo || test_failed $LINENO [ ! -e bar ] || test_failed $LINENO [ ! -e baz ] || test_failed $LINENO rm -f foo bar baz || framework_failure -"${TARLZ}" -q -xf "${testdir}"/test3_eoa4.tar +"${TARLZ}" -q -xf test3_eoa4.tar [ $? = 2 ] || test_failed $LINENO cmp cfoo foo || test_failed $LINENO cmp cbar bar || test_failed $LINENO cmp cbaz baz || test_failed $LINENO -rm -f foo bar baz || framework_failure +rm -f foo bar baz test3_eoa1.tar || framework_failure +rm -f test3_eoa2.tar test3_eoa3.tar test3_eoa4.tar || framework_failure # for i in 0 2 6 ; do "${TARLZ}" -q -n$i -xf "${testdir}"/test3_eoa1.tar.lz [ $? = 2 ] || test_failed $LINENO $i + if [ "${d_works}" = yes ] ; then + "${TARLZ}" -q -n$i -df "${testdir}"/test3_eoa1.tar.lz --ignore-ids + [ $? = 2 ] || test_failed $LINENO $i + fi cmp cfoo foo || test_failed $LINENO $i cmp cbar bar || test_failed $LINENO $i cmp cbaz baz || test_failed $LINENO $i @@ -472,27 +492,28 @@ for i in "${testdir}"/tar_in_tlz1.tar.lz "${testdir}"/tar_in_tlz2.tar.lz ; do rm -f out0 out2 out6 outv0 outv2 outv6 || framework_failure for j in 0 2 6 ; do "${TARLZ}" -xf "$i" -n$j || test_failed $LINENO "$i $j" - cmp "${in_tar}" test.txt.tar || test_failed $LINENO "$i $j" - cmp "${test3}" test3.tar || test_failed $LINENO "$i $j" + cmp in.tar test.txt.tar || test_failed $LINENO "$i $j" + cmp t3.tar test3.tar || test_failed $LINENO "$i $j" rm -f test.txt.tar test3.tar || framework_failure done done # test --list and --extract with global headers uncompressed for i in gh1 gh2 gh3 gh4 ; do - "${TARLZ}" -tf "${testdir}"/test3_${i}.tar > out || test_failed $LINENO $i + lzip -cd "${testdir}"/test3_${i}.tar.lz > test3_${i}.tar || framework_failure + "${TARLZ}" -tf test3_${i}.tar > out || test_failed $LINENO $i diff -u list3 out || test_failed $LINENO $i - "${TARLZ}" -tvf "${testdir}"/test3_${i}.tar > out || test_failed $LINENO $i + "${TARLZ}" -tvf test3_${i}.tar > out || test_failed $LINENO $i diff -u vlist3 out || test_failed $LINENO $i - "${TARLZ}" -xf "${testdir}"/test3_${i}.tar || test_failed $LINENO $i + "${TARLZ}" -xf test3_${i}.tar || test_failed $LINENO $i cmp cfoo foo || test_failed $LINENO $i cmp cbar bar || test_failed $LINENO $i cmp cbaz baz || test_failed $LINENO $i - rm -f foo bar baz out || framework_failure + rm -f foo bar baz out test3_${i}.tar || framework_failure done # test --list and --extract with empty lzip member -cat "${em_lz}" "${test3_lz}" > test3_em.tar.lz || framework_failure +cat "${em_lz}" t3.tar.lz > test3_em.tar.lz || framework_failure "${TARLZ}" -q -tf test3_em.tar.lz > out [ $? = 2 ] || test_failed $LINENO "${TARLZ}" -tvf - < test3_em.tar.lz > out || test_failed $LINENO @@ -510,7 +531,7 @@ rm -f foo bar baz || framework_failure # test --list and --extract with global headers and extended tar members # split among lzip members -for i in gh1 gh2 gh3 gh4 gh5 gh6 sm1 sm2 sm3 sm4 ; do +for i in gh1 gh2 gh3 gh4 gh5 gh6 gh7 gh8 sm1 sm2 sm3 sm4 ; do for j in 0 2 6 ; do "${TARLZ}" -n$j -tf "${testdir}"/test3_${i}.tar.lz > out || test_failed $LINENO "$i $j" @@ -531,18 +552,23 @@ for i in gh1 gh2 gh3 gh4 gh5 gh6 sm1 sm2 sm3 sm4 ; do done rm -f list3 vlist3 || framework_failure +# multi-threaded --list succeeds with test_bad2.txt.tar.lz and +# test3_bad3.tar.lz because their headers are intact. +"${TARLZ}" -tf "${test_bad2}.tar.lz" > /dev/null || test_failed $LINENO +"${TARLZ}" -tf "${t3_bad3_lz}" > /dev/null || test_failed $LINENO + printf "\ntesting --concatenate..." # test --concatenate compressed cp "${in}" out.tar.lz || framework_failure # invalid tar.lz -"${TARLZ}" -Aqf out.tar.lz "${test3_lz}" +"${TARLZ}" -Aqf out.tar.lz t3.tar.lz [ $? = 2 ] || test_failed $LINENO -cp "${in_tar_lz}" out.tar.lz || framework_failure -"${TARLZ}" -q --un -Af out.tar.lz "${test3_lz}" # contradictory ext +cp in.tar.lz out.tar.lz || framework_failure +"${TARLZ}" -q --un -Af out.tar.lz t3.tar.lz # contradictory ext [ $? = 1 ] || test_failed $LINENO -cmp "${in_tar_lz}" out.tar.lz || test_failed $LINENO -cp "${in_tar_lz}" out.tar.lz || framework_failure -"${TARLZ}" -Af out.tar.lz "${test3_lz}" || test_failed $LINENO +cmp in.tar.lz out.tar.lz || test_failed $LINENO +cp in.tar.lz out.tar.lz || framework_failure +"${TARLZ}" -Af out.tar.lz t3.tar.lz || test_failed $LINENO "${TARLZ}" -xf out.tar.lz || test_failed $LINENO cmp "${in}" test.txt || test_failed $LINENO cmp cfoo foo || test_failed $LINENO @@ -550,47 +576,47 @@ cmp cbar bar || test_failed $LINENO cmp cbaz baz || test_failed $LINENO rm -f test.txt foo bar baz || framework_failure touch aout.tar.lz || framework_failure # concatenate to empty file -"${TARLZ}" -Aqf aout.tar.lz "${in_tar}" +"${TARLZ}" -Aqf aout.tar.lz in.tar [ $? = 2 ] || test_failed $LINENO -"${TARLZ}" -Af aout.tar.lz "${in_tar_lz}" "${test3_lz}" || test_failed $LINENO +"${TARLZ}" -Af aout.tar.lz in.tar.lz t3.tar.lz || test_failed $LINENO cmp out.tar.lz aout.tar.lz || test_failed $LINENO "${TARLZ}" -Af aout.tar.lz || test_failed $LINENO # concatenate nothing cmp out.tar.lz aout.tar.lz || test_failed $LINENO "${TARLZ}" -Aqf aout.tar.lz aout.tar.lz || test_failed $LINENO cmp out.tar.lz aout.tar.lz || test_failed $LINENO -"${TARLZ}" -Aq "${in_tar_lz}" "${test3}" > aout.tar.lz # to stdout +"${TARLZ}" -Aq in.tar.lz t3.tar > aout.tar.lz # to stdout [ $? = 2 ] || test_failed $LINENO -cmp "${in_tar_lz}" aout.tar.lz || test_failed $LINENO -"${TARLZ}" -A "${in_tar_lz}" "${test3_lz}" > aout.tar.lz || test_failed $LINENO +cmp in.tar.lz aout.tar.lz || test_failed $LINENO +"${TARLZ}" -A in.tar.lz t3.tar.lz > aout.tar.lz || test_failed $LINENO cmp out.tar.lz aout.tar.lz || test_failed $LINENO -cp "${eoa_lz}" aout.tar.lz || framework_failure -"${TARLZ}" -Aqf aout.tar.lz "${in_tar}" # concatenate to empty archive +cp eoa.lz aout.tar.lz || framework_failure +"${TARLZ}" -Aqf aout.tar.lz in.tar # concatenate to empty archive [ $? = 2 ] || test_failed $LINENO -"${TARLZ}" -Af aout.tar.lz "${in_tar_lz}" "${test3_lz}" || test_failed $LINENO +"${TARLZ}" -Af aout.tar.lz in.tar.lz t3.tar.lz || test_failed $LINENO cmp out.tar.lz aout.tar.lz || test_failed $LINENO -cp "${in_tar_lz}" aout.tar.lz || framework_failure -"${TARLZ}" -Aqf aout.tar.lz "${test3_lz}" "${test3}" +cp in.tar.lz aout.tar.lz || framework_failure +"${TARLZ}" -Aqf aout.tar.lz t3.tar.lz t3.tar [ $? = 2 ] || test_failed $LINENO cmp out.tar.lz aout.tar.lz || test_failed $LINENO rm -f aout.tar.lz || framework_failure touch aout.tar.lz || framework_failure # --exclude -"${TARLZ}" -Af aout.tar.lz "${in_tar_lz}" "${test3_lz}" --exclude 'test3*' || +"${TARLZ}" -Af aout.tar.lz in.tar.lz t3.tar.lz --exclude 't3*' || test_failed $LINENO -"${TARLZ}" -Af aout.tar.lz "${in_tar_lz}" "${test3_lz}" --exclude '*txt*' || +"${TARLZ}" -Af aout.tar.lz in.tar.lz t3.tar.lz --exclude 'in*' || test_failed $LINENO cmp out.tar.lz aout.tar.lz || test_failed $LINENO rm -f out.tar.lz aout.tar.lz || framework_failure # test --concatenate uncompressed -cp "${in}" out.tar || framework_failure # invalid tar -"${TARLZ}" -Aqf out.tar "${test3}" +cp "${in}" out.tar || framework_failure # invalid tar +"${TARLZ}" -Aqf out.tar t3.tar [ $? = 2 ] || test_failed $LINENO -cp "${in_tar}" out.tar || framework_failure -"${TARLZ}" -q -0 -Af out.tar "${test3}" # contradictory ext +cp in.tar out.tar || framework_failure +"${TARLZ}" -q -0 -Af out.tar t3.tar # contradictory ext [ $? = 1 ] || test_failed $LINENO -cmp "${in_tar}" out.tar || test_failed $LINENO -cp "${in_tar}" out.tar || framework_failure -"${TARLZ}" -Af out.tar "${test3}" || test_failed $LINENO +cmp in.tar out.tar || test_failed $LINENO +cp in.tar out.tar || framework_failure +"${TARLZ}" -Af out.tar t3.tar || test_failed $LINENO "${TARLZ}" -xf out.tar || test_failed $LINENO cmp "${in}" test.txt || test_failed $LINENO cmp cfoo foo || test_failed $LINENO @@ -598,34 +624,32 @@ cmp cbar bar || test_failed $LINENO cmp cbaz baz || test_failed $LINENO rm -f test.txt foo bar baz || framework_failure touch aout.tar || framework_failure # concatenate to empty file -"${TARLZ}" -Aqf aout.tar "${in_tar_lz}" +"${TARLZ}" -Aqf aout.tar in.tar.lz [ $? = 2 ] || test_failed $LINENO -"${TARLZ}" -Af aout.tar "${in_tar}" "${test3}" || test_failed $LINENO +"${TARLZ}" -Af aout.tar in.tar t3.tar || test_failed $LINENO cmp out.tar aout.tar || test_failed $LINENO "${TARLZ}" -Af aout.tar || test_failed $LINENO # concatenate nothing cmp out.tar aout.tar || test_failed $LINENO "${TARLZ}" -Aqf aout.tar aout.tar || test_failed $LINENO cmp out.tar aout.tar || test_failed $LINENO -"${TARLZ}" -Aq "${in_tar}" "${test3_lz}" > aout.tar # to stdout +"${TARLZ}" -Aq in.tar t3.tar.lz > aout.tar # to stdout [ $? = 2 ] || test_failed $LINENO -cmp "${in_tar}" aout.tar || test_failed $LINENO -"${TARLZ}" -A "${in_tar}" "${test3}" > aout.tar || test_failed $LINENO +cmp in.tar aout.tar || test_failed $LINENO +"${TARLZ}" -A in.tar t3.tar > aout.tar || test_failed $LINENO cmp out.tar aout.tar || test_failed $LINENO -cp "${eoa}" aout.tar || framework_failure # concatenate to empty archive -"${TARLZ}" -Aqf aout.tar "${in_tar_lz}" +cp eoa aout.tar || framework_failure # concatenate to empty archive +"${TARLZ}" -Aqf aout.tar in.tar.lz [ $? = 2 ] || test_failed $LINENO -"${TARLZ}" -Af aout.tar "${in_tar}" "${test3}" || test_failed $LINENO +"${TARLZ}" -Af aout.tar in.tar t3.tar || test_failed $LINENO cmp out.tar aout.tar || test_failed $LINENO -cp "${in_tar}" aout.tar || framework_failure -"${TARLZ}" -Aqf aout.tar "${test3}" "${test3_lz}" +cp in.tar aout.tar || framework_failure +"${TARLZ}" -Aqf aout.tar t3.tar t3.tar.lz [ $? = 2 ] || test_failed $LINENO cmp out.tar aout.tar || test_failed $LINENO rm -f aout.tar || framework_failure touch aout.tar || framework_failure # --exclude -"${TARLZ}" -Af aout.tar "${test3}" "${in_tar}" --exclude 'test3*' || - test_failed $LINENO -"${TARLZ}" -Af aout.tar "${test3}" "${in_tar}" --exclude '*txt*' || - test_failed $LINENO +"${TARLZ}" -Af aout.tar t3.tar in.tar --exclude 't3*' || test_failed $LINENO +"${TARLZ}" -Af aout.tar t3.tar in.tar --exclude 'in*' || test_failed $LINENO cmp out.tar aout.tar || test_failed $LINENO rm -f out.tar aout.tar || framework_failure @@ -748,24 +772,23 @@ cmp cfoo foo || test_failed $LINENO rm -f out.tar foo bar baz || framework_failure # test --create --mtime +safe_dates='1969-12-31T23:59:58 1969-12-31T23:59:59 + 1970-01-01T00:00:00 1970-01-01T00:00:01 @0 + 2038-01-18T03:14:07 2038-01-19T03:14:07 2038-01-19T03:14:08 + 2106-02-07T06:28:15 2106-02-07T06:28:16 + 2242-03-16T12:56:31 2242-03-16T12:56:32 @8589934591 @8589934592' dates='@-9223372036854775808 @-9223372036854775807 -2147481748-12-31T23:59:59 -1970-01-01T00:00:00 0000-01-01T00:00:00 0000-01-01T00:00:01 0000-01-02T00:00:00 1697-10-17T11:03:27 1697-10-17T11:03:28 1697-10-17T11:03:29 1833-11-24T17:31:43 1833-11-24T17:31:44 1833-11-24T17:31:45 1901-12-13T20:45:51 1901-12-13T20:45:52 1901-12-13T20:45:53 - 1901-12-14T20:45:51 - 1969-12-31T23:59:58 1969-12-31T23:59:59 - 1970-01-01T00:00:00 1970-01-01T00:00:01 @0 - 2038-01-18T03:14:07 2038-01-19T03:14:07 2038-01-19T03:14:08 - 2106-02-07T06:28:15 2106-02-07T06:28:16 - 2242-03-16T12:56:31 2242-03-16T12:56:32 @8589934591 @8589934592 - 9999-12-31T23:59:58 9999-12-31T23:59:59 + 1901-12-14T20:45:51 9999-12-31T23:59:58 9999-12-31T23:59:59 2147483647-12-31T23:59:59 @9223372036854775807' touch -d 2022-01-05T12:22:13 bar >/dev/null 2>&1 || touch -d '2022-01-05 12:22:13' bar >/dev/null 2>&1 || touch bar || framework_failure -for i in ${dates} @-8Ei '2017-10-01 09:00:00' '2017-10-1 9:0:0' \ +for i in ${safe_dates} '2017-10-01 09:00:00' '2017-10-1 9:0:0' \ '2017-10-01 09:00' '2017-10-01 09' 2017-10-01 ./bar ; do # Skip a time stamp $i if it's out of range for this platform, # or if it uses a notation that this platform does not recognize. @@ -776,13 +799,15 @@ for i in ${dates} @-8Ei '2017-10-01 09:00:00' '2017-10-1 9:0:0' \ "${TARLZ}" -q -df out.tar && test_failed $LINENO "$i" "${TARLZ}" -xf out.tar || test_failed $LINENO "$i" if [ "${d_works}" = yes ] ; then - "${TARLZ}" -df out.tar --ignore-overflow || test_failed $LINENO "$i" + "${TARLZ}" -df out.tar --ignore-overflow || + { echo ; "${TARLZ}" -tvf out.tar ; ls -l foo ; test_failed $LINENO "$i" ; } fi done rm -f out.tar foo bar || framework_failure mkdir dir || framework_failure -for i in ${dates} ; do touch -d "$i" "dir/f$i" >/dev/null 2>&1 ; done +for i in ${safe_dates} ${dates} ; do touch -d "$i" "dir/f$i" >/dev/null 2>&1 +done "${TARLZ}" -cf out.tar dir || test_failed $LINENO is_uncompressed out.tar || test_failed $LINENO "${TARLZ}" -df out.tar || test_failed $LINENO @@ -790,7 +815,7 @@ rm -rf out.tar dir || framework_failure printf "\ntesting --diff..." -"${TARLZ}" -xf "${test3_lz}" || test_failed $LINENO +"${TARLZ}" -xf t3.tar.lz || test_failed $LINENO "${TARLZ}" -cf out.tar foo || test_failed $LINENO "${TARLZ}" -cf aout.tar foo --anonymous || test_failed $LINENO is_uncompressed out.tar || test_failed $LINENO @@ -799,27 +824,25 @@ if cmp out.tar aout.tar > /dev/null ; then printf "\nwarning: '--diff' test can't be run as root.\n" else for i in 0 2 6 ; do - "${TARLZ}" -n$i -xf "${test3_lz}" || test_failed $LINENO $i - "${TARLZ}" -n$i -df "${test3_lz}" > out$i + "${TARLZ}" -n$i -xf t3.tar.lz || test_failed $LINENO $i + "${TARLZ}" -n$i -df t3.tar.lz > out$i [ $? = 1 ] || test_failed $LINENO $i - "${TARLZ}" -n$i -df "${test3_lz}" --ignore-ids || test_failed $LINENO $i - "${TARLZ}" -n$i -df "${test3_lz}" --exclude '*' || test_failed $LINENO $i - "${TARLZ}" -n$i -df "${in_tar_lz}" --exclude '*' || test_failed $LINENO $i + "${TARLZ}" -n$i -df t3.tar.lz --ignore-ids || test_failed $LINENO $i + "${TARLZ}" -n$i -df t3.tar.lz --exclude '*' || test_failed $LINENO $i + "${TARLZ}" -n$i -df in.tar.lz --exclude '*' || test_failed $LINENO $i rm -f bar || framework_failure - "${TARLZ}" -n$i -df "${test3_lz}" --ignore-ids foo baz || + "${TARLZ}" -n$i -df t3.tar.lz --ignore-ids foo baz || test_failed $LINENO $i - "${TARLZ}" -n$i -df "${test3_lz}" --ignore-metadata foo baz || + "${TARLZ}" -n$i -df t3.tar.lz --ignore-metadata foo baz || test_failed $LINENO $i - "${TARLZ}" -n$i -df "${test3_lz}" --exclude bar --ignore-ids || + "${TARLZ}" -n$i -df t3.tar.lz --exclude bar --ignore-ids || test_failed $LINENO $i rm -f foo baz || framework_failure - "${TARLZ}" -q -n$i -xf "${test3dir_lz}" || test_failed $LINENO $i - "${TARLZ}" -q -n$i -df "${test3dir_lz}" --ignore-ids || - test_failed $LINENO $i - "${TARLZ}" -q -n$i -df "${test3dir_lz}" dir --ignore-ids || - test_failed $LINENO $i - "${TARLZ}" -n$i -df "${test3_lz}" --ignore-ids -C dir || + "${TARLZ}" -q -n$i -xf t3_dir.tar.lz || test_failed $LINENO $i + "${TARLZ}" -q -n$i -df t3_dir.tar.lz --ignore-ids || test_failed $LINENO $i + "${TARLZ}" -q -n$i -df t3_dir.tar.lz dir --ignore-ids || test_failed $LINENO $i + "${TARLZ}" -n$i -df t3.tar.lz --ignore-ids -C dir || test_failed $LINENO $i rm -rf dir || framework_failure done cmp out0 out2 || test_failed $LINENO @@ -834,68 +857,72 @@ printf "\ntesting --delete..." cp "${in}" out.tar || framework_failure # invalid tar "${TARLZ}" -q -f out.tar --delete foo [ $? = 2 ] || test_failed $LINENO +cmp "${in}" out.tar || test_failed $LINENO rm -f out.tar || framework_failure cp "${in}" out.tar.lz || framework_failure # invalid tar.lz "${TARLZ}" -q -f out.tar.lz --delete foo [ $? = 2 ] || test_failed $LINENO -cp "${in_lz}" out.tar.lz || framework_failure # invalid tar.lz +cmp "${in}" out.tar.lz || test_failed $LINENO +cp in.lz out.tar.lz || framework_failure # invalid tar.lz "${TARLZ}" -q -f out.tar.lz --delete foo [ $? = 2 ] || test_failed $LINENO -rm -f out.tar.lz || framework_failure +cmp in.lz out.tar.lz || test_failed $LINENO +rm -f in.lz out.tar.lz || framework_failure for e in "" .lz ; do - "${TARLZ}" -A "${in_tar}"$e "${test3}"$e > out.tar$e || test_failed $LINENO $e + "${TARLZ}" -A in.tar$e t3.tar$e > out.tar$e || test_failed $LINENO $e "${TARLZ}" -f out.tar$e --delete test.txt || test_failed $LINENO $e - cmp "${test3}"$e out.tar$e || test_failed $LINENO $e + cmp t3.tar$e out.tar$e || test_failed $LINENO $e "${TARLZ}" -f out.tar$e --delete || test_failed $LINENO $e # delete nothing - cmp "${test3}"$e out.tar$e || test_failed $LINENO $e + cmp t3.tar$e out.tar$e || test_failed $LINENO $e "${TARLZ}" -q -f out.tar$e --delete nx_file [ $? = 1 ] || test_failed $LINENO $e - cmp "${test3}"$e out.tar$e || test_failed $LINENO $e - "${TARLZ}" -A "${in_tar}"$e "${test3dir}"$e > out.tar$e || test_failed $LINENO $e + cmp t3.tar$e out.tar$e || test_failed $LINENO $e + "${TARLZ}" -A in.tar$e t3_dir.tar$e > out.tar$e || test_failed $LINENO $e "${TARLZ}" -q -f out.tar$e --delete test.txt || test_failed $LINENO $e - cmp "${test3dir}"$e out.tar$e || test_failed $LINENO $e - "${TARLZ}" -A "${in_tar}"$e "${test3dir}"$e > out.tar$e || test_failed $LINENO $e + cmp t3_dir.tar$e out.tar$e || test_failed $LINENO $e + "${TARLZ}" -A in.tar$e t3_dir.tar$e > out.tar$e || test_failed $LINENO $e "${TARLZ}" -q -f out.tar$e --delete dir || test_failed $LINENO $e - cmp "${in_tar}"$e out.tar$e || test_failed $LINENO $e - "${TARLZ}" -A "${in_tar}"$e "${test3dir}"$e > out.tar$e || test_failed $LINENO $e + cmp in.tar$e out.tar$e || test_failed $LINENO $e + "${TARLZ}" -A in.tar$e t3_dir.tar$e > out.tar$e || test_failed $LINENO $e "${TARLZ}" -q -f out.tar$e --del dir/foo dir/bar dir/baz || test_failed $LINENO $e - cmp "${in_tar}"$e out.tar$e || test_failed $LINENO $e - "${TARLZ}" -A "${in_tar}"$e "${test3dir}"$e > out.tar$e || test_failed $LINENO $e + cmp in.tar$e out.tar$e || test_failed $LINENO $e + "${TARLZ}" -A in.tar$e t3_dir.tar$e > out.tar$e || test_failed $LINENO $e "${TARLZ}" -q -f out.tar$e --del dir/foo dir/baz || test_failed $LINENO $e - cmp "${in_tar}"$e out.tar$e > /dev/null && test_failed $LINENO $e + cmp in.tar$e out.tar$e > /dev/null && test_failed $LINENO $e "${TARLZ}" -q -f out.tar$e --del dir/bar || test_failed $LINENO $e - cmp "${in_tar}"$e out.tar$e || test_failed $LINENO $e - "${TARLZ}" -A "${in_tar}"$e "${test3}"$e > out.tar$e || test_failed $LINENO $e + cmp in.tar$e out.tar$e || test_failed $LINENO $e + "${TARLZ}" -A in.tar$e t3.tar$e > out.tar$e || test_failed $LINENO $e "${TARLZ}" -f out.tar$e --delete foo bar baz || test_failed $LINENO $e - cmp "${in_tar}"$e out.tar$e || test_failed $LINENO $e - "${TARLZ}" -A "${in_tar}"$e "${test3}"$e > out.tar$e || test_failed $LINENO $e + cmp in.tar$e out.tar$e || test_failed $LINENO $e + "${TARLZ}" -A in.tar$e t3.tar$e > out.tar$e || test_failed $LINENO $e "${TARLZ}" -f out.tar$e --del test.txt foo bar baz || test_failed $LINENO $e - cmp "${eoa}"$e out.tar$e || test_failed $LINENO $e - "${TARLZ}" -A "${in_tar}"$e "${test3}"$e > out.tar$e || test_failed $LINENO $e + cmp eoa$e out.tar$e || test_failed $LINENO $e + "${TARLZ}" -A in.tar$e t3.tar$e > out.tar$e || test_failed $LINENO $e for i in test.txt foo bar baz ; do "${TARLZ}" -f out.tar$e --delete $i || test_failed $LINENO "$e $i" done - cmp "${eoa}"$e out.tar$e || test_failed $LINENO $e - "${TARLZ}" -A "${in_tar}"$e "${test3}"$e > out.tar$e || test_failed $LINENO $e + cmp eoa$e out.tar$e || test_failed $LINENO $e + "${TARLZ}" -A in.tar$e t3.tar$e > out.tar$e || test_failed $LINENO $e for i in baz bar foo test.txt ; do "${TARLZ}" -f out.tar$e --delete $i || test_failed $LINENO "$e $i" done - cmp "${eoa}"$e out.tar$e || test_failed $LINENO $e - "${TARLZ}" -A "${in_tar}"$e "${test3}"$e > out.tar$e || test_failed $LINENO $e + cmp eoa$e out.tar$e || test_failed $LINENO $e + "${TARLZ}" -A in.tar$e t3.tar$e > out.tar$e || test_failed $LINENO $e for i in foo bar test.txt baz ; do "${TARLZ}" -f out.tar$e --delete $i || test_failed $LINENO "$e $i" done - cmp "${eoa}"$e out.tar$e || test_failed $LINENO $e - "${TARLZ}" -A "${in_tar}"$e "${t155}"$e "${test3}"$e > out.tar$e || + cmp eoa$e out.tar$e || test_failed $LINENO $e + "${TARLZ}" -A in.tar$e t155.tar$e t3.tar$e > out.tar$e || test_failed $LINENO $e "${TARLZ}" -f out.tar$e --del baz foo test.txt bar || test_failed $LINENO $e - cmp "${t155}"$e out.tar$e || test_failed $LINENO $e + cmp t155.tar$e out.tar$e || test_failed $LINENO $e "${TARLZ}" -f out.tar$e --delete link || test_failed $LINENO $e "${TARLZ}" -q -tf out.tar$e || test_failed $LINENO $e - cmp "${t155}"$e out.tar$e > /dev/null && test_failed $LINENO $e + cmp t155.tar$e out.tar$e > /dev/null && test_failed $LINENO $e rm -f out.tar$e || framework_failure done +rm -f in.tar in.tar.lz t3.tar t3_dir.tar t3_dir.tar.lz || framework_failure # test --delete individual member after collective member cp cfoo foo || framework_failure @@ -921,17 +948,17 @@ rm -f out.tar.lz foo bar baz test.txt || framework_failure "${TARLZ}" -q -f test3_em.tar.lz --delete foo [ $? = 2 ] || test_failed $LINENO rm -f test3_em.tar.lz || framework_failure -cp "${testdir}"/test3_gh5.tar.lz out.tar.lz || framework_failure +cp "${testdir}"/test3_gh1.tar.lz out.tar.lz || framework_failure for i in foo bar baz ; do "${TARLZ}" -f out.tar.lz --delete $i || test_failed $LINENO $i done rm -f out.tar.lz || framework_failure -for i in 1 2 3 4 ; do - cp "${testdir}"/test3_gh${i}.tar out.tar || framework_failure - for j in foo bar baz ; do - "${TARLZ}" -f out.tar --delete $j || test_failed $LINENO "$i $j" - done - rm -f out.tar || framework_failure +for i in gh1 gh2 gh3 gh4 ; do + lzip -cd "${testdir}"/test3_${i}.tar.lz > out.tar || framework_failure + for j in foo bar baz ; do + "${TARLZ}" -f out.tar --delete $j || test_failed $LINENO "$i $j" + done + rm -f out.tar || framework_failure done printf "\ntesting --dereference..." @@ -1007,10 +1034,10 @@ cmp out.tar.lz aout.tar.lz || test_failed $LINENO "${TARLZ}" --un -q -rf aout.tar.lz foo bar baz # contradictory ext [ $? = 1 ] || test_failed $LINENO cmp out.tar.lz aout.tar.lz || test_failed $LINENO -cp "${eoa_lz}" aout.tar.lz || framework_failure # append to empty archive +cp eoa.lz aout.tar.lz || framework_failure # append to empty archive "${TARLZ}" -0 -rf aout.tar.lz foo bar baz || test_failed $LINENO cmp out.tar.lz aout.tar.lz || test_failed $LINENO -rm -f out.tar.lz aout.tar.lz || framework_failure +rm -f eoa.lz out.tar.lz aout.tar.lz || framework_failure # test --append --uncompressed "${TARLZ}" -cf out.tar foo bar baz || test_failed $LINENO @@ -1041,10 +1068,10 @@ cmp out.tar aout.tar || test_failed $LINENO "${TARLZ}" -0 -q -rf aout.tar foo bar baz # contradictory ext [ $? = 1 ] || test_failed $LINENO cmp out.tar aout.tar || test_failed $LINENO -cp "${eoa}" aout.tar || framework_failure # append to empty archive +cp eoa aout.tar || framework_failure # append to empty archive "${TARLZ}" -rf aout.tar foo bar baz || test_failed $LINENO cmp out.tar aout.tar || test_failed $LINENO -rm -f out.tar aout.tar || framework_failure +rm -f eoa out.tar aout.tar || framework_failure # test --append to solid archive "${TARLZ}" --solid -q -0 -cf out.tar.lz "${in}" foo bar || test_failed $LINENO @@ -1136,13 +1163,13 @@ fi printf "\ntesting long names..." -"${TARLZ}" -q -tf "${t155}" || test_failed $LINENO -"${TARLZ}" -q -tf "${t155_lz}" || test_failed $LINENO +"${TARLZ}" -q -tf t155.tar || test_failed $LINENO +"${TARLZ}" -q -tf t155.tar.lz || test_failed $LINENO if [ "${ln_works}" = yes ] ; then mkdir dir1 || framework_failure - "${TARLZ}" -C dir1 -xf "${t155}" || test_failed $LINENO + "${TARLZ}" -C dir1 -xf t155.tar || test_failed $LINENO mkdir dir2 || framework_failure - "${TARLZ}" -C dir2 -xf "${t155_lz}" || test_failed $LINENO + "${TARLZ}" -C dir2 -xf t155.tar.lz || test_failed $LINENO diff -ru dir1 dir2 || test_failed $LINENO "${TARLZ}" -cf out.tar.lz dir2 || test_failed $LINENO rm -rf dir2 || framework_failure @@ -1152,6 +1179,7 @@ if [ "${ln_works}" = yes ] ; then rmdir dir1 2> /dev/null && test_failed $LINENO rm -rf out.tar.lz dir2 dir1 || framework_failure fi +rm -f t155.tar.lz || framework_failure "${TARLZ}" -tvf "${testdir}"/ug32chars.tar.lz | grep -q \ -e very_long_owner_name_of_32_chars/very_long_group_name_of_32_chars || @@ -1255,6 +1283,19 @@ rm -f foo bar baz test.txt out.tar.lz out.tar outz.tar foobarbaz.tar.lz \ printf "\ntesting bad input..." +"${TARLZ}" -q -tf "${testdir}"/test3_crc.tar.lz +[ $? = 2 ] || test_failed $LINENO +"${TARLZ}" -q -n0 -tf "${testdir}"/test3_crc.tar.lz +[ $? = 2 ] || test_failed $LINENO +lzip -cd "${testdir}"/test3_crc.tar.lz | "${TARLZ}" -q -tf - +[ $? = 2 ] || test_failed $LINENO +"${TARLZ}" -q -tf "${testdir}"/test3_crc.tar.lz --missing-crc +[ $? = 2 ] || test_failed $LINENO +"${TARLZ}" -q -n0 -tf "${testdir}"/test3_crc.tar.lz --missing-crc +[ $? = 2 ] || test_failed $LINENO +lzip -cd "${testdir}"/test3_crc.tar.lz | "${TARLZ}" -q -tf - --missing-crc +[ $? = 2 ] || test_failed $LINENO + # test --extract ".." mkdir dir1 || framework_failure cd dir1 || framework_failure @@ -1273,15 +1314,6 @@ done cd .. || framework_failure rm -rf dir1 || framework_failure -# test --list and --extract truncated tar -dd if="${in_tar}" of=truncated.tar bs=1000 count=1 2> /dev/null -"${TARLZ}" -q -tf truncated.tar > /dev/null -[ $? = 2 ] || test_failed $LINENO -"${TARLZ}" -q -xf truncated.tar -[ $? = 2 ] || test_failed $LINENO -[ ! -e test.txt ] || test_failed $LINENO -rm -f truncated.tar || framework_failure - # test --delete with split 'bar' tar member for i in 1 2 3 4 ; do cp "${testdir}"/test3_sm${i}.tar.lz out.tar.lz || framework_failure @@ -1302,22 +1334,25 @@ done # test --list and --extract format violations if [ "${ln_works}" = yes ] ; then mkdir dir1 || framework_failure - "${TARLZ}" -C dir1 -xf "${t155}" || test_failed $LINENO + "${TARLZ}" -C dir1 -xf t155.tar || test_failed $LINENO fi -for i in 1 2 3 ; do - "${TARLZ}" -q -tf "${testdir}"/t155_fv${i}.tar +for i in 1 2 3 7 ; do + lzip -cd "${testdir}"/t155_fv${i}.tar.lz > t155_fv${i}.tar || + framework_failure + "${TARLZ}" -q -tf t155_fv${i}.tar [ $? = 2 ] || test_failed $LINENO $i - "${TARLZ}" -q -tf "${testdir}"/t155_fv${i}.tar --permissive || - test_failed $LINENO $i + "${TARLZ}" -q -tf t155_fv${i}.tar --permissive || test_failed $LINENO $i if [ "${ln_works}" = yes ] ; then mkdir dir2 || framework_failure - "${TARLZ}" -C dir2 -xf "${testdir}"/t155_fv${i}.tar --permissive || + "${TARLZ}" -C dir2 -xf t155_fv${i}.tar --permissive || test_failed $LINENO $i diff -ru dir1 dir2 || test_failed $LINENO $i rm -rf dir2 || framework_failure fi + rm -f t155_fv${i}.tar || framework_failure done -for i in 1 2 3 4 5 6 ; do +rm -f t155.tar || framework_failure +for i in 1 2 3 4 5 6 7 ; do "${TARLZ}" -q -tf "${testdir}"/t155_fv${i}.tar.lz [ $? = 2 ] || test_failed $LINENO $i "${TARLZ}" -q -tf "${testdir}"/t155_fv${i}.tar.lz --permissive || @@ -1332,7 +1367,8 @@ for i in 1 2 3 4 5 6 ; do done if [ "${ln_works}" = yes ] ; then rm -rf dir1 || framework_failure ; fi -for i in "${testdir}"/test3_nn.tar "${testdir}"/test3_nn.tar.lz ; do +lzip -cd "${testdir}"/test3_nn.tar.lz > test3_nn.tar || framework_failure +for i in test3_nn.tar "${testdir}"/test3_nn.tar.lz ; do "${TARLZ}" -q -n0 -tf "$i" || test_failed $LINENO "$i" "${TARLZ}" -q -n4 -tf "$i" || test_failed $LINENO "$i" "${TARLZ}" -q -n0 -xf "$i" || test_failed $LINENO "$i" @@ -1352,12 +1388,15 @@ for i in "${testdir}"/test3_nn.tar "${testdir}"/test3_nn.tar.lz ; do cmp cbaz baz || test_failed $LINENO "$i" rm -f foo bar baz || framework_failure done +rm -f test3_nn.tar || framework_failure printf "\ntesting --keep-damaged..." -# test --extract and --keep-damaged compressed +# test --list, --extract, and --keep-damaged truncated/damaged compressed rm -f test.txt || framework_failure -for i in "${inbad1}" "${inbad2}" ; do +for i in "${test_bad1}" "${test_bad2}" ; do + "${TARLZ}" -q -n0 -tf "${i}.tar.lz" + [ $? = 2 ] || test_failed $LINENO "$i" "${TARLZ}" -q -xf "${i}.tar.lz" [ $? = 2 ] || test_failed $LINENO "$i" [ ! -e test.txt ] || test_failed $LINENO "$i" @@ -1365,103 +1404,106 @@ for i in "${inbad1}" "${inbad2}" ; do "${TARLZ}" -q -n0 -xf "${i}.tar.lz" --keep-damaged [ $? = 2 ] || test_failed $LINENO "$i" [ -e test.txt ] || test_failed $LINENO "$i" - cmp "$i" test.txt 2> /dev/null || test_failed $LINENO "$i" + cmp "$i" test.txt || test_failed $LINENO "$i" rm -f test.txt || framework_failure done # rm -f foo bar baz || framework_failure -"${TARLZ}" -q -n0 -xf "${bad1_lz}" +"${TARLZ}" -q -n0 -xf "${t3_bad1_lz}" [ $? = 2 ] || test_failed $LINENO [ ! -e foo ] || test_failed $LINENO cmp cbar bar || test_failed $LINENO cmp cbaz baz || test_failed $LINENO rm -f foo bar baz || framework_failure -"${TARLZ}" -q -n0 -xf "${bad2_lz}" +"${TARLZ}" -q -n0 -xf "${t3_bad2_lz}" [ $? = 2 ] || test_failed $LINENO [ ! -e foo ] || test_failed $LINENO cmp cbar bar || test_failed $LINENO cmp cbaz baz || test_failed $LINENO rm -f foo bar baz || framework_failure -"${TARLZ}" -q -n0 -xf "${bad3_lz}" +"${TARLZ}" -q -n0 -xf "${t3_bad3_lz}" [ $? = 2 ] || test_failed $LINENO cmp cfoo foo || test_failed $LINENO [ ! -e bar ] || test_failed $LINENO cmp cbaz baz || test_failed $LINENO rm -f foo bar baz || framework_failure -"${TARLZ}" -q -n0 -xf "${bad3_lz}" --keep-damaged -[ $? = 2 ] || test_failed $LINENO -cmp cfoo foo || test_failed $LINENO -cmp cbar bar 2> /dev/null || test_failed $LINENO -cmp cbaz baz || test_failed $LINENO -rm -f foo bar baz || framework_failure -"${TARLZ}" -q -n0 -xf "${bad4_lz}" -[ $? = 2 ] || test_failed $LINENO -[ ! -e foo ] || test_failed $LINENO -[ ! -e bar ] || test_failed $LINENO -cmp cbaz baz || test_failed $LINENO -rm -f foo bar baz || framework_failure -"${TARLZ}" -q -n0 -xf "${bad4_lz}" --keep-damaged -[ $? = 2 ] || test_failed $LINENO -[ ! -e foo ] || test_failed $LINENO -cmp cbar bar 2> /dev/null || test_failed $LINENO -cmp cbaz baz || test_failed $LINENO -rm -f foo bar baz || framework_failure -"${TARLZ}" -q -n0 -xf "${bad5_lz}" -[ $? = 2 ] || test_failed $LINENO -[ ! -e foo ] || test_failed $LINENO -[ ! -e bar ] || test_failed $LINENO -cmp cbaz baz || test_failed $LINENO -rm -f foo bar baz || framework_failure -"${TARLZ}" -q -n0 -xf "${bad5_lz}" --keep-damaged -[ $? = 2 ] || test_failed $LINENO -cmp cfoo foo 2> /dev/null || test_failed $LINENO -[ ! -e bar ] || test_failed $LINENO -cmp cbaz baz || test_failed $LINENO -rm -f foo bar baz || framework_failure -"${TARLZ}" -q -n0 -xf "${bad6_lz}" +"${TARLZ}" -q -n0 -xf "${t3_bad3_lz}" --keep-damaged [ $? = 2 ] || test_failed $LINENO cmp cfoo foo || test_failed $LINENO cmp cbar bar || test_failed $LINENO cmp cbaz baz || test_failed $LINENO +rm -f foo bar baz || framework_failure +"${TARLZ}" -q -n0 -xf "${t3_bad4_lz}" +[ $? = 2 ] || test_failed $LINENO +[ ! -e foo ] || test_failed $LINENO +[ ! -e bar ] || test_failed $LINENO +cmp cbaz baz || test_failed $LINENO +rm -f foo bar baz || framework_failure +"${TARLZ}" -q -n0 -xf "${t3_bad4_lz}" --keep-damaged +[ $? = 2 ] || test_failed $LINENO +[ ! -e foo ] || test_failed $LINENO +cmp cbar bar || test_failed $LINENO +cmp cbaz baz || test_failed $LINENO +rm -f foo bar baz || framework_failure +"${TARLZ}" -q -n0 -xf "${t3_bad5_lz}" +[ $? = 2 ] || test_failed $LINENO +[ ! -e foo ] || test_failed $LINENO +[ ! -e bar ] || test_failed $LINENO +cmp cbaz baz || test_failed $LINENO +rm -f foo bar baz || framework_failure +"${TARLZ}" -q -n0 -xf "${t3_bad5_lz}" --keep-damaged +[ $? = 2 ] || test_failed $LINENO +cmp cfoo foo || test_failed $LINENO +[ ! -e bar ] || test_failed $LINENO +cmp cbaz baz || test_failed $LINENO +rm -f foo bar baz || framework_failure +"${TARLZ}" -q -n0 -xf "${testdir}"/test3_bad6.tar.lz +[ $? = 2 ] || test_failed $LINENO +cmp cfoo foo || test_failed $LINENO +cmp cbar bar || test_failed $LINENO +cmp cbaz baz || test_failed $LINENO +rm -f foo bar baz || framework_failure -# test --extract and --keep-damaged uncompressed +# test --list, --extract, and --keep-damaged truncated uncompressed +"${TARLZ}" -q -tf "${test_bad1}.tar" +[ $? = 2 ] || test_failed $LINENO rm -f test.txt || framework_failure -"${TARLZ}" -q -xf "${inbad1}.tar" +"${TARLZ}" -q -xf "${test_bad1}.tar" [ $? = 2 ] || test_failed $LINENO [ ! -e test.txt ] || test_failed $LINENO rm -f test.txt || framework_failure -"${TARLZ}" -q -xf "${inbad1}.tar" --keep-damaged +"${TARLZ}" -q -xf "${test_bad1}.tar" --keep-damaged [ $? = 2 ] || test_failed $LINENO [ -e test.txt ] || test_failed $LINENO -cmp "${inbad1}" test.txt 2> /dev/null || test_failed $LINENO +cmp "${test_bad1}" test.txt || test_failed $LINENO rm -f test.txt || framework_failure -# -rm -f foo bar baz || framework_failure -"${TARLZ}" -q -xf "${bad1}" + +# test --extract and --keep-damaged uncompressed +"${TARLZ}" -q -xf "${testdir}"/test3_bad1.tar [ $? = 2 ] || test_failed $LINENO [ ! -e foo ] || test_failed $LINENO cmp cbar bar || test_failed $LINENO cmp cbaz baz || test_failed $LINENO rm -f foo bar baz || framework_failure -"${TARLZ}" -q -xf "${bad2}" +"${TARLZ}" -q -xf "${testdir}"/test3_bad2.tar [ $? = 2 ] || test_failed $LINENO cmp cfoo foo || test_failed $LINENO [ ! -e bar ] || test_failed $LINENO cmp cbaz baz || test_failed $LINENO rm -f foo bar baz || framework_failure -"${TARLZ}" -q -xf "${bad3}" +"${TARLZ}" -q -xf "${testdir}"/test3_bad3.tar [ $? = 2 ] || test_failed $LINENO cmp cfoo foo || test_failed $LINENO cmp cbar bar || test_failed $LINENO [ ! -e baz ] || test_failed $LINENO rm -f foo bar baz || framework_failure -"${TARLZ}" -q -xf "${bad4}" +"${TARLZ}" -q -xf "${testdir}"/test3_bad4.tar [ $? = 2 ] || test_failed $LINENO cmp cfoo foo || test_failed $LINENO [ ! -e bar ] || test_failed $LINENO [ ! -e baz ] || test_failed $LINENO rm -f foo bar baz || framework_failure -"${TARLZ}" -q -xf "${bad5}" +"${TARLZ}" -q -xf "${testdir}"/test3_bad5.tar [ $? = 2 ] || test_failed $LINENO cmp cfoo foo || test_failed $LINENO cmp cbar bar || test_failed $LINENO @@ -1469,20 +1511,20 @@ cmp cbaz baz || test_failed $LINENO rm -f cfoo cbar cbaz foo bar baz || framework_failure # rm -f test3.tar.lz || framework_failure -"${TARLZ}" -q -xf "${tlzit1}" +"${TARLZ}" -q -xf "${testdir}"/tlz_in_tar1.tar [ $? = 2 ] || test_failed $LINENO [ ! -e foo ] || test_failed $LINENO [ ! -e bar ] || test_failed $LINENO [ ! -e baz ] || test_failed $LINENO [ ! -e test3.tar.lz ] || test_failed $LINENO rm -f foo bar baz test3.tar.lz || framework_failure -"${TARLZ}" -q -xf "${tlzit2}" +"${TARLZ}" -q -xf "${testdir}"/tlz_in_tar2.tar [ $? = 2 ] || test_failed $LINENO [ ! -e foo ] || test_failed $LINENO [ ! -e bar ] || test_failed $LINENO [ ! -e baz ] || test_failed $LINENO -cmp "${test3_lz}" test3.tar.lz || test_failed $LINENO -rm -f foo bar baz test3.tar.lz || framework_failure +cmp t3.tar.lz test3.tar.lz || test_failed $LINENO +rm -f foo bar baz t3.tar.lz test3.tar.lz || framework_failure echo if [ ${fail} = 0 ] ; then diff --git a/testsuite/eoa_blocks.tar.lz b/testsuite/eoa_blocks.lz similarity index 100% rename from testsuite/eoa_blocks.tar.lz rename to testsuite/eoa_blocks.lz diff --git a/testsuite/eoa_blocks.tar b/testsuite/eoa_blocks.tar deleted file mode 100644 index 06d7405020018ddf3cacee90fd4af10487da3d20..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1024 ScmZQz7zLvtFd70QH3R?z00031 diff --git a/testsuite/t155.tar b/testsuite/t155.tar deleted file mode 100644 index f2b8a4ea9178c8d05e73efa6372fc1230c12c6ad..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9216 zcmeHL%~HZJ5YCyW;01K@w@Ep0s7Sqe^V~s1WyIDgbw=ObY(a*05a>W@kljO*Zjw#r z>+ZMPFxH;%rAVtvhj41ewPIf-Z9^}g^L&&s&KolCyIEnN`!UL2`%lv>8NE+p0T4fa z=aYFnOOuy4@jyCKcJJ;VuE*I}$^Ia)z;6TF@f}(^-x2`~a|_^ttdMGpN+y&7r9ntz zaoidS3{HXIZ~SLzns3{0*Z8)zG`10e!W-N}30)dT6<3z{zl2INFG4Noe>P3tI>Pk2 z*eMc%ui3yQq;f*&$>?S%xuk^9jEfv>73196g4eP82mfoS!B`7o!T(AzdGOAA7$E-N z1EglApfjL=-oXD(NA-U*xUXAX)&I!*mE!+Ocv1c*XPTG7z}N^NxcIz;F}|DstM$Jj z{$CH&f7nOAK%x7e_CD@b{+FKcf9d{*i;zbHJG^*WZO>_H?u;?MoBv(?Z@3X4UHvb( zwvPX?CaJks?deqi*M{s^3(zl6i2sj;Xni{?{y&@R9wh@FLPY#u|6{kb0%P2`|9Nbk jR9mM~8}9$zyi=U_!ukI>O;3R`?Li0-0)zk|;28oxeYq|J diff --git a/testsuite/t155_fv1.tar b/testsuite/t155_fv1.tar deleted file mode 100644 index 1ef64c3f473d4b482bcbe60e9e1cac8a9bf96f76..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10240 zcmeHL%~HZJ5YCyW;01K@w@Ep0s7Sqe^V~s1WyIDgbw=ObY(a(=1Uk?kWcSddn`Afh zHT&&0j5Q~GDblLkCY)Mvt=LyiThYtsJRK#B^NP&3-K;Rs{TQXM{ikUXjov4r00RQYI>Pk2 z*vS$?TeE>nNcn`&lflhUa!CoHnj>aU#$NP z@&9_D{=+_=3lzHlY3}3h-J_((eF%vE%YW>KR$z=9_dk!V zlVa;sYRmncjknj8@Vga9GXDg=H2 DG2ea< diff --git a/testsuite/t155_fv2.tar b/testsuite/t155_fv2.tar deleted file mode 100644 index f732b308160b1928c0faec28479868bd1fcd8fed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10240 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 T&5Zy600000tsDRV00000xMEoc diff --git a/testsuite/test.txt.tar b/testsuite/test.txt.tar deleted file mode 100644 index 151ab1ea20f672ed5118d7c5f7c112afd062f3c0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 37888 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 L@NWt{oC5z3uyye? diff --git a/testsuite/test.txt.tar.lz b/testsuite/test.txt.tar.lz index 654b2b04e45e67c31b8c95d72484c5bbdc1fe696..d2f5aa118e0f968df8adbc9637115fcce1a6fde3 100644 GIT binary patch delta 7434 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 IP?P%{DB>=A7XSbN delta 7434 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 IOq2B-E80Iy#Q*>R diff --git a/testsuite/test3.tar b/testsuite/test3.tar deleted file mode 100644 index d58fb455ddbb42cb27cc6a615680cb037f70f599..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4096 zcmeHGyAFUL5M%ah4m|ODaB#iE$KuypRtJqHXBgV$5-!lDrL$S^h5%VL)CpWCjx@wR zsb(2eN9?2YX$ayitiHdmTUkK4m~D4_Py4&O4ew^Nu3>qU=Ip?P&+m;|`q%xJ%>H`^ qgZGg?`~QQ_tx`0otuwe_g7wex%z_@Q;_qS7iG=6%>JG4uU!3S#w-PG zUd}iM76vd#o|s^0e8kLqyLp3UDdT4KFE4wKre7Bo?0M;L6lp!n>X|5aFwdv|GmUpP zoD{HgglS8ec*4ji=DzN#v;1$HciVV`9ainPDeLl-{k-+`hw^hihcfRi(P;WJ(Lv$- Nm3OvqU6TzNjR7U{MWg@# delta 185 zcmcc4^n_`GhDD^q?MwHi8MI0E dc56)DH|2HB`tSWSf#Lj>ceXICnUhr*jRCDOQJMe% diff --git a/testsuite/test3_bad1.tar.lz b/testsuite/test3_bad1.tar.lz index 9f5d40fd4c9384c00ed0856b5acb62953e7e0999..dc5927a80d81c04c004767bdeeb0472c20cd00e4 100644 GIT binary patch delta 172 zcmaFDbe(B}hDmsfgYLiCb7b>0otuwe_g7wex%z_@Q;_qS7iG=6%>JG4uU!3S#w-PG zUd}iM76vd#o|s^0e8kLqyLp3UDdT4KFE4wKre7Bo?0M;L6lp!n>X|5aFwdv|GmUpP zoD{HgglS8ec*4ji=DzN#v;1$HciVV`9ainPDeLl-{k-+`hw^hihcfRi(P;WJ(Lv$- Nm3OvqU6TzNjR7U{MWg@# delta 185 zcmcc4^n_`GhDD^q?MwHi8MI0E dc56)DH|2HB`tSWSf#Lj>ceXICnUhr*jRCDOQJMe% diff --git a/testsuite/test3_bad2.tar.lz b/testsuite/test3_bad2.tar.lz index 182c04833bf881e7711caaf1f6d4e428b16da919..f9203dcacd47690a2aa00e38603dbb8aa578e012 100644 GIT binary patch delta 172 zcmaFDbe(B}hDmsfgYLiCb7b=}otuwe_g7wex%z_@Q;_qS7iG=6%>JG4uU!3S#w-PG zUd}iM76vd#o|s^0e8kLqyLp3UDdT4KFE4wKre7Bo?0M;L6lp!n>X|5aFwdv|GmUpP zoD{HgglS8ec*4ji=DzN#v;1$HciVV`9ainPDeLl-{k-+`hw^hihcfRi(P;WJ(Lv$- Nm3OvqU6TzNjR8#ZMdSbg delta 185 zcmcc4^n_`GhDD^q?MwHi8MI0E dc56)DH|2HB`tSWSf#Lj>ceXICnUhr*jRC?@QMv#C diff --git a/testsuite/test3_bad3.tar.lz b/testsuite/test3_bad3.tar.lz index 3b46163b68daf8114c7771a5195652a5591126c5..a1b934842c43004efd9dddf6b792ade8b9b5af2a 100644 GIT binary patch delta 172 zcmaFDbe(B}hDmsfgYLiCb7b>0otuwe_g7wex%z_@Q;_qS7iG=6%>JG4uU!3S#w-PG zUd}iM76vd#o|s^0e8kLqyLp3UDdT4KFE4wKre7Bo?0M;L6lq;$^-PpInCH{~nZ`RC zP72sL!nCDKJYi%Mb60E dc56)DH|2HB`tSWSf#Lj>ceXICnUhr*jRB{@QHlTn diff --git a/testsuite/test3_bad4.tar.lz b/testsuite/test3_bad4.tar.lz index 7ac6d986210258be84bca96e87ada05bb105600a..94737448ddc4bc22377a80193ea661cdb8fc212e 100644 GIT binary patch delta 172 zcmaFDbe(B}hDmsfgYLiCb7b=}otuwe_g7wex%z_@Q;_qS7iG=6%>JG4uU!3S#w-PG zUd}iM76vd#o|s^0e8kLqyLp3UDdT4KFE4wKre7Bo?0M;L6lq;$^-PpInCH{~nZ`RC zP72sL!nCDKJYi%Mb60E dc56)DH|2HB`tSWSf#Lj>ceXICnUhr*jRCyjQK|p{ diff --git a/testsuite/test3_bad5.tar.lz b/testsuite/test3_bad5.tar.lz index 5b4feb3dec47deeac432de8b4910d8b932f11b22..660757e424b0e05e95fc7b211138ab690f3e7746 100644 GIT binary patch delta 160 zcmaFDbe(B}hD>;igYLiCb7b>0otuwe_g7wex%z_@Q;_qS7iG;81I;ETaM}3rKl9Fi z736pSh?)0x^9IXO#?9(qUiKbMzb-1+^U~ia(t4KFGg0nfo=^X08t-g4DPZTwz`_6q zDHBf^8O7Y!U3HfKZS!s$kFdk4{WfJ?p0b~}p8imN&gW3(y(Jn=e*bhS} delta 173 zcmcc4^n_`GhJ2*M?MwHi8MImWE8r?|wBbSLI>b6~6oR?g=yFT5F?E-uM;s|HY-q z1t$gU92r;`z#wDd0V8ucwvC75njd|axY8q#b%kNgf``YKhy`G@YA|U-wsDdb#?86;qJ&nHOcvyUhNb@2_0_XT~fA zZC=he1{MY|NS++WWN3WE%zL|egJmh>X7w*Gdyl4H7ZvPz>2DNiJR_(Vb>++QSy!G^l@^e0iGVd+XX!F71M)A=C1^Uk%GudDwP^?|!{|!pykV z+US!ve#QKMaVc`aNdY@YnBI)Z`Y7LX;&Eu6{eExG>Y8P{ZfO6|0+npw7 zW_NKdm|xx{Y4_J>K|#jRY?06N8t1fnPG_I`$nCGcxQdLLhI$}H)!W2;c>JLJAlP(S8Kc1QO+ z1e#w)G*g}rWe_TQzeM0q{=E`k|8H$^P4XUluP8ZnM(J@|^r+ zld01E|68q#EAQJ}%3$DN00SXL2<eP?w332TFwnw-MpHu<67+h zg(fRz3Qn#(vHsnx=9oDN7mN!;&V6p!aat(i@p5;CeGht{XPr@7vqe{%DXinNg=f|8 zqk3W&R~-L;=WW_jpLMC%hir%-(unVgKL0+>7A*;z>raz!C4O97|29A;caPl$2X67t z-4CzNVUtRpU2$J-Ij42ozjmW(B?net&``R);*9U&#Q*>i*?=+t literal 0 HcmV?d00001 diff --git a/testsuite/test3_dir.tar b/testsuite/test3_dir.tar deleted file mode 100644 index e0c2b29949af0277fe76e364e70bc1bab6436f0c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4096 zcmeH`!3uyN42C`TGHOkGo+5%yg4v~~Z$fs6APRPf{#_;;&iVg*dYGafLr{WYI6RTc znd6+fM9MZbkvER0<-WDn*UAX)bc|l+)J1+WhLDcp#o?}hapemow!Zqpp4d@F0iF5p znE=7a&_Cv%8t^aZNg2)7ga0DN5A)9dD*5$S{5$Y3=}DR0cPscWVjx2SC;$b1q`(Fo CaXcje diff --git a/testsuite/test3_eoa1.tar b/testsuite/test3_eoa1.tar deleted file mode 100644 index 175b8076c32f98ca199faf5b2a8ed209f1763dda..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3072 zcmeHGF%Ezr3}p5-1q;3h2iGQui?8=s9W0otuwe_g7wex%z_@Q;_qS7iG=6%>JG4uU!3S#w-PG zUd}iM76vd#o|s^0e8kLqyLp3UDdT4KFE4wKre7Bo?0M;L6lp!n>X|5aFwdv|GmUpP zoD{HgglS8ec*4js=DzN#v;1$HciVV`9ainPDeLl-{k-+`hw^hihcfRi(P;WJ(Lv$- Mm3OvqT@X7N0Jf+^TL1t6 delta 188 zcmZ3@w1a7ahDD^q?MwHi8MI0E cc56)DH|2HB`tSWSf#Lj>ceXICnGnkv0A0~gUH||9 diff --git a/testsuite/test3_eoa2.tar b/testsuite/test3_eoa2.tar deleted file mode 100644 index 458be1e4f3887a7b46685a74fb110d9decc12f8f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3584 zcmeHGIS#-e3}fzV0;KdkFtA4IvGnydYX_=UO2rT_k%+*SjjM>$1_6oLwF%rOj@bDa zm=n#+3-+F3m>p>gYwsW1o>x$=B72YTY2Uj$@UDtX1IsgM$_^BKer>eUzwSRJ>%Vp| ncpv#&|9|j#REm;Mb!nga@2&sp!GHkyTmSzs+BnRC0Ufvie&;t% diff --git a/testsuite/test3_eoa2.tar.lz b/testsuite/test3_eoa2.tar.lz index 1f479538ca279b95e3d0efca8ff0f694a968d59e..9e90248579b6c20f2ac4f9bbd528e6977373f9cd 100644 GIT binary patch delta 172 zcmaFBbeU;_hDmsfgYLiCb7b>0otuwe_g7wex%z_@Q;_qS7iG=6%>JG4uU!3S#w-PG zUd}iM76vd#o|s^0e8kLqyLp3UDdT4KFE4wKre7Bo?0M;L6lp!n>X|5aFwdv|GmUpP zoD{HgglS8ec*4ji=DzN#v;1$HciVV`9ainPDeLl-{k-+`hw^hihcfRi(P;WJ(Lv$- Nm3OvqU6TzN^#LH3MVJ5p delta 185 zcmcc2^nhuChDD^q?MwHi8MI0E dc56)DH|2HB`tSWSf#Lj>ceXICnUhr*^#P}CQH}rr diff --git a/testsuite/test3_eoa3.tar b/testsuite/test3_eoa3.tar deleted file mode 100644 index 3003a93532ef188caea0c8a8411f67eeaea767b7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4608 zcmeH{y$*md2!uQPG_+!Uo;tXa__O%*URMW=CYTP#LQ+zWe1x0Lf_DfatA?5p*Ni6( zu}`X52GtSuD191&cn?JB;ZJ3g86wzHT~EA vFPZ-Dy%_Yz{^|ceaBfv%Du?@MpYA{M{@oV?0qmdm{|9JlkN^pgKw|_>A(J;w diff --git a/testsuite/test3_eoa3.tar.lz b/testsuite/test3_eoa3.tar.lz index 20ba9f8c0030825796714e8359918b4c11f5cb9b..2f7bea5011644455d4ad0057969cb1ad47348bf8 100644 GIT binary patch delta 172 zcmeBS{?9Z)!z8@LLHFP6IkI`0&dtZK`ztTKT>Zg{DaiTEi?ZfjX8+FjSFZjuW0rz8 zFJ~MB3j-J=PfUn3K4Rv*-MqoFlyS5AmzTXq)31vP_Pq2rinN|(^-PpInCH{~nZ`RC zP72sL!nCDK{9t4hb6^L<;g!gbGGUT>Fn!P1av`Q5LE<*GbvyTW(B-aTPvTx)Ig z$s507{=c{sx!|OLog++Z#>5v!=5lNs563k>`Yv&$M17YISYec+mlhUBdyn9y^Dv;*!o#b=NqB{Zl1vX;>XzQr%85z6m;g$E2K*@B)7+xG z1RQ{Vxu#PN_h^Yq=y{Si#Omfr`B2Slr3-8yc=?VoczAryJKI(4aC=uCkF06Zwe=ro F>kazGe-How diff --git a/testsuite/test3_eoa4.tar.lz b/testsuite/test3_eoa4.tar.lz index 1593feb44dffe40ff0f4376a1493e1f8ceb4f438..42a629c2f116d37098847a2d690b3a69a3e1f951 100644 GIT binary patch delta 172 zcmbQv(#0}C!z8@LLHFP6IkI`0&dtZK`ztTKT>Zg{DaiTEi?ZfjX8+FjSFZjuW0rz8 zFJ~MB3j-J=PfRd0K4Rv*-MqoFlyS5AmzTXq)31vP_Pq2rinN|(^-PpInCH{~nZ`RC zP72sL!nCDKJYi%Mb6Fn!P1av`Q5LE<*GbvyTW(B-aTPvTx)Ig z$s507{=c{sx!|OLog++Z#>4|g=5lNs563k>`Yv&$MZg{DaiTEi?ZfjX8+FjSFZjuW0rz8 zFJ~MB3j-J=Z%hziG(KYHz1_UQvXpVN`j?lzN7Jv13iiD8H;S~LW%W#yJDBIw|Cz=+ z8%_$?Il}a%OkU1tWEpc`chy<`x6Qk4Ji-pE_S=+odCGp?diq28IiEwB_m*fh{h8>X OaQ@0WTevoetqcImHbega delta 190 zcmeBTna(mn!y?k*_NDvM3|cXsxBdq|TNeM`apR46Kcf!^rc{Lj7D~HY#R^9H9z_;aivEf>k7k~1rLue%YSU`dufU1 eK3BUnChwc_x@P_N{+YmV{>nRBnBGi?^$Y-enot4& diff --git a/testsuite/test3_gh1.tar b/testsuite/test3_gh1.tar deleted file mode 100644 index f9695619bfce732f55502c4f0ba7053e4493d802..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5120 zcmeH|&uYUk490WrQ|JLkaBL^trH8`)+;-X?z)6%gkl4jhw$i7cS~^B|=-9wn1{Q%} zDgGor@gqs{cOlLpm+E2&PAeg0vbo3xI*bDup9m31e(thakwfMB9xbh&x9tG@7}#{K zG#%{B9=2#|^e_%s134fZ2pZ>mL-5+V&QR+t-?BhkJXMFWB?Q+1``R5+fCuLR_om-< zc=M@!)@|*8o>;p?dPB7QC84c3VQkG^StMOZ36D=NY2_;=SL=Imt5k8fn7g(AV!pm}DSp=6#+!sG}4-Z{GF#CP>;cV}GV{YQ{yyr0mc zmHhJy9Y^Ya%)cli|DhMd`n>F<8&Jm_BW%43MBcqu6x~tCezir-a;}LdPwcnCZ$5h4WY5*&?)YGZ_N_YH>!( delta 197 zcmdnUvX5orEe-ofhufF#OEYN2c;5OS{A^kLd&iA8;{A+19GFsB{=n@PQ<}^7UzhgJ z{HNX1pv}t}$H2k>2I&xrVZy`7E{w|N`?h3->z=#3-Y)Her6JSuyI&2p+naik+$?`&auGbjIJGzI_+Zd5q{ diff --git a/testsuite/test3_gh2.tar b/testsuite/test3_gh2.tar deleted file mode 100644 index f5f0c315c7424ace9cb16b68ce22320a49b83ea8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5120 zcmeHI&1%Ci49>Yvp$8bjvE6i+9tQil?X*3BlPGNe*Eu`!o0EJ94A z`1_Pkk=znxu}z7U)@FxQ?CUBnn-xY|TdKLW3yBxX=y|4MCw9Q!j_iU^1|mMk_g(zl z-U-KAqS=JgOR4>11&-4|ZhB!(zEWI9X`QW3ypbM0`acr<)M1Nq4mAw}dR{)QyAkAa zucw`E9(aw%=(z0|fm)Ec+V69KdkTO% zH*9*m`ow<6?fi&=1-n9iMfCC}qi@9Fd?Ty^lFzh;hsWo<4z%%FOnIBe)?fTv zl^lMt?a=2xKL1sj&i~kj5qznCYNQM3JXMOy?W3pmtNt%i|3@#35J>$~|6fAr#E}$8 K3M2(4robEgKYlF$ diff --git a/testsuite/test3_gh2.tar.lz b/testsuite/test3_gh2.tar.lz index 48f18dd873c048c0608e0c17ba62f4b0f3494065..92c93a7650add4280ed15b666202eba7ebf83cb1 100644 GIT binary patch delta 152 zcmcc5vXNzihDmsfgYLiCb7b>0otuwe_g7wex%z_@Q;_qS7iG=6%>JG4uU!3S#w-PG zUd}iM76vd#-k6}sC~FjHJR_(Vb>++QSy!G^l@^e0iGVd+XX!Pz*j%o&k(J42BY3OV(Ge zn!GOLo&Nv-x4ttlXsJzG4O6T;c^0FQxg6WZ!*R`zzDr!`5y-m2ux7!-4jRC!fOK|`I diff --git a/testsuite/test3_gh3.tar b/testsuite/test3_gh3.tar deleted file mode 100644 index e0d3a9d29fe7889eb1326de7f21bfecb0001c571..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5120 zcmeHJ&1%Ci49>Yvp$8bnvE6i+9tQil?X*3B(aM@QHVze76B6} z{vfDOQl6-pp?TO+Xm5}t$$S{{l{*M;7k9L{{MjIl;S4?!@V$=UYe7jxQx;| z+njhKy?oC9Nbr{qn->dMQ`e#A)zh}=K^_O2mP*&c&K+QjuEGHO9xGr1#-5?3&^d;e zhFZrBdD-QGxA-3I_YEUZ4f0>r9_Ik}6aWvd+qHQ0h5dos_#Oibc8UCo=;cdBUx~x{ zO0o*be5N%#JU-9YVQuv4=1yH3Q{1L&Yl^c~y5kfuqZot6&z}Ezn*XDuP85{>Cm~J5 PWr9fzBnA=#|A~P&@9};u diff --git a/testsuite/test3_gh3.tar.lz b/testsuite/test3_gh3.tar.lz index 89a31a6db4e6f0017183c4f96a4c59c11e0e7f7e..3af21eff21459263d1ea998b89760bcbce3294df 100644 GIT binary patch delta 332 zcmZo=-N-UQ!z8@LLHFP6IkI`0&dtZK`ztTKT>Zg{DaiTEi?ZfjX8+FjSFZjuW0rz8 zFJ~MB3j-J=PflQTmouU5tVFS*d$*W_9l+?N~oiF|CS}M zTWe(*Ed%U>8>41g{#)EN<(%958+t`XcWz9crJ69)qWAN`j7u#=8fOXv6t~N3TnoE$ zGH~(X#~bor?x~TfpQccLYNfEj)~%TzuWmcCgyDSQ2D7EY&o?ygklX!coz`8GbPLC@%A+Vf*2_oYNp%{FkJOdbc7$ka|z6U*P z5j_~F((UZK?c@Bol$6)z65YNQ)6-8sYna@^Xnjb>=b`!p2kXFx?oTE}uE|*|*}(Pd z#wvq(xBtmWJ@H@(e><(?L;sQWQVX_otSx7)-D!F7OwFc+EMbYx7g(l9@TWC42^>B3 zTOe!Vw36Li#mTe8`cAD&;5s#b^)Km!ZRL}?V?X^ae#^dd7h}!e?9<14q^k~3)Lix_ z`tN0m_!V!#|$OBmaMN_HF;gg zJN^IvZ+&NA&{CVW8m3rx@+?Lpb2+w+hvS+beV4e>Ban54VaYEGbjIJGzI`2S*)P| diff --git a/testsuite/test3_gh4.tar b/testsuite/test3_gh4.tar deleted file mode 100644 index 0655c31a0d97ef25859d8f2d08ccb2b737a454c7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5120 zcmeHKO>4t249&T}LO);x$9B_QdKm2Ew$t_loJ45@iB%kREB*CT#>VId9UHjhU=awB z;`hXppGjJxEOsb~(%NjgOMJge%4UVp)|P5%?Mmu}GJ2V**o{s2`;lD`%0R^D_1_*7UdFZ8V2;de%f{;$mL+u zQ{{TtxdUv`H5gz&VgpRTI5M;p2FLKyQSZ1TFRMK87T>GmzGDPxLH?`yV-9dn0r234 zU5{5^*dMr^A2G0CSIDo3UfyK%jU=3JgjGQDh1T%!_?*|FHhO(?r>>1DZ_}|g_q~<+ LO9~_f=B>aRC$fGm diff --git a/testsuite/test3_gh4.tar.lz b/testsuite/test3_gh4.tar.lz index 5b9f6053c3875da39f9436f4e12751193cb87660..5e291e3837a2ca0807eb3148889ae1f59ae649f5 100644 GIT binary patch delta 363 zcmbQuwvlCmhDmsfgYLiCb7b>0otuwe_g7wex%z_@Q;_qS7iG=6%>JG4uU!3S#w-PG zUd}iM76vd#p1g+9*wA~sd4pvs<7V|QFME%sUl$eZdFgKyX+6v8nJ9NK&!_)0jdwPj z6tHuI=}MVg&tzm2b6}VN;WTSlCKPV6UTZb)KR>D%aYfvwX%$s0rtU-Q8O+7E$*6f&h7mT zy&|JKHzv8ya`W?S8XP>#j+B&f2gf}g@WjVdz?^?;@(U(o0PXsn AkN^Mx literal 795 zcmeZ?@(f_)VKA0{!?>&9u#1lbzh>IL;`b67p)#v;Xa3XfY0&27jALM70E2W0#oz<6 zh9U1lX^-xvwG5)%5izH=R&A^`c(MC^{r^`V8yGyFB+4={Le!h1X_NDtQ9U8VcFVNj z-3))$|NsACDg(pojR$AK)El6xm*{Q!9`vk5^kAS$x3llIkMrYFQeK-&bo*LNPe1*v zVR8$j^&uUfhw2j?tOFmqKba7@CTFc=1J|z`s|@Dd{wF8(#DgXL?X->${YTbIE!fVn zww$$gr{%#jHJcW)BsyPUnIgfT*4QL)^we*GtclY~c5@Xc&l2l9wJw3{)cn=Iq!YH4 zPwI~S^t<>i`_5gAHGi{DAMcT_Iy_NxS-+3w z=@H1f!mwt+!{f{HA6xrgTH?9S)ozW+`=-3ES^vF%CNP}8^3E2P4l~i+^Y{P%#oHTr Wnr{F9KmP**gWmeB?_p|nplShEqapGD diff --git a/testsuite/test3_gh5.tar.lz b/testsuite/test3_gh5.tar.lz index b8f4abee65f77b37a26f81a3e51862df7c3dc0e0..41c48f3f06391edd1871641db3209d6965824893 100644 GIT binary patch delta 195 zcmdnT(!erduczlk``v$w9^GKy!oN}3Q91XxP?SI{SJq^wjFXfdnQ+DM1nm+nh5XvKKm`XBskS^Rs)jW^=`j6NKgQd$1M z?G{s-%lBWG_RsvM-P54W%NYl=CY^y{GCQNb`Mxb#;kxH8ueVFPU}?y-{O(u7a#bF- zUE#Z5@18I-uC+G$=@H1f!mwt+ q!{f{HA6xrgTH?9S)ozW+`=-3ES^vF%CNP}8^3E2fH*@k?Mq>bKAZO^wjFXfdnQ+VB`oN){+3}BE9p%^CY z6;HRY4gCM$xW@yY_?QZq{4IvbY>WmGN6fspn>SdNGHzD?^0N16`gKvko|pczm7D+n z5vyl7q|PSAzyvWV25KPG&4vNN*^oR0uK8G^zEzxNDGtoie{FQgMFh`|KzQ$+_02IAt AO8@`> delta 205 zcmcb{(#bM0d9SDEMEl);iyqxz-@?C9*-<(7xKNZpELYZI<-h~WbKSJ+IbZJE8t1=% z?K6X4-XHZmWE8r z?|wBbSLI>b6~6oR?g=yFT5F?E-uM;s|HY-q1t$gU92r<3nlmQvXEZXGW7~K*uKCe- zi7PzZ)S^i^d-%Cq8_qp1wF?rvV*EQ?E_s;}|^H<*4!t`cNR%S8=01Ycv A6#xJL diff --git a/testsuite/test3_gh7.tar.lz b/testsuite/test3_gh7.tar.lz new file mode 100644 index 0000000000000000000000000000000000000000..8f7f6299464f72b81536ca117930deee9f71b543 GIT binary patch literal 636 zcmeZ?@(f_)VKA0{!?>&9u#1lbzh>IL;`b67p)fM_TBbzeq2h*YjcTiUyJGKr=K-UZeg@Oq~r5YeS(8^;6wK(6C&5-td(rw z`gLQK!Mxl5u!O&z*72eL$a<*-+d0;jv)1mkJb0#N(?XU+=L;-TB>2-Bn*@%Y z`Yn((aazf4uHxibVtuF9C2*aZzxtPS!nX2B-Lap37r$lSxr?#pZ}#cqJ6%DlHkqv_8?2Zi%j-r2$;I|bDahWx+(|1aL&z|(a5|Nr?P7#Q@{Z+#C_ Is{>UF0FP((l>h($ literal 0 HcmV?d00001 diff --git a/testsuite/test3_gh8.tar.lz b/testsuite/test3_gh8.tar.lz new file mode 100644 index 0000000000000000000000000000000000000000..ca78e35f8fc4f7bb045f95e0e8bd4ec1d30bd69a GIT binary patch literal 786 zcmeZ?@(f_)VKA0{!?>&9u#1lbzh>IL;`b67p)k_z5&0qaXI$>M+r0&>H zzl-0p@7%>$^EdnS@gC`_!xJ@^_4{aEUbWzV(4+UxI!n;w(z|Nq~> z#J~{II*k{mOdVC3p@i2Gkt>WRv*z*a6WncGIQi%9&&QT6Z=dY%$N$Vb|5cFR{Uc^x zYpzPqtpE2Zu;IZQ+x;+;yiiSIfCcYd^{sPwa*Vf2``_zOTmAnZ-vox=pQo*YX+w(@ zkOS6NE?>a3`Tzg#JqH-7Cail3Q>u+-1F>H6-ok^&Oa4>Jw#VGpU3HfKZS!s$kFdk4 z{WfJ?p0b~}p8imN&gW3(y(Jn=eQvd(} literal 0 HcmV?d00001 diff --git a/testsuite/test3_nn.tar b/testsuite/test3_nn.tar deleted file mode 100644 index c738dee5fd2bb80e5d1e9de4bfc82aa91446e943..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4096 zcmeH|u?~PB3`9HoH3cht4-T$PP#3@6vN~WKAjDjkCNyxDYhKt?sdcec5TVkop52aw z-0VdHFCx+Aj%U9Rw>DPZKldYLlgz3|P48`9yF2h~Q=JC32Wvk_pg{kPe~!UB@ZVwg qYs%oiL(Zd0tm$fQ?K}VW|GNPGn{S55_7DC$hDmsfgYLiCb7b>0otuwe_g7wex%z_@Q;_qS7iG=6%>JG4uU!3S#w-PG zUd}iM76vd#o|rJjDCWNIsNN*^oR0uK8G^zEzxNDGtoie P{FQgMFkLB=wHb{8#qBWj delta 126 zcmcc0bdPC*hDD^q?MwHi8MI0Ec56)D ZH|2HB`tSWSf#Lj>ceXICnUj?mjRB2jH;w=R diff --git a/testsuite/test3_sm1.tar.lz b/testsuite/test3_sm1.tar.lz index 6eb394711fee859b81499374f18295049e055b7e..177c2a0d75c4f21e25d2e630b97929f0ed81cd18 100644 GIT binary patch delta 174 zcmX@ivW;behDmsfgYLiCb7b>0otuwe_g7wex%z_@Q;_qS7iG=6%>JG4uU!3S#w-PG zUd}iM76vd#-k2c3Xne%Xd%JmqWhvuk^)D}bkEUN273_KGZxm@g%j%gZcQDVV|1*tu zHk=f&bA;(jnY^6Q$SCH%?y9r=Z<}}9c!V8R?YAlG@|69&_4J4Gb3TVM?=8`2`ZLi% O;rx|%wg_zkOvV5^<3%U{ delta 187 zcmdnSa+qa;hDD^q?MwHi8MI4*ft)HYku@y;!2M|))j^|3mzU{mjBq=_tFy2 feXe$EOx`!;bhDmsfgYLiCb7b>0otuwe_g7wex%z_@Q;_qS7iG=6%>JG4uU!3S#w-PG zUd}iM76vd#-k30p(J1D=?y9r=Z<}}9c!V8R?YAlG@|69&_4J4Gb3TVM?=8`2`ZLi% Q;rx|%wlHlelMR`S0q>A8F#rGn delta 127 zcmcc3@`PoAhDD^q?MwHi8MI^e)L`9N{>L+6^1nn9v)wo|Jd61(h|>ou6Aoo a-Z$lS&HC^CGlAj!m3OuW;eyXq|e+veRi9$|-7`)$g)JY_#`J^i8loX?@mdrLH${!DaG QIDh4xElgX=0otuwe_g7wex%z_@Q;_qS7iG=6%>JG4uU!3S#w-PG zUd}iM76vd#-k1==WE68>chy<`x6Qk4Ji-pE_S=+odCGp?diq28IiEwB_m*fh{h8>X QaQ@0WTbQ<#$*-A=0i-lB9smFU delta 127 zcmbQrHjizBhDD^q?MwHi8MI@ zs-M~*H@B+YU;2jmGNnG!LrU@+nmB@8a9xC;!Zq@lkA%mi#fL><8B) ziF+yM3sovQ_dc9z|GQ?gf_?M2K(^6m=H+`IP6YU3^XOOJ0^x4Hb} zC;sz~Qr)f3g$WijJT;h|{np}a`pZR8g2loYo);(&8PPwvPhO=m` zb(Q*B-$km88w*;eO?+&W;jmDs#=QRj!}itpZoazhFw{e8#f8)OLaOR)?u`Qr!|Vn6 z9!klYEd8}^PhF42jl0&5Ildo!8dIG<;nufBV%63$`#G;B-d1Va7N)$%QDY8=mBaVh zU9SQyr01#B|E@gts_IU353Z2P|NH;{;_VGQO}GF5pZ|e@L2v!m_Y5qMbf*KM7yyWK B$w2@B literal 0 HcmV?d00001 diff --git a/testsuite/tlz_in_tar1.tar b/testsuite/tlz_in_tar1.tar index f2dfd6c4f92f12acbabf8335b1577373a64863a3..a29026d06a108509401d4a39eaae7d2d729c4d85 100644 GIT binary patch delta 212 zcmZn=Xb_mtDq&>KU}$7)Vs2t&ZenV}U|?WqWMFK>U@+N`F=aC&qdKEWc#DJXzu9wS z^E92Ck6-s!UV6FugB4Sd^O+ZA&AZJ0o$s$){b$B31#Mo=I0hC5Fi4)9z+`BA#LRoU zd4pvs<7V|QFME%sUl$eZdFgKyX+6v8nJ9NK&!_)0jdwPj6tHuI=}Vb>g2~7z=DzN# zv;1$HciVV`9ainPDeLl-{k-+`hw^hihcfRi(P;WJ(Lv$-m3OvqZJQ05&$CQa-~|9p C8cj3+ delta 222 zcmZn=Xb_mtDq&*6U}$7)Vs2t&ZenV}U|?WqWMFK_U@+N`F=aC&qdKESq{Hn?_oW%M zVmxpC4}P{R{=MVI8}WWd9}Y~ZEPvp3iz&_J`>#v;Xa3XfY0&27jALM70E6_&F-(T$ z`?h3->z=#3-Y)Her6JSuyI&2 z$p@H>%;nfN9*%2%^j+dgk3iNHhBXTw9$%LK*xL8f63>0Ec56)DH|2HB`tSWSf#Lj> PceXISnVVIZ&$9pkQwUgU diff --git a/testsuite/tlz_in_tar2.tar b/testsuite/tlz_in_tar2.tar index be860c6132e0345cd4c79c2a6c479477d1e5a7d6..f0773584918f46719d219cad5e9af0638c3f5f49 100644 GIT binary patch delta 213 zcmZpWXpq>@$|7N8&R}R{Y+`O=WNu<=!eC%vXk=h)#9%PlkTGR5Bda>2NqCEc?!Vb{ zWb-tgn~z`jS6+I#`hyiykn@=rWzDWRpECp>|&Nv1Z1~5pToWN#ie8kLq zyLp3UDdT4KFE4wKre7Bo?0M;L6lp!n>X|5aFwdv|GmUpPoD{Hggy~C}e1gr$DCWNI zsNN*^oR0uK8G^zEzxNDGtoie{FQgMaBZ6n+0U~~RNw^w De|SxJ delta 223 zcmZpWXpq>@$|7N6!eD4*Y+`O=WNu<=!eC%vXk=h)$Y3zpkTGR5Bda>2MWn;+OZTN2 zv|>DO{SSV&EdIUY#vAc|MjsALsVslsc8e*^<@>Ko`)B^s?rG5G<&0xsVE}{l$uVq( z=KHo}h3lTXyxuPDf~6tT^1ELR%T;;Uc7^YLy?esUxYpX}lQ(|F{C{yNa=}RfJ4cw_ zjL8SsjLhZOHXe>^e)L`9N{>L+6^1nn9v)wo|Jd61(h|>ou6Aoo-Z$lS&HC^CGlAj! Qm3Ou70Eenrt^fc4