Merging upstream version 0.24.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
b3a4316df0
commit
d842f57fc5
33 changed files with 905 additions and 882 deletions
23
ChangeLog
23
ChangeLog
|
@ -1,3 +1,15 @@
|
|||
2023-09-20 Antonio Diaz Diaz <antonio@gnu.org>
|
||||
|
||||
* Version 0.24 released.
|
||||
* decode.cc (decode), common_decode.cc (check_skip_filename):
|
||||
Make option '-C' position-dependent also for diff and extract.
|
||||
(Reported by Devon Sean McCullough).
|
||||
* create.cc (encode): Deduct '--uncompressed' from archive name ext.
|
||||
* compress.cc (show_atpos_error): New function showing errno msg.
|
||||
(compress_archive): Exit with error status 2 if archive is empty.
|
||||
* Limit the size of a header set (extended+ustar) to INT_MAX.
|
||||
* check.sh: Fix '--diff' test on OS/2 again. (Reported by Elbert Pol).
|
||||
|
||||
2022-09-23 Antonio Diaz Diaz <antonio@gnu.org>
|
||||
|
||||
* Version 0.23 released.
|
||||
|
@ -40,9 +52,8 @@
|
|||
decode_lz.cc (dworker): Likewise. (Reported by Florian Schmaus).
|
||||
* New options '-z, --compress' and '-o, --output'.
|
||||
* New option '--warn-newer'.
|
||||
* tarlz.texi (Portable character set): Link to moe section on Unicode.
|
||||
(Invoking tarlz): Document concatenation to standard output.
|
||||
* check.sh: Fix the '--diff' test on OS/2.
|
||||
* tarlz.texi (Invoking tarlz): Document concatenation to stdout.
|
||||
* check.sh: Fix the '--diff' test on OS/2. (Reported by Elbert Pol).
|
||||
|
||||
2021-01-08 Antonio Diaz Diaz <antonio@gnu.org>
|
||||
|
||||
|
@ -154,7 +165,7 @@
|
|||
* New option '--keep-damaged'.
|
||||
* New option '--no-solid'.
|
||||
* create.cc (archive_write): Minimize dictionary size.
|
||||
* create.cc: Detect and skip archive in '-A', '-c', and '-r'.
|
||||
Detect and skip archive in '-A', '-c', and '-r'.
|
||||
* main.cc (show_version): Show the version of lzlib being used.
|
||||
|
||||
2018-10-19 Antonio Diaz Diaz <antonio@gnu.org>
|
||||
|
@ -162,7 +173,7 @@
|
|||
* Version 0.6 released.
|
||||
* New option '-A, --concatenate'.
|
||||
* Option '--ignore-crc' replaced with '--missing-crc'.
|
||||
* create.cc (add_member): Verify that uid, gid, mtime, devmajor,
|
||||
* create.cc (add_member): Check that uid, gid, mtime, devmajor,
|
||||
and devminor are in ustar range.
|
||||
* configure: Accept appending to CXXFLAGS; 'CXXFLAGS+=OPTIONS'.
|
||||
* Makefile.in: Use tarlz in target 'dist'.
|
||||
|
@ -208,7 +219,7 @@
|
|||
* Version 0.1 released.
|
||||
|
||||
|
||||
Copyright (C) 2013-2022 Antonio Diaz Diaz.
|
||||
Copyright (C) 2013-2023 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
|
||||
|
|
13
INSTALL
13
INSTALL
|
@ -23,8 +23,8 @@ Procedure
|
|||
or
|
||||
lzip -cd tarlz[version].tar.lz | tar -xf -
|
||||
|
||||
This creates the directory ./tarlz[version] containing the source from
|
||||
the main archive.
|
||||
This creates the directory ./tarlz[version] containing the source code
|
||||
extracted from the archive.
|
||||
|
||||
2. Change to tarlz directory and run configure.
|
||||
(Try 'configure --help' for usage instructions).
|
||||
|
@ -46,7 +46,8 @@ the main archive.
|
|||
4. Optionally, type 'make check' to run the tests that come with tarlz.
|
||||
|
||||
5. Type 'make install' to install the program and any data files and
|
||||
documentation.
|
||||
documentation. You need root privileges to install into a prefix owned
|
||||
by root.
|
||||
|
||||
Or type 'make install-compress', which additionally compresses the
|
||||
info manual and the man page after installation.
|
||||
|
@ -66,15 +67,15 @@ object files and executables to go and run the 'configure' script.
|
|||
'configure' automatically checks for the source code in '.', in '..', and
|
||||
in the directory that 'configure' is in.
|
||||
|
||||
'configure' recognizes the option '--srcdir=DIR' to control where to
|
||||
look for the sources. Usually 'configure' can determine that directory
|
||||
'configure' recognizes the option '--srcdir=DIR' to control where to look
|
||||
for the source code. Usually 'configure' can determine that directory
|
||||
automatically.
|
||||
|
||||
After running 'configure', you can run 'make' and 'make install' as
|
||||
explained above.
|
||||
|
||||
|
||||
Copyright (C) 2013-2022 Antonio Diaz Diaz.
|
||||
Copyright (C) 2013-2023 Antonio Diaz Diaz.
|
||||
|
||||
This file is free documentation: you have unlimited permission to copy,
|
||||
distribute, and modify it.
|
||||
|
|
16
Makefile.in
16
Makefile.in
|
@ -8,8 +8,8 @@ SHELL = /bin/sh
|
|||
CAN_RUN_INSTALLINFO = $(SHELL) -c "install-info --version" > /dev/null 2>&1
|
||||
|
||||
objs = arg_parser.o lzip_index.o archive_reader.o common.o common_decode.o \
|
||||
compress.o create.o create_lz.o decode.o decode_lz.o delete.o \
|
||||
delete_lz.o exclude.o extended.o main.o
|
||||
common_mutex.o compress.o create.o create_lz.o decode.o decode_lz.o \
|
||||
delete.o delete_lz.o exclude.o extended.o main.o
|
||||
|
||||
|
||||
.PHONY : all install install-bin install-info install-man \
|
||||
|
@ -32,13 +32,15 @@ main.o : main.cc
|
|||
$(objs) : Makefile
|
||||
arg_parser.o : arg_parser.h
|
||||
archive_reader.o : tarlz.h lzip_index.h archive_reader.h
|
||||
common.o : tarlz.h arg_parser.h
|
||||
common_decode.o : tarlz.h arg_parser.h
|
||||
common.o : tarlz.h
|
||||
common_decode.o : tarlz.h arg_parser.h decode.h
|
||||
common_mutex.o : common_mutex.h
|
||||
compress.o : tarlz.h arg_parser.h
|
||||
create.o : tarlz.h arg_parser.h create.h
|
||||
create_lz.o : tarlz.h arg_parser.h create.h
|
||||
create_lz.o : tarlz.h arg_parser.h common_mutex.h create.h
|
||||
decode.o : tarlz.h arg_parser.h lzip_index.h archive_reader.h decode.h
|
||||
decode_lz.o : tarlz.h arg_parser.h lzip_index.h archive_reader.h decode.h
|
||||
decode_lz.o : tarlz.h arg_parser.h lzip_index.h archive_reader.h \
|
||||
common_mutex.h decode.h
|
||||
delete.o : tarlz.h arg_parser.h lzip_index.h archive_reader.h
|
||||
delete_lz.o : tarlz.h arg_parser.h lzip_index.h archive_reader.h
|
||||
exclude.o : tarlz.h
|
||||
|
@ -52,7 +54,7 @@ doc : info man
|
|||
info : $(VPATH)/doc/$(pkgname).info
|
||||
|
||||
$(VPATH)/doc/$(pkgname).info : $(VPATH)/doc/$(pkgname).texi
|
||||
cd $(VPATH)/doc && makeinfo $(pkgname).texi
|
||||
cd $(VPATH)/doc && $(MAKEINFO) $(pkgname).texi
|
||||
|
||||
man : $(VPATH)/doc/$(progname).1
|
||||
|
||||
|
|
60
NEWS
60
NEWS
|
@ -1,53 +1,17 @@
|
|||
Changes in version 0.23:
|
||||
Changes in version 0.24:
|
||||
|
||||
Tarlz now can create and decode the extended records 'atime' and 'mtime',
|
||||
allowing times beyond the ustar range (before 1970-01-01 00:00:00 UTC or
|
||||
after 2242-03-16 12:56:31 UTC).
|
||||
The option '-C, --directory' is now position-dependent also for diff and
|
||||
extract. (Reported by Devon Sean McCullough).
|
||||
|
||||
Tarlz now can create and decode the extended records 'uid' and 'gid',
|
||||
allowing user and group IDs beyond the ustar limit of 2_097_151.
|
||||
Option '--uncompressed' can now be omitted if it can be deduced from the
|
||||
archive name. (An uncompressed archive name lacks a '.lz' or '.tlz' extension).
|
||||
|
||||
The new option '--ignore-overflow', which makes '-d, --diff' ignore
|
||||
differences in mtime caused by overflow on 32-bit systems, has been added.
|
||||
The diagnostic shown when a file being archived or an archive being
|
||||
compressed fails to read, now shows the cause of the failure; end-of-file or
|
||||
read error.
|
||||
|
||||
Tarlz now refuses to read archive data from a terminal or write archive data
|
||||
to a terminal. (Reported by DustDFG).
|
||||
'-z, --compress' now exits with error status 2 if any input archive is an
|
||||
empty file.
|
||||
|
||||
In the date format of option '--mtime' the time of day 'HH:MM:SS' is now
|
||||
optional and defaults to '00:00:00'. Both space and 'T' are now accepted as
|
||||
separator between date and time.
|
||||
|
||||
Diagnostics caused by invalid arguments to command line options now show the
|
||||
argument and the name of the option.
|
||||
|
||||
Tarlz now diagnoses separately the failure to create an intermediate
|
||||
directory during extraction.
|
||||
|
||||
Failure to extract a member due to environmental problems is no longer fatal
|
||||
in serial extraction. (It was already non-fatal in parallel extraction).
|
||||
|
||||
The diagnostics emitted by the parallel decoder should now be identical to
|
||||
the corresponding diagnostics of the serial decoder.
|
||||
|
||||
Column alignment has been improved in listings by printing "user/group size"
|
||||
in a field of minimum width 19 with at least 8 characters for size.
|
||||
|
||||
The diagnostic shown when the filesystem reports a wrong st_size for a
|
||||
symbolic link has been improved. (Reported by Jason Lenz).
|
||||
|
||||
The diagnostic "File is the archive" has been changed to "Archive can't
|
||||
contain itself" following a similar change made by Paul Eggert to GNU tar.
|
||||
|
||||
The warning "Removing leading '/' from member names." is now not shown when
|
||||
compressing nor if the member causing it is excluded.
|
||||
|
||||
The texinfo category of the manual has been changed from 'Data Compression'
|
||||
to 'Archiving' to match that of GNU tar.
|
||||
|
||||
'end-of-archive' (EOA) is now used consistently to refer to the blocks of
|
||||
binary zeros used to mark the end of the archive.
|
||||
|
||||
Operations are now listed before options in the --help output and in the
|
||||
manual.
|
||||
|
||||
Many small improvements have been made to the code and documentation.
|
||||
A failure in the '--diff' test of the testsuite on OS/2 has been fixed.
|
||||
(Reported by Elbert Pol).
|
||||
|
|
6
README
6
README
|
@ -67,8 +67,8 @@ place.
|
|||
Because of the above, tarlz protects the extended records with a Cyclic
|
||||
Redundancy Check (CRC) in a way compatible with standard tar tools.
|
||||
|
||||
Tarlz does not understand other tar formats like gnu, oldgnu, star or v7.
|
||||
The command 'tarlz -tf archive.tar.lz > /dev/null' can be used to verify
|
||||
Tarlz does not understand other tar formats like gnu, oldgnu, star, or v7.
|
||||
The command 'tarlz -t -f archive.tar.lz > /dev/null' can be used to check
|
||||
that the format of the archive is compatible with tarlz.
|
||||
|
||||
The diagram below shows the correspondence between each tar member (formed
|
||||
|
@ -87,7 +87,7 @@ tar.lz
|
|||
+===============+=================================================+========+
|
||||
|
||||
|
||||
Copyright (C) 2013-2022 Antonio Diaz Diaz.
|
||||
Copyright (C) 2013-2023 Antonio Diaz Diaz.
|
||||
|
||||
This file is free documentation: you have unlimited permission to copy,
|
||||
distribute, and modify it.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* Tarlz - Archiver with multimember lzip compression
|
||||
Copyright (C) 2013-2022 Antonio Diaz Diaz.
|
||||
Copyright (C) 2013-2023 Antonio Diaz Diaz.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -90,7 +90,7 @@ int Archive_reader_base::parse_records( Extended & extended,
|
|||
const long long edsize = parse_octal( header + size_o, size_l );
|
||||
const long long bufsize = round_up( edsize );
|
||||
if( edsize <= 0 ) return err( 2, misrec_msg ); // no extended records
|
||||
if( edsize >= 1LL << 33 || bufsize >= INT_MAX )
|
||||
if( edsize >= 1LL << 33 || bufsize > max_edata_size )
|
||||
return err( -2, longrec_msg ); // records too long
|
||||
if( !rbuf.resize( bufsize ) ) return err( -1, mem_msg );
|
||||
e_msg_ = ""; e_code_ = 0;
|
||||
|
@ -116,10 +116,10 @@ int Archive_reader::read( uint8_t * const buf, const int size )
|
|||
const int rd = readblock( ad.infd, buf, size );
|
||||
if( rd != size && errno ) return err( -1, rdaerr_msg, errno, rd );
|
||||
const Lzip_header & header = (*(const Lzip_header *)buf);
|
||||
const bool islz = ( rd >= min_member_size && header.verify_magic() &&
|
||||
header.verify_version() &&
|
||||
const bool islz = ( rd >= min_member_size && header.check_magic() &&
|
||||
header.check_version() &&
|
||||
isvalid_ds( header.dictionary_size() ) );
|
||||
const bool istar = ( rd == size && verify_ustar_chksum( buf ) );
|
||||
const bool istar = ( rd == size && check_ustar_chksum( buf ) );
|
||||
const bool iseoa =
|
||||
( !islz && !istar && rd == size && block_is_zero( buf, size ) );
|
||||
bool maybe_lz = islz; // maybe corrupt tar.lz
|
||||
|
@ -139,7 +139,7 @@ int Archive_reader::read( uint8_t * const buf, const int size )
|
|||
{ LZ_decompress_close( decoder ); decoder = 0; return err( -1, mem_msg ); }
|
||||
xLZ_decompress_write( decoder, buf, rd );
|
||||
const int ret = read( buf, size ); if( ret != 0 ) return ret;
|
||||
if( verify_ustar_chksum( buf ) || block_is_zero( buf, size ) ) return 0;
|
||||
if( check_ustar_chksum( buf ) || block_is_zero( buf, size ) ) return 0;
|
||||
return err( 2, islz ? posix_lz_msg : "" );
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* Tarlz - Archiver with multimember lzip compression
|
||||
Copyright (C) 2013-2022 Antonio Diaz Diaz.
|
||||
Copyright (C) 2013-2023 Antonio Diaz Diaz.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* Arg_parser - POSIX/GNU command line argument parser. (C++ version)
|
||||
Copyright (C) 2006-2022 Antonio Diaz Diaz.
|
||||
Copyright (C) 2006-2023 Antonio Diaz Diaz.
|
||||
|
||||
This library is free software. Redistribution and use in source and
|
||||
binary forms, with or without modification, are permitted provided
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* Arg_parser - POSIX/GNU command line argument parser. (C++ version)
|
||||
Copyright (C) 2006-2022 Antonio Diaz Diaz.
|
||||
Copyright (C) 2006-2023 Antonio Diaz Diaz.
|
||||
|
||||
This library is free software. Redistribution and use in source and
|
||||
binary forms, with or without modification, are permitted provided
|
||||
|
|
81
common.cc
81
common.cc
|
@ -1,5 +1,5 @@
|
|||
/* Tarlz - Archiver with multimember lzip compression
|
||||
Copyright (C) 2013-2022 Antonio Diaz Diaz.
|
||||
Copyright (C) 2013-2023 Antonio Diaz Diaz.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -19,82 +19,9 @@
|
|||
|
||||
#include <cctype>
|
||||
#include <cerrno>
|
||||
#include <cstdlib>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "tarlz.h"
|
||||
#include "arg_parser.h"
|
||||
|
||||
|
||||
void xinit_mutex( pthread_mutex_t * const mutex )
|
||||
{
|
||||
const int errcode = pthread_mutex_init( mutex, 0 );
|
||||
if( errcode )
|
||||
{ show_error( "pthread_mutex_init", errcode ); exit_fail_mt(); }
|
||||
}
|
||||
|
||||
void xinit_cond( pthread_cond_t * const cond )
|
||||
{
|
||||
const int errcode = pthread_cond_init( cond, 0 );
|
||||
if( errcode )
|
||||
{ show_error( "pthread_cond_init", errcode ); exit_fail_mt(); }
|
||||
}
|
||||
|
||||
|
||||
void xdestroy_mutex( pthread_mutex_t * const mutex )
|
||||
{
|
||||
const int errcode = pthread_mutex_destroy( mutex );
|
||||
if( errcode )
|
||||
{ show_error( "pthread_mutex_destroy", errcode ); exit_fail_mt(); }
|
||||
}
|
||||
|
||||
void xdestroy_cond( pthread_cond_t * const cond )
|
||||
{
|
||||
const int errcode = pthread_cond_destroy( cond );
|
||||
if( errcode )
|
||||
{ show_error( "pthread_cond_destroy", errcode ); exit_fail_mt(); }
|
||||
}
|
||||
|
||||
|
||||
void xlock( pthread_mutex_t * const mutex )
|
||||
{
|
||||
const int errcode = pthread_mutex_lock( mutex );
|
||||
if( errcode )
|
||||
{ show_error( "pthread_mutex_lock", errcode ); exit_fail_mt(); }
|
||||
}
|
||||
|
||||
|
||||
void xunlock( pthread_mutex_t * const mutex )
|
||||
{
|
||||
const int errcode = pthread_mutex_unlock( mutex );
|
||||
if( errcode )
|
||||
{ show_error( "pthread_mutex_unlock", errcode ); exit_fail_mt(); }
|
||||
}
|
||||
|
||||
|
||||
void xwait( pthread_cond_t * const cond, pthread_mutex_t * const mutex )
|
||||
{
|
||||
const int errcode = pthread_cond_wait( cond, mutex );
|
||||
if( errcode )
|
||||
{ show_error( "pthread_cond_wait", errcode ); exit_fail_mt(); }
|
||||
}
|
||||
|
||||
|
||||
void xsignal( pthread_cond_t * const cond )
|
||||
{
|
||||
const int errcode = pthread_cond_signal( cond );
|
||||
if( errcode )
|
||||
{ show_error( "pthread_cond_signal", errcode ); exit_fail_mt(); }
|
||||
}
|
||||
|
||||
|
||||
void xbroadcast( pthread_cond_t * const cond )
|
||||
{
|
||||
const int errcode = pthread_cond_broadcast( cond );
|
||||
if( errcode )
|
||||
{ show_error( "pthread_cond_broadcast", errcode ); exit_fail_mt(); }
|
||||
}
|
||||
|
||||
|
||||
unsigned long long parse_octal( const uint8_t * const ptr, const int size )
|
||||
|
@ -143,9 +70,3 @@ int writeblock( const int fd, const uint8_t * const buf, const int size )
|
|||
}
|
||||
return sz;
|
||||
}
|
||||
|
||||
|
||||
bool nonempty_arg( const Arg_parser & parser, const int i )
|
||||
{
|
||||
return ( parser.code( i ) == 0 && !parser.argument( i ).empty() );
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* Tarlz - Archiver with multimember lzip compression
|
||||
Copyright (C) 2013-2022 Antonio Diaz Diaz.
|
||||
Copyright (C) 2013-2023 Antonio Diaz Diaz.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -19,12 +19,13 @@
|
|||
|
||||
#include <cerrno>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <ctime>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "tarlz.h"
|
||||
#include "arg_parser.h"
|
||||
#include "decode.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
@ -125,7 +126,7 @@ bool format_member_name( const Extended & extended, const Tar_header header,
|
|||
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 the UTC epoch
|
||||
{ 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 );
|
||||
|
@ -184,36 +185,48 @@ bool show_member_name( const Extended & extended, const Tar_header header,
|
|||
|
||||
bool check_skip_filename( const Cl_options & cl_opts,
|
||||
std::vector< char > & name_pending,
|
||||
const char * const filename )
|
||||
const char * const filename, const int chdir_fd )
|
||||
{
|
||||
static int c_idx = -1; // parser index of last -C executed
|
||||
if( Exclude::excluded( filename ) ) return true; // skip excluded files
|
||||
bool skip = cl_opts.num_files > 0; // if no files specified, skip nothing
|
||||
if( skip ) // else skip all but the files (or trees) specified
|
||||
for( int i = 0; i < cl_opts.parser.arguments(); ++i )
|
||||
if( nonempty_arg( cl_opts.parser, i ) )
|
||||
if( cl_opts.num_files <= 0 ) return false; // no files specified, no skip
|
||||
bool skip = true; // else skip all but the files (or trees) specified
|
||||
bool chdir_pending = false;
|
||||
|
||||
for( int i = 0; i < cl_opts.parser.arguments(); ++i )
|
||||
{
|
||||
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;
|
||||
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 );
|
||||
skip = false; name_pending[i] = false;
|
||||
if( chdir_pending && chdir_fd >= 0 )
|
||||
{
|
||||
std::string removed_prefix;
|
||||
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 );
|
||||
skip = false; name_pending[i] = false; break; }
|
||||
if( c_idx > i )
|
||||
{ if( fchdir( chdir_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 )
|
||||
{
|
||||
if( cl_opts.parser.code( j ) != 'C' ) continue;
|
||||
const char * const dir = cl_opts.parser.argument( j ).c_str();
|
||||
if( chdir( dir ) != 0 )
|
||||
{ show_file_error( dir, chdir_msg, errno ); throw Chdir_error(); }
|
||||
c_idx = j;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return skip;
|
||||
}
|
||||
|
||||
|
||||
mode_t get_umask()
|
||||
{
|
||||
static mode_t mask = 0; // read once, cache the result
|
||||
static bool first_call = true;
|
||||
if( first_call ) { first_call = false; mask = umask( 0 ); umask( mask );
|
||||
mask &= S_IRWXU | S_IRWXG | S_IRWXO; }
|
||||
return mask;
|
||||
}
|
||||
|
||||
|
||||
bool make_path( const std::string & name )
|
||||
{
|
||||
const mode_t mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
|
||||
|
|
160
common_mutex.cc
Normal file
160
common_mutex.cc
Normal file
|
@ -0,0 +1,160 @@
|
|||
/* Tarlz - Archiver with multimember lzip compression
|
||||
Copyright (C) 2013-2023 Antonio Diaz Diaz.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#include "tarlz.h"
|
||||
#include "common_mutex.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
int error_status = 0;
|
||||
|
||||
} // end namespace
|
||||
|
||||
|
||||
void xinit_mutex( pthread_mutex_t * const mutex )
|
||||
{
|
||||
const int errcode = pthread_mutex_init( mutex, 0 );
|
||||
if( errcode )
|
||||
{ show_error( "pthread_mutex_init", errcode ); exit_fail_mt(); }
|
||||
}
|
||||
|
||||
void xinit_cond( pthread_cond_t * const cond )
|
||||
{
|
||||
const int errcode = pthread_cond_init( cond, 0 );
|
||||
if( errcode )
|
||||
{ show_error( "pthread_cond_init", errcode ); exit_fail_mt(); }
|
||||
}
|
||||
|
||||
|
||||
void xdestroy_mutex( pthread_mutex_t * const mutex )
|
||||
{
|
||||
const int errcode = pthread_mutex_destroy( mutex );
|
||||
if( errcode )
|
||||
{ show_error( "pthread_mutex_destroy", errcode ); exit_fail_mt(); }
|
||||
}
|
||||
|
||||
void xdestroy_cond( pthread_cond_t * const cond )
|
||||
{
|
||||
const int errcode = pthread_cond_destroy( cond );
|
||||
if( errcode )
|
||||
{ show_error( "pthread_cond_destroy", errcode ); exit_fail_mt(); }
|
||||
}
|
||||
|
||||
|
||||
void xlock( pthread_mutex_t * const mutex )
|
||||
{
|
||||
const int errcode = pthread_mutex_lock( mutex );
|
||||
if( errcode )
|
||||
{ show_error( "pthread_mutex_lock", errcode ); exit_fail_mt(); }
|
||||
}
|
||||
|
||||
|
||||
void xunlock( pthread_mutex_t * const mutex )
|
||||
{
|
||||
const int errcode = pthread_mutex_unlock( mutex );
|
||||
if( errcode )
|
||||
{ show_error( "pthread_mutex_unlock", errcode ); exit_fail_mt(); }
|
||||
}
|
||||
|
||||
|
||||
void xwait( pthread_cond_t * const cond, pthread_mutex_t * const mutex )
|
||||
{
|
||||
const int errcode = pthread_cond_wait( cond, mutex );
|
||||
if( errcode )
|
||||
{ show_error( "pthread_cond_wait", errcode ); exit_fail_mt(); }
|
||||
}
|
||||
|
||||
|
||||
void xsignal( pthread_cond_t * const cond )
|
||||
{
|
||||
const int errcode = pthread_cond_signal( cond );
|
||||
if( errcode )
|
||||
{ show_error( "pthread_cond_signal", errcode ); exit_fail_mt(); }
|
||||
}
|
||||
|
||||
|
||||
void xbroadcast( pthread_cond_t * const cond )
|
||||
{
|
||||
const int errcode = pthread_cond_broadcast( cond );
|
||||
if( errcode )
|
||||
{ show_error( "pthread_cond_broadcast", errcode ); exit_fail_mt(); }
|
||||
}
|
||||
|
||||
|
||||
/* This can be called from any thread, main thread or sub-threads alike,
|
||||
since they all call common helper functions that call exit_fail_mt()
|
||||
in case of an error.
|
||||
*/
|
||||
void exit_fail_mt( const int retval )
|
||||
{
|
||||
// calling 'exit' more than once results in undefined behavior
|
||||
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
pthread_mutex_lock( &mutex ); // ignore errors to avoid loop
|
||||
std::exit( retval );
|
||||
}
|
||||
|
||||
|
||||
/* If msgp is null, print the message, else return the message in *msgp.
|
||||
If prefix is already in the list, print nothing or return empty *msgp.
|
||||
Return true if a message is printed or returned in *msgp. */
|
||||
bool print_removed_prefix( const std::string & prefix,
|
||||
std::string * const msgp )
|
||||
{
|
||||
// prevent two threads from modifying the list of prefixes at the same time
|
||||
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static std::vector< std::string > prefixes; // list of prefixes
|
||||
|
||||
if( verbosity < 0 || prefix.empty() )
|
||||
{ if( msgp ) msgp->clear(); return false; }
|
||||
xlock( &mutex );
|
||||
for( unsigned i = 0; i < prefixes.size(); ++i )
|
||||
if( prefixes[i] == prefix )
|
||||
{ xunlock( &mutex ); if( msgp ) msgp->clear(); return false; }
|
||||
prefixes.push_back( prefix );
|
||||
std::string msg( "Removing leading '" ); msg += prefix;
|
||||
msg += "' from member names.";
|
||||
if( msgp ) *msgp = msg; else show_error( msg.c_str() );
|
||||
xunlock( &mutex ); // put here to prevent mixing calls to show_error
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void set_error_status( const int retval )
|
||||
{
|
||||
// prevent two threads from modifying the error_status at the same time
|
||||
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
xlock( &mutex );
|
||||
if( error_status < retval ) error_status = retval;
|
||||
xunlock( &mutex );
|
||||
}
|
||||
|
||||
|
||||
int final_exit_status( int retval, const bool show_msg )
|
||||
{
|
||||
if( retval == 0 && error_status )
|
||||
{ if( show_msg )
|
||||
show_error( "Exiting with failure status due to previous errors." );
|
||||
retval = error_status; }
|
||||
return retval;
|
||||
}
|
30
common_mutex.h
Normal file
30
common_mutex.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
/* Tarlz - Archiver with multimember lzip compression
|
||||
Copyright (C) 2013-2023 Antonio Diaz Diaz.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
void xinit_mutex( pthread_mutex_t * const mutex );
|
||||
void xinit_cond( pthread_cond_t * const cond );
|
||||
void xdestroy_mutex( pthread_mutex_t * const mutex );
|
||||
void xdestroy_cond( pthread_cond_t * const cond );
|
||||
void xlock( pthread_mutex_t * const mutex );
|
||||
void xunlock( pthread_mutex_t * const mutex );
|
||||
void xwait( pthread_cond_t * const cond, pthread_mutex_t * const mutex );
|
||||
void xsignal( pthread_cond_t * const cond );
|
||||
void xbroadcast( pthread_cond_t * const cond );
|
||||
|
||||
// non-pthread_* declarations are in tarlz.h
|
||||
|
||||
const char * const conofin_msg = "courier not finished.";
|
48
compress.cc
48
compress.cc
|
@ -1,5 +1,5 @@
|
|||
/* Tarlz - Archiver with multimember lzip compression
|
||||
Copyright (C) 2013-2022 Antonio Diaz Diaz.
|
||||
Copyright (C) 2013-2023 Antonio Diaz Diaz.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -20,7 +20,6 @@
|
|||
#include <cerrno>
|
||||
#include <csignal>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <stdint.h> // for lzlib.h
|
||||
#include <unistd.h>
|
||||
#include <utime.h>
|
||||
|
@ -131,7 +130,7 @@ void close_and_set_permissions( const struct stat * const in_statsp )
|
|||
}
|
||||
|
||||
|
||||
bool archive_write( const uint8_t * const buf, const long long size,
|
||||
bool archive_write( const uint8_t * const buf, const int size,
|
||||
LZ_Encoder * const encoder )
|
||||
{
|
||||
static bool flushed = true; // avoid flushing empty lzip members
|
||||
|
@ -140,13 +139,12 @@ bool archive_write( const uint8_t * const buf, const long long size,
|
|||
flushed = ( size <= 0 );
|
||||
enum { obuf_size = 65536 };
|
||||
uint8_t obuf[obuf_size];
|
||||
long long sz = 0;
|
||||
int sz = 0;
|
||||
if( flushed ) LZ_compress_finish( encoder ); // flush encoder
|
||||
while( sz < size || flushed )
|
||||
{
|
||||
if( sz < size )
|
||||
{ const int wr = LZ_compress_write( encoder, buf + sz,
|
||||
std::min( size - sz, (long long)max_dictionary_size ) );
|
||||
{ const int wr = LZ_compress_write( encoder, buf + sz, size - sz );
|
||||
if( wr < 0 ) internal_error( "library error (LZ_compress_write)." );
|
||||
sz += wr; }
|
||||
if( sz >= size && !flushed ) break; // minimize dictionary size
|
||||
|
@ -216,26 +214,34 @@ int compress_archive( const Cl_options & cl_opts,
|
|||
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
|
||||
{
|
||||
int total_header_size = header_size; // size of header(s) read
|
||||
int total_header_size = header_size; // e_header + edata + u_header
|
||||
const int rd = readblock( infd, rbuf.u8(), header_size );
|
||||
if( rd == 0 && errno == 0 ) break; // missing EOA blocks
|
||||
if( rd == 0 && errno == 0 ) // missing EOA blocks
|
||||
{ if( !first_header ) break;
|
||||
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; }
|
||||
first_header = false;
|
||||
|
||||
if( to_file && outfd < 0 ) // open outfd after verifying infd
|
||||
const bool is_header = check_ustar_chksum( rbuf.u8() );
|
||||
const bool is_zero = !is_header && block_is_zero( rbuf.u8(), header_size );
|
||||
if( to_file && outfd < 0 && ( is_header || is_zero ) )
|
||||
{
|
||||
// open outfd after checking infd
|
||||
outfd = open_outstream( output_filename, true, 0, false );
|
||||
// check tty only once and don't try to delete a tty
|
||||
if( outfd < 0 || !check_tty_out() ) { close( infd ); return 1; }
|
||||
delete_output_on_interrupt = true;
|
||||
}
|
||||
|
||||
if( !verify_ustar_chksum( rbuf.u8() ) ) // maybe EOA block
|
||||
if( !is_header ) // maybe EOA block
|
||||
{
|
||||
if( block_is_zero( rbuf.u8(), header_size ) ) // first EOA block
|
||||
if( is_zero ) // first EOA block
|
||||
{ tail_compress( cl_opts, infd, rbuf.u8(), encoder ); break; }
|
||||
show_file_error( filename, bad_hdr_msg ); close( infd ); return 2;
|
||||
}
|
||||
|
@ -246,7 +252,7 @@ int compress_archive( const Cl_options & cl_opts,
|
|||
const long long edsize = parse_octal( rbuf.u8() + size_o, size_l );
|
||||
const long long bufsize = round_up( edsize );
|
||||
// overflow or no extended data
|
||||
if( edsize <= 0 || edsize >= 1LL << 33 || bufsize >= INT_MAX )
|
||||
if( edsize <= 0 || edsize >= 1LL << 33 || bufsize > max_edata_size )
|
||||
{ show_file_error( filename, bad_hdr_msg ); close( infd ); return 2; }
|
||||
if( !rbuf.resize( total_header_size + bufsize ) )
|
||||
{ show_file_error( filename, mem_msg ); close( infd ); return 1; }
|
||||
|
@ -263,7 +269,7 @@ int compress_archive( const Cl_options & cl_opts,
|
|||
if( readblock( infd, rbuf.u8() + total_header_size, header_size ) != header_size )
|
||||
{ show_file_error( filename, errno ? rderr_msg : end_msg, errno );
|
||||
close( infd ); return errno ? 1 : 2; }
|
||||
if( !verify_ustar_chksum( rbuf.u8() ) )
|
||||
if( !check_ustar_chksum( rbuf.u8() ) )
|
||||
{ show_file_error( filename, bad_hdr_msg ); close( infd ); return 2; }
|
||||
const Typeflag typeflag2 = (Typeflag)(rbuf() + total_header_size)[typeflag_o];
|
||||
if( typeflag2 == tf_extended || typeflag2 == tf_global )
|
||||
|
@ -294,9 +300,7 @@ int compress_archive( const Cl_options & cl_opts,
|
|||
rest -= rd;
|
||||
if( rd != size )
|
||||
{
|
||||
if( verbosity >= 0 )
|
||||
std::fprintf( stderr, "'%s' ends unexpectedly at pos %llu\n",
|
||||
filename, file_size - rest );
|
||||
show_atpos_error( filename, file_size - rest, true );
|
||||
close( infd ); return 1;
|
||||
}
|
||||
if( !archive_write( buf, size, encoder ) ) { close( infd ); return 1; }
|
||||
|
@ -321,6 +325,18 @@ int compress_archive( const Cl_options & cl_opts,
|
|||
} // end namespace
|
||||
|
||||
|
||||
void show_atpos_error( const char * const filename, const long long pos,
|
||||
const bool isarchive )
|
||||
{
|
||||
if( verbosity < 0 ) return;
|
||||
std::fprintf( stderr, "%s: %s: %s %s at pos %llu%s%s\n", program_name,
|
||||
filename, isarchive ? "Archive" : "File",
|
||||
( errno > 0 ) ? "read error" : "ends unexpectedly", pos,
|
||||
( errno > 0 ) ? ": " : "",
|
||||
( errno > 0 ) ? std::strerror( errno ) : "" );
|
||||
}
|
||||
|
||||
|
||||
int compress( const Cl_options & cl_opts )
|
||||
{
|
||||
if( cl_opts.num_files > 1 && cl_opts.output_filename.size() )
|
||||
|
|
69
configure
vendored
69
configure
vendored
|
@ -1,12 +1,12 @@
|
|||
#! /bin/sh
|
||||
# configure script for Tarlz - Archiver with multimember lzip compression
|
||||
# Copyright (C) 2013-2022 Antonio Diaz Diaz.
|
||||
# Copyright (C) 2013-2023 Antonio Diaz Diaz.
|
||||
#
|
||||
# This configure script is free software: you have unlimited permission
|
||||
# to copy, distribute, and modify it.
|
||||
|
||||
pkgname=tarlz
|
||||
pkgversion=0.23
|
||||
pkgversion=0.24
|
||||
progname=tarlz
|
||||
srctrigger=doc/${pkgname}.texi
|
||||
|
||||
|
@ -25,6 +25,7 @@ CPPFLAGS=
|
|||
CXXFLAGS='-Wall -W -O2'
|
||||
LDFLAGS=
|
||||
LIBS='-llz -lpthread'
|
||||
MAKEINFO=makeinfo
|
||||
|
||||
# checking whether we are using GNU C++.
|
||||
/bin/sh -c "${CXX} --version" > /dev/null 2>&1 || { CXX=c++ ; CXXFLAGS=-O2 ; }
|
||||
|
@ -35,7 +36,7 @@ no_create=
|
|||
while [ $# != 0 ] ; do
|
||||
|
||||
# Get the first arg, and shuffle
|
||||
option=$1 ; arg2=no
|
||||
option="$1" ; arg2=no
|
||||
shift
|
||||
|
||||
# Add the argument quoted to args
|
||||
|
@ -43,12 +44,12 @@ while [ $# != 0 ] ; do
|
|||
else args="${args} \"${option}\"" ; fi
|
||||
|
||||
# Split out the argument for options that take them
|
||||
case ${option} in
|
||||
*=*) optarg=`echo "${option}" | sed -e 's,^[^=]*=,,;s,/$,,'` ;;
|
||||
case "${option}" in
|
||||
*=*) optarg="`echo "${option}" | sed -e 's,^[^=]*=,,;s,/$,,'`" ;;
|
||||
esac
|
||||
|
||||
# Process the options
|
||||
case ${option} in
|
||||
case "${option}" in
|
||||
--help | -h)
|
||||
echo "Usage: $0 [OPTION]... [VAR=VALUE]..."
|
||||
echo
|
||||
|
@ -58,7 +59,7 @@ while [ $# != 0 ] ; do
|
|||
echo "Options and variables: [defaults in brackets]"
|
||||
echo " -h, --help display this help and exit"
|
||||
echo " -V, --version output version information and exit"
|
||||
echo " --srcdir=DIR find the sources in DIR [. or ..]"
|
||||
echo " --srcdir=DIR find the source code in DIR [. or ..]"
|
||||
echo " --prefix=DIR install into DIR [${prefix}]"
|
||||
echo " --exec-prefix=DIR base directory for arch-dependent files [${exec_prefix}]"
|
||||
echo " --bindir=DIR user executables directory [${bindir}]"
|
||||
|
@ -71,34 +72,36 @@ while [ $# != 0 ] ; do
|
|||
echo " CXXFLAGS+=OPTIONS append options to the current value of CXXFLAGS"
|
||||
echo " LDFLAGS=OPTIONS command line options for the linker [${LDFLAGS}]"
|
||||
echo " LIBS=OPTIONS libraries to pass to the linker [${LIBS}]"
|
||||
echo " MAKEINFO=NAME makeinfo program to use [${MAKEINFO}]"
|
||||
echo
|
||||
exit 0 ;;
|
||||
--version | -V)
|
||||
echo "Configure script for ${pkgname} version ${pkgversion}"
|
||||
exit 0 ;;
|
||||
--srcdir) srcdir=$1 ; arg2=yes ;;
|
||||
--prefix) prefix=$1 ; arg2=yes ;;
|
||||
--exec-prefix) exec_prefix=$1 ; arg2=yes ;;
|
||||
--bindir) bindir=$1 ; arg2=yes ;;
|
||||
--datarootdir) datarootdir=$1 ; arg2=yes ;;
|
||||
--infodir) infodir=$1 ; arg2=yes ;;
|
||||
--mandir) mandir=$1 ; arg2=yes ;;
|
||||
--srcdir) srcdir="$1" ; arg2=yes ;;
|
||||
--prefix) prefix="$1" ; arg2=yes ;;
|
||||
--exec-prefix) exec_prefix="$1" ; arg2=yes ;;
|
||||
--bindir) bindir="$1" ; arg2=yes ;;
|
||||
--datarootdir) datarootdir="$1" ; arg2=yes ;;
|
||||
--infodir) infodir="$1" ; arg2=yes ;;
|
||||
--mandir) mandir="$1" ; arg2=yes ;;
|
||||
|
||||
--srcdir=*) srcdir=${optarg} ;;
|
||||
--prefix=*) prefix=${optarg} ;;
|
||||
--exec-prefix=*) exec_prefix=${optarg} ;;
|
||||
--bindir=*) bindir=${optarg} ;;
|
||||
--datarootdir=*) datarootdir=${optarg} ;;
|
||||
--infodir=*) infodir=${optarg} ;;
|
||||
--mandir=*) mandir=${optarg} ;;
|
||||
--no-create) no_create=yes ;;
|
||||
--srcdir=*) srcdir="${optarg}" ;;
|
||||
--prefix=*) prefix="${optarg}" ;;
|
||||
--exec-prefix=*) exec_prefix="${optarg}" ;;
|
||||
--bindir=*) bindir="${optarg}" ;;
|
||||
--datarootdir=*) datarootdir="${optarg}" ;;
|
||||
--infodir=*) infodir="${optarg}" ;;
|
||||
--mandir=*) mandir="${optarg}" ;;
|
||||
--no-create) no_create=yes ;;
|
||||
|
||||
CXX=*) CXX=${optarg} ;;
|
||||
CPPFLAGS=*) CPPFLAGS=${optarg} ;;
|
||||
CXXFLAGS=*) CXXFLAGS=${optarg} ;;
|
||||
CXX=*) CXX="${optarg}" ;;
|
||||
CPPFLAGS=*) CPPFLAGS="${optarg}" ;;
|
||||
CXXFLAGS=*) CXXFLAGS="${optarg}" ;;
|
||||
CXXFLAGS+=*) CXXFLAGS="${CXXFLAGS} ${optarg}" ;;
|
||||
LDFLAGS=*) LDFLAGS=${optarg} ;;
|
||||
LDFLAGS=*) LDFLAGS="${optarg}" ;;
|
||||
LIBS=*) LIBS="${optarg} ${LIBS}" ;;
|
||||
MAKEINFO=*) MAKEINFO="${optarg}" ;;
|
||||
|
||||
--*)
|
||||
echo "configure: WARNING: unrecognized option: '${option}'" 1>&2 ;;
|
||||
|
@ -118,19 +121,19 @@ while [ $# != 0 ] ; do
|
|||
fi
|
||||
done
|
||||
|
||||
# Find the source files, if location was not specified.
|
||||
# Find the source code, if location was not specified.
|
||||
srcdirtext=
|
||||
if [ -z "${srcdir}" ] ; then
|
||||
srcdirtext="or . or .." ; srcdir=.
|
||||
if [ ! -r "${srcdir}/${srctrigger}" ] ; then srcdir=.. ; fi
|
||||
if [ ! -r "${srcdir}/${srctrigger}" ] ; then
|
||||
## the sed command below emulates the dirname command
|
||||
srcdir=`echo "$0" | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
|
||||
srcdir="`echo "$0" | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ ! -r "${srcdir}/${srctrigger}" ] ; then
|
||||
echo "configure: Can't find sources in ${srcdir} ${srcdirtext}" 1>&2
|
||||
echo "configure: Can't find source code in ${srcdir} ${srcdirtext}" 1>&2
|
||||
echo "configure: (At least ${srctrigger} is missing)." 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
@ -150,7 +153,7 @@ if [ -z "${no_create}" ] ; then
|
|||
# This script is free software: you have unlimited permission
|
||||
# to copy, distribute, and modify it.
|
||||
|
||||
exec /bin/sh $0 ${args} --no-create
|
||||
exec /bin/sh "$0" ${args} --no-create
|
||||
EOF
|
||||
chmod +x config.status
|
||||
fi
|
||||
|
@ -168,10 +171,11 @@ echo "CPPFLAGS = ${CPPFLAGS}"
|
|||
echo "CXXFLAGS = ${CXXFLAGS}"
|
||||
echo "LDFLAGS = ${LDFLAGS}"
|
||||
echo "LIBS = ${LIBS}"
|
||||
echo "MAKEINFO = ${MAKEINFO}"
|
||||
rm -f Makefile
|
||||
cat > Makefile << EOF
|
||||
# Makefile for Tarlz - Archiver with multimember lzip compression
|
||||
# Copyright (C) 2013-2022 Antonio Diaz Diaz.
|
||||
# Copyright (C) 2013-2023 Antonio Diaz Diaz.
|
||||
# This file was generated automatically by configure. Don't edit.
|
||||
#
|
||||
# This Makefile is free software: you have unlimited permission
|
||||
|
@ -192,9 +196,10 @@ CPPFLAGS = ${CPPFLAGS}
|
|||
CXXFLAGS = ${CXXFLAGS}
|
||||
LDFLAGS = ${LDFLAGS}
|
||||
LIBS = ${LIBS}
|
||||
MAKEINFO = ${MAKEINFO}
|
||||
EOF
|
||||
cat "${srcdir}/Makefile.in" >> Makefile
|
||||
|
||||
echo "OK. Now you can run make."
|
||||
echo "If make fails, verify that the compression library lzlib is correctly"
|
||||
echo "If make fails, check that the compression library lzlib is correctly"
|
||||
echo "installed (see INSTALL)."
|
||||
|
|
180
create.cc
180
create.cc
|
@ -1,5 +1,5 @@
|
|||
/* Tarlz - Archiver with multimember lzip compression
|
||||
Copyright (C) 2013-2022 Antonio Diaz Diaz.
|
||||
Copyright (C) 2013-2023 Antonio Diaz Diaz.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -20,8 +20,6 @@
|
|||
#include <algorithm>
|
||||
#include <cerrno>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <pthread.h>
|
||||
#include <stdint.h> // for lzlib.h
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
|
@ -50,7 +48,6 @@ const char * archive_namep = 0;
|
|||
unsigned long long partial_data_size = 0; // size of current block
|
||||
Resizable_buffer grbuf; // extended header + data
|
||||
int goutfd = -1;
|
||||
int error_status = 0;
|
||||
|
||||
|
||||
bool option_C_after_relative_filename( const Arg_parser & parser )
|
||||
|
@ -78,7 +75,7 @@ long long check_compressed_appendable( const int fd, const bool remove_eoa )
|
|||
if( rd == 0 && errno == 0 ) return 0; // append to empty archive
|
||||
if( rd < min_member_size || ( rd != bufsize && errno ) ) return -1;
|
||||
const Lzip_header * const p = (const Lzip_header *)buf; // shut up gcc
|
||||
if( !p->verify_magic() || !p->verify_version() ) return -1;
|
||||
if( !p->check_magic() || !p->check_version() ) return -1;
|
||||
LZ_Decoder * decoder = LZ_decompress_open(); // decompress first header
|
||||
if( !decoder || LZ_decompress_errno( decoder ) != LZ_ok ||
|
||||
LZ_decompress_write( decoder, buf, rd ) != rd ||
|
||||
|
@ -86,7 +83,7 @@ long long check_compressed_appendable( const int fd, const bool remove_eoa )
|
|||
{ LZ_decompress_close( decoder ); return -1; }
|
||||
LZ_decompress_close( decoder );
|
||||
const bool maybe_eoa = block_is_zero( buf, header_size );
|
||||
if( !verify_ustar_chksum( buf ) && !maybe_eoa ) return -1;
|
||||
if( !check_ustar_chksum( buf ) && !maybe_eoa ) return -1;
|
||||
const long long end = lseek( fd, 0, SEEK_END );
|
||||
if( end < min_member_size ) return -1;
|
||||
|
||||
|
@ -100,7 +97,7 @@ long long check_compressed_appendable( const int fd, const bool remove_eoa )
|
|||
Lzip_header header; // read last header
|
||||
if( seek_read( fd, header.data, Lzip_header::size,
|
||||
end - member_size ) != Lzip_header::size ) return -1;
|
||||
if( !header.verify_magic() || !header.verify_version() ||
|
||||
if( !header.check_magic() || !header.check_version() ||
|
||||
!isvalid_ds( header.dictionary_size() ) ) return -1;
|
||||
|
||||
// EOA marker in last member must contain between 512 and 32256 zeros alone
|
||||
|
@ -142,7 +139,7 @@ long long check_uncompressed_appendable( const int fd, const bool remove_eoa )
|
|||
const int rd = readblock( fd, header, header_size );
|
||||
if( rd == 0 && errno == 0 ) break; // missing EOA blocks
|
||||
if( rd != header_size ) return -1;
|
||||
if( !verify_ustar_chksum( header ) ) // maybe EOA block
|
||||
if( !check_ustar_chksum( header ) ) // maybe EOA block
|
||||
{ if( block_is_zero( header, header_size ) ) break; else return -1; }
|
||||
const Typeflag typeflag = (Typeflag)header[typeflag_o];
|
||||
if( typeflag == tf_extended || typeflag == tf_global )
|
||||
|
@ -150,7 +147,7 @@ long long check_uncompressed_appendable( const int fd, const bool remove_eoa )
|
|||
if( prev_extended ) return -1;
|
||||
const long long edsize = parse_octal( header + size_o, size_l );
|
||||
const long long bufsize = round_up( edsize );
|
||||
if( edsize <= 0 || edsize >= 1LL << 33 || bufsize >= INT_MAX )
|
||||
if( edsize <= 0 || edsize >= 1LL << 33 || bufsize > max_edata_size )
|
||||
return -1; // overflow or no extended data
|
||||
if( !rbuf.resize( bufsize ) ) return -2;
|
||||
if( readblock( fd, rbuf.u8(), bufsize ) != bufsize )
|
||||
|
@ -204,21 +201,6 @@ bool archive_write( const uint8_t * const buf, const int size )
|
|||
}
|
||||
|
||||
|
||||
bool write_extended( const Extended & extended )
|
||||
{
|
||||
const long long ebsize = extended.format_block( grbuf ); // may be 0
|
||||
if( ebsize < 0 )
|
||||
{ show_error( ( ebsize == -2 ) ? mem_msg2 : eferec_msg ); return false; }
|
||||
for( long long pos = 0; pos < ebsize; ) // write extended block to archive
|
||||
{
|
||||
int size = std::min( ebsize - pos, 1LL << 20 );
|
||||
if( !archive_write( grbuf.u8() + pos, size ) ) return false;
|
||||
pos += size;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Return true if it stores filename in the ustar header.
|
||||
bool store_name( const char * const filename, Extended & extended,
|
||||
Tar_header header, const bool force_extended_name )
|
||||
|
@ -260,12 +242,15 @@ int add_member( const char * const filename, const struct stat *,
|
|||
const int infd = file_size ? open_instream( filename ) : -1;
|
||||
if( file_size && infd < 0 ) { set_error_status( 1 ); return 0; }
|
||||
|
||||
const int ebsize = extended.format_block( grbuf ); // may be 0
|
||||
if( ebsize < 0 ) { show_error( extended.full_size_error() ); return 1; }
|
||||
if( encoder && gcl_opts->solidity == bsolid &&
|
||||
block_is_full( extended.full_size(), file_size, gcl_opts->data_size,
|
||||
block_is_full( ebsize, file_size, gcl_opts->data_size,
|
||||
partial_data_size ) && !archive_write( 0, 0 ) ) return 1;
|
||||
// write extended block to archive
|
||||
if( ebsize > 0 && !archive_write( grbuf.u8(), ebsize ) ) return 1;
|
||||
if( !archive_write( header, header_size ) ) return 1;
|
||||
|
||||
if( !write_extended( extended ) || !archive_write( header, header_size ) )
|
||||
return 1;
|
||||
if( file_size )
|
||||
{
|
||||
const long long bufsize = 32 * header_size;
|
||||
|
@ -278,9 +263,7 @@ int add_member( const char * const filename, const struct stat *,
|
|||
rest -= rd;
|
||||
if( rd != size )
|
||||
{
|
||||
if( verbosity >= 0 )
|
||||
std::fprintf( stderr, "File '%s' ends unexpectedly at pos %llu\n",
|
||||
filename, file_size - rest );
|
||||
show_atpos_error( filename, file_size - rest, false );
|
||||
close( infd ); return 1;
|
||||
}
|
||||
if( rest == 0 ) // last read
|
||||
|
@ -400,31 +383,6 @@ const char * remove_leading_dotslash( const char * const filename,
|
|||
}
|
||||
|
||||
|
||||
/* If msgp is null, print the message, else return the message in *msgp.
|
||||
If prefix is already in the list, print nothing or return empty *msgp.
|
||||
Return true if a message is printed or returned in *msgp. */
|
||||
bool print_removed_prefix( const std::string & prefix,
|
||||
std::string * const msgp )
|
||||
{
|
||||
// prevent two threads from modifying the list of prefixes at the same time
|
||||
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static std::vector< std::string > prefixes; // list of prefixes
|
||||
|
||||
if( verbosity < 0 || prefix.empty() )
|
||||
{ if( msgp ) msgp->clear(); return false; }
|
||||
xlock( &mutex );
|
||||
for( unsigned i = 0; i < prefixes.size(); ++i )
|
||||
if( prefixes[i] == prefix )
|
||||
{ xunlock( &mutex ); if( msgp ) msgp->clear(); return false; }
|
||||
prefixes.push_back( prefix );
|
||||
std::string msg( "Removing leading '" ); msg += prefix;
|
||||
msg += "' from member names.";
|
||||
if( msgp ) *msgp = msg; else show_error( msg.c_str() );
|
||||
xunlock( &mutex ); // put here to prevent mixing calls to show_error
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// set file_size != 0 only for regular files
|
||||
bool fill_headers( const char * const filename, Extended & extended,
|
||||
Tar_header header, long long & file_size, const int flag )
|
||||
|
@ -534,13 +492,13 @@ bool fill_headers( const char * const filename, Extended & extended,
|
|||
}
|
||||
|
||||
|
||||
bool block_is_full( const long long extended_size,
|
||||
bool block_is_full( const int extended_size,
|
||||
const unsigned long long file_size,
|
||||
const unsigned long long target_size,
|
||||
unsigned long long & partial_data_size )
|
||||
{
|
||||
const unsigned long long member_size = // may overflow 'long long'
|
||||
header_size + extended_size + round_up( file_size );
|
||||
extended_size + header_size + round_up( file_size );
|
||||
if( partial_data_size >= target_size ||
|
||||
( partial_data_size >= min_data_size &&
|
||||
partial_data_size + member_size / 2 > target_size ) )
|
||||
|
@ -549,25 +507,6 @@ bool block_is_full( const long long extended_size,
|
|||
}
|
||||
|
||||
|
||||
void set_error_status( const int retval )
|
||||
{
|
||||
// prevent two threads from modifying the error_status at the same time
|
||||
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
xlock( &mutex );
|
||||
if( error_status < retval ) error_status = retval;
|
||||
xunlock( &mutex );
|
||||
}
|
||||
|
||||
int final_exit_status( int retval, const bool show_msg )
|
||||
{
|
||||
if( retval == 0 && error_status )
|
||||
{ if( show_msg )
|
||||
show_error( "Exiting with failure status due to previous errors." );
|
||||
retval = error_status; }
|
||||
return retval;
|
||||
}
|
||||
|
||||
unsigned ustar_chksum( const Tar_header header )
|
||||
{
|
||||
unsigned chksum = chksum_l * 0x20; // treat chksum field as spaces
|
||||
|
@ -577,8 +516,8 @@ unsigned ustar_chksum( const Tar_header header )
|
|||
}
|
||||
|
||||
|
||||
bool verify_ustar_chksum( const Tar_header header )
|
||||
{ return ( verify_ustar_magic( header ) &&
|
||||
bool check_ustar_chksum( const Tar_header header )
|
||||
{ return ( check_ustar_magic( header ) &&
|
||||
ustar_chksum( header ) == parse_octal( header + chksum_o, chksum_l ) ); }
|
||||
|
||||
|
||||
|
@ -591,10 +530,25 @@ bool has_lz_ext( const std::string & name )
|
|||
}
|
||||
|
||||
|
||||
int Cl_options::compressed() const // tri-state bool with error (-2)
|
||||
{
|
||||
const int lz_ext = archive_name.empty() ? -1 : has_lz_ext( archive_name );
|
||||
if( !level_set ) return lz_ext; // no level set in command line
|
||||
const bool cl_compressed = !uncompressed();
|
||||
if( lz_ext < 0 || lz_ext == cl_compressed ) return cl_compressed;
|
||||
show_file_error( archive_name.c_str(), lz_ext ?
|
||||
"Uncompressed archive can't have .lz or .tlz extension." :
|
||||
"Compressed archive requires .lz or .tlz extension." );
|
||||
return -2;
|
||||
}
|
||||
|
||||
|
||||
int concatenate( const Cl_options & cl_opts )
|
||||
{
|
||||
if( cl_opts.num_files <= 0 )
|
||||
{ if( verbosity >= 1 ) show_error( "Nothing to concatenate." ); return 0; }
|
||||
int compressed = cl_opts.compressed(); // tri-state bool
|
||||
if( compressed == -2 ) return 1;
|
||||
const bool to_stdout = cl_opts.archive_name.empty();
|
||||
archive_namep = to_stdout ? "(stdout)" : cl_opts.archive_name.c_str();
|
||||
const int outfd =
|
||||
|
@ -604,24 +558,17 @@ int concatenate( const Cl_options & cl_opts )
|
|||
{ close( outfd ); return 1; }
|
||||
if( !to_stdout && !archive_attrs.init( outfd ) )
|
||||
{ show_file_error( archive_namep, "Can't stat", errno ); return 1; }
|
||||
int compressed; // tri-state bool
|
||||
if( to_stdout ) compressed = -1; // unknown
|
||||
else
|
||||
if( !to_stdout && compressed >= 0 ) // level or ext are set in cl
|
||||
{
|
||||
compressed = has_lz_ext( cl_opts.archive_name ); // default value
|
||||
long long pos = check_compressed_appendable( outfd, true );
|
||||
if( pos > 0 ) compressed = true;
|
||||
else if( pos < 0 )
|
||||
{
|
||||
pos = check_uncompressed_appendable( outfd, true );
|
||||
if( pos > 0 ) compressed = false;
|
||||
else if( pos == -2 ) { show_error( mem_msg ); close( outfd ); return 1; }
|
||||
else if( pos < 0 )
|
||||
{ show_file_error( archive_namep, compressed ?
|
||||
"This does not look like an appendable tar.lz archive." :
|
||||
"This does not look like an appendable tar archive." );
|
||||
close( outfd ); return 2; }
|
||||
}
|
||||
const long long pos = compressed ?
|
||||
check_compressed_appendable( outfd, true ) :
|
||||
check_uncompressed_appendable( outfd, true );
|
||||
if( pos == -2 ) { show_error( mem_msg ); close( outfd ); return 1; }
|
||||
if( pos < 0 )
|
||||
{ show_file_error( archive_namep, compressed ?
|
||||
"This does not look like an appendable tar.lz archive." :
|
||||
"This does not look like an appendable tar archive." );
|
||||
close( outfd ); return 2; }
|
||||
}
|
||||
|
||||
int retval = 0;
|
||||
|
@ -634,17 +581,18 @@ int concatenate( const Cl_options & cl_opts )
|
|||
const int infd = open_instream( filename );
|
||||
if( infd < 0 ) { retval = 1; break; }
|
||||
struct stat st;
|
||||
if( !to_stdout && fstat( infd, &st ) == 0 && archive_attrs.is_the_archive( st ) )
|
||||
{ show_file_error( filename, "Archive can't contain itself; not concatenated." );
|
||||
close( infd ); continue; }
|
||||
if( !to_stdout && fstat( infd, &st ) == 0 &&
|
||||
archive_attrs.is_the_archive( st ) )
|
||||
{ show_file_error( filename, "Archive can't contain itself; "
|
||||
"not concatenated." ); close( infd ); continue; }
|
||||
long long size;
|
||||
if( compressed < 0 ) // not initialized yet
|
||||
if( compressed < 0 ) // not initialized yet
|
||||
{
|
||||
if( ( size = check_compressed_appendable( infd, false ) ) > 0 )
|
||||
compressed = true;
|
||||
else if( ( size = check_uncompressed_appendable( infd, false ) ) > 0 )
|
||||
compressed = false;
|
||||
else if( size != -2 ) { size = -1 ; compressed = has_lz_ext( filename ); }
|
||||
else if( size != -2 ) { size = -1; compressed = has_lz_ext( filename ); }
|
||||
}
|
||||
else size = compressed ? check_compressed_appendable( infd, false ) :
|
||||
check_uncompressed_appendable( infd, false );
|
||||
|
@ -673,15 +621,12 @@ int concatenate( const Cl_options & cl_opts )
|
|||
int encode( const Cl_options & cl_opts )
|
||||
{
|
||||
if( !grbuf.size() ) { show_error( mem_msg ); return 1; }
|
||||
const bool compressed = ( cl_opts.level >= 0 && cl_opts.level <= 9 );
|
||||
int compressed = cl_opts.compressed(); // tri-state bool
|
||||
if( compressed == -2 ) return 1;
|
||||
const bool to_stdout = cl_opts.archive_name.empty();
|
||||
archive_namep = to_stdout ? "(stdout)" : cl_opts.archive_name.c_str();
|
||||
gcl_opts = &cl_opts;
|
||||
|
||||
if( !to_stdout && !compressed && has_lz_ext( cl_opts.archive_name ) )
|
||||
{ show_file_error( archive_namep,
|
||||
"Uncompressed mode incompatible with .lz extension." ); return 2; }
|
||||
|
||||
const bool append = cl_opts.program_mode == m_append;
|
||||
if( cl_opts.num_files <= 0 )
|
||||
{
|
||||
|
@ -701,18 +646,23 @@ int encode( const Cl_options & cl_opts )
|
|||
{ close( goutfd ); return 1; }
|
||||
if( append && !to_stdout )
|
||||
{
|
||||
if( compressed && check_compressed_appendable( goutfd, true ) < 0 )
|
||||
{ show_file_error( archive_namep,
|
||||
"This does not look like an appendable tar.lz archive." );
|
||||
close( goutfd ); return 2; }
|
||||
if( !compressed )
|
||||
long long pos;
|
||||
if( compressed < 0 ) // not initialized yet
|
||||
{
|
||||
const long long pos = check_uncompressed_appendable( goutfd, true );
|
||||
if( pos == -2 ) { show_error( mem_msg ); close( goutfd ); return 1; }
|
||||
if( pos < 0 ) { show_file_error( archive_namep,
|
||||
"This does not look like an appendable tar archive." );
|
||||
close( goutfd ); return 2; }
|
||||
if( ( pos = check_compressed_appendable( goutfd, true ) ) > 0 )
|
||||
compressed = true;
|
||||
else if( ( pos = check_uncompressed_appendable( goutfd, true ) ) > 0 )
|
||||
compressed = false;
|
||||
else if( pos != -2 ) { pos = -1; compressed = false; } // unknown
|
||||
}
|
||||
else pos = compressed ? check_compressed_appendable( goutfd, true ) :
|
||||
check_uncompressed_appendable( goutfd, true );
|
||||
if( pos == -2 ) { show_error( mem_msg ); close( goutfd ); return 1; }
|
||||
if( pos < 0 )
|
||||
{ show_file_error( archive_namep, compressed ?
|
||||
"This does not look like an appendable tar.lz archive." :
|
||||
"This does not look like an appendable tar archive." );
|
||||
close( goutfd ); return 2; }
|
||||
}
|
||||
|
||||
if( !archive_attrs.init( goutfd ) )
|
||||
|
|
3
create.h
3
create.h
|
@ -1,5 +1,5 @@
|
|||
/* Tarlz - Archiver with multimember lzip compression
|
||||
Copyright (C) 2013-2022 Antonio Diaz Diaz.
|
||||
Copyright (C) 2013-2023 Antonio Diaz Diaz.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -45,4 +45,3 @@ public:
|
|||
extern Archive_attrs archive_attrs;
|
||||
|
||||
const char * const cant_stat = "Can't stat input file";
|
||||
const char * const eferec_msg = "Error formatting extended records.";
|
||||
|
|
50
create_lz.cc
50
create_lz.cc
|
@ -1,5 +1,5 @@
|
|||
/* Tarlz - Archiver with multimember lzip compression
|
||||
Copyright (C) 2013-2022 Antonio Diaz Diaz.
|
||||
Copyright (C) 2013-2023 Antonio Diaz Diaz.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -20,7 +20,6 @@
|
|||
#include <algorithm>
|
||||
#include <cerrno>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <queue>
|
||||
#include <pthread.h>
|
||||
#include <stdint.h> // for lzlib.h
|
||||
|
@ -31,6 +30,7 @@
|
|||
|
||||
#include "tarlz.h"
|
||||
#include "arg_parser.h"
|
||||
#include "common_mutex.h"
|
||||
#include "create.h"
|
||||
|
||||
|
||||
|
@ -87,9 +87,9 @@ struct Ipacket // filename, file size and headers
|
|||
const uint8_t * const header;
|
||||
|
||||
Ipacket() : file_size( 0 ), extended( 0 ), header( 0 ) {}
|
||||
Ipacket( const char * const name, const long long s,
|
||||
Ipacket( const char * const name, const long long fs,
|
||||
const Extended * const ext, const uint8_t * const head )
|
||||
: file_size( s ), filename( name ), extended( ext ), header( head ) {}
|
||||
: file_size( fs ), filename( name ), extended( ext ), header( head ) {}
|
||||
};
|
||||
|
||||
struct Opacket // compressed data to be written to the archive
|
||||
|
@ -269,11 +269,14 @@ int add_member_lz( const char * const filename, const struct stat *,
|
|||
{ delete[] header; delete extended; return 0; }
|
||||
print_removed_prefix( extended->removed_prefix );
|
||||
|
||||
if( gcl_opts->solidity == bsolid &&
|
||||
block_is_full( extended->full_size(), file_size, gcl_opts->data_size,
|
||||
partial_data_size ) )
|
||||
courierp->receive_packet( new Ipacket ); // end of group
|
||||
|
||||
if( gcl_opts->solidity == bsolid )
|
||||
{
|
||||
const int ebsize = extended->full_size();
|
||||
if( ebsize < 0 ) { show_error( extended->full_size_error() ); return 1; }
|
||||
if( block_is_full( ebsize, file_size, gcl_opts->data_size,
|
||||
partial_data_size ) )
|
||||
courierp->receive_packet( new Ipacket ); // end of group
|
||||
}
|
||||
courierp->receive_packet( new Ipacket( filename, file_size, extended, header ) );
|
||||
|
||||
if( gcl_opts->solidity == no_solid ) // one tar member per group
|
||||
|
@ -331,9 +334,9 @@ extern "C" void * grouper( void * arg )
|
|||
}
|
||||
|
||||
|
||||
/* Writes ibuf to encoder. To minimize dictionary size, it does not read
|
||||
from encoder until encoder's input buffer is full or finish is true.
|
||||
Sends opacket to courier and allocates new obuf each time obuf is full.
|
||||
/* Write ibuf to encoder. To minimize dictionary size, do not read from
|
||||
encoder until encoder's input buffer is full or finish is true.
|
||||
Send opacket to courier and allocate new obuf each time obuf is full.
|
||||
*/
|
||||
void loop_encode( const uint8_t * const ibuf, const int isize,
|
||||
uint8_t * & obuf, int & opos, Packet_courier & courier,
|
||||
|
@ -423,8 +426,7 @@ extern "C" void * cworker( void * arg )
|
|||
}
|
||||
|
||||
const char * const filename = ipacket->filename.c_str();
|
||||
const int infd =
|
||||
ipacket->file_size ? open_instream( filename ) : -1;
|
||||
const int infd = ipacket->file_size ? open_instream( filename ) : -1;
|
||||
if( ipacket->file_size && infd < 0 ) // can't read file data
|
||||
{ delete[] ipacket->header; delete ipacket->extended; delete ipacket;
|
||||
set_error_status( 1 ); continue; } // skip file
|
||||
|
@ -444,17 +446,11 @@ extern "C" void * cworker( void * arg )
|
|||
}
|
||||
}
|
||||
|
||||
if( !ipacket->extended->empty() ) // compress extended block
|
||||
{
|
||||
const long long ebsize = ipacket->extended->format_block( rbuf );
|
||||
if( ebsize < 0 )
|
||||
{ show_error( ( ebsize == -2 ) ? mem_msg2 : eferec_msg ); exit_fail_mt(); }
|
||||
/* Limit the size of the extended block to INT_MAX - 1 so that it can
|
||||
be fed to lzlib as one buffer. */
|
||||
if( ebsize >= INT_MAX )
|
||||
{ show_error( "Extended records size >= INT_MAX." ); exit_fail_mt(); }
|
||||
const int ebsize = ipacket->extended->format_block( rbuf ); // may be 0
|
||||
if( ebsize < 0 )
|
||||
{ show_error( ipacket->extended->full_size_error() ); exit_fail_mt(); }
|
||||
if( ebsize > 0 ) // compress extended block
|
||||
loop_encode( rbuf.u8(), ebsize, data, opos, courier, encoder, worker_id );
|
||||
}
|
||||
// compress ustar header
|
||||
loop_encode( ipacket->header, header_size, data, opos, courier,
|
||||
encoder, worker_id );
|
||||
|
@ -472,9 +468,7 @@ extern "C" void * cworker( void * arg )
|
|||
rest -= rd;
|
||||
if( rd != size )
|
||||
{
|
||||
if( verbosity >= 0 )
|
||||
std::fprintf( stderr, "File '%s' ends unexpectedly at pos %llu\n",
|
||||
filename, ipacket->file_size - rest );
|
||||
show_atpos_error( filename, ipacket->file_size - rest, false );
|
||||
close( infd ); exit_fail_mt();
|
||||
}
|
||||
if( rest == 0 ) // last read
|
||||
|
@ -595,6 +589,6 @@ int encode_lz( const Cl_options & cl_opts, const char * const archive_namep,
|
|||
courier.ocheck_counter,
|
||||
courier.owait_counter );
|
||||
|
||||
if( !courier.finished() ) internal_error( "courier not finished." );
|
||||
if( !courier.finished() ) internal_error( conofin_msg );
|
||||
return final_exit_status( retval );
|
||||
}
|
||||
|
|
102
decode.cc
102
decode.cc
|
@ -1,5 +1,5 @@
|
|||
/* Tarlz - Archiver with multimember lzip compression
|
||||
Copyright (C) 2013-2022 Antonio Diaz Diaz.
|
||||
Copyright (C) 2013-2023 Antonio Diaz Diaz.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -21,7 +21,7 @@
|
|||
#include <cctype>
|
||||
#include <cerrno>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <fcntl.h>
|
||||
#include <stdint.h> // for lzlib.h
|
||||
#include <unistd.h>
|
||||
#include <utime.h>
|
||||
|
@ -246,9 +246,37 @@ void format_file_diff( std::string & ostr, const char * const filename,
|
|||
{ if( verbosity >= 0 )
|
||||
{ ostr += filename; ostr += ": "; ostr += msg; ostr += '\n'; } }
|
||||
|
||||
|
||||
bool option_C_present( const Arg_parser & parser )
|
||||
{
|
||||
for( int i = 0; i < parser.arguments(); ++i )
|
||||
if( parser.code( i ) == 'C' ) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool option_C_after_filename( const Arg_parser & parser )
|
||||
{
|
||||
for( int i = 0; i < parser.arguments(); ++i )
|
||||
if( nonempty_arg( parser, i ) )
|
||||
while( ++i < parser.arguments() )
|
||||
if( parser.code( i ) == 'C' ) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
} // end namespace
|
||||
|
||||
|
||||
mode_t get_umask()
|
||||
{
|
||||
static mode_t mask = 0; // read once, cache the result
|
||||
static bool first_call = true;
|
||||
if( first_call ) { first_call = false; mask = umask( 0 ); umask( mask );
|
||||
mask &= S_IRWXU | S_IRWXG | S_IRWXO; }
|
||||
return mask;
|
||||
}
|
||||
|
||||
|
||||
bool compare_file_type( std::string & estr, std::string & ostr,
|
||||
const Cl_options & cl_opts,
|
||||
const Extended & extended, const Tar_header header )
|
||||
|
@ -380,32 +408,37 @@ int decode( const Cl_options & cl_opts )
|
|||
const Archive_descriptor ad( cl_opts.archive_name );
|
||||
if( ad.infd < 0 ) return 1;
|
||||
|
||||
// Execute -C options and mark filenames to be compared, extracted or listed.
|
||||
// name_pending is of type char instead of bool to allow concurrent update.
|
||||
std::vector< char > name_pending( cl_opts.parser.arguments(), false );
|
||||
for( int i = 0; i < cl_opts.parser.arguments(); ++i )
|
||||
{
|
||||
const int code = cl_opts.parser.code( i );
|
||||
if( code == 'C' && cl_opts.program_mode != m_list )
|
||||
const bool c_present = option_C_present( cl_opts.parser ) &&
|
||||
cl_opts.program_mode != m_list;
|
||||
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 )
|
||||
{ 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 )
|
||||
{
|
||||
if( cl_opts.parser.code( i ) != 'C' ) continue;
|
||||
const char * const dir = cl_opts.parser.argument( i ).c_str();
|
||||
if( chdir( dir ) != 0 )
|
||||
{ show_file_error( dir, chdir_msg, errno ); return 1; }
|
||||
}
|
||||
if( !code && cl_opts.parser.argument( i ).size() &&
|
||||
/* Mark filenames to be compared, extracted or listed.
|
||||
name_pending is of type char instead of bool to allow concurrent update. */
|
||||
std::vector< char > name_pending( cl_opts.parser.arguments(), false );
|
||||
for( int i = 0; i < cl_opts.parser.arguments(); ++i )
|
||||
if( nonempty_arg( cl_opts.parser, i ) && // skip opts, empty names
|
||||
!Exclude::excluded( cl_opts.parser.argument( i ).c_str() ) )
|
||||
name_pending[i] = true;
|
||||
}
|
||||
|
||||
// multi-threaded --list is faster even with 1 thread and 1 file in archive
|
||||
// but multi-threaded --diff and --extract probably need at least 2 of each
|
||||
if( ( cl_opts.program_mode == m_diff || cl_opts.program_mode == m_list ||
|
||||
cl_opts.program_mode == m_extract ) && cl_opts.num_workers > 0 &&
|
||||
ad.indexed && ad.lzip_index.members() >= 2 ) // one file + EOA
|
||||
{
|
||||
// show_file_error( ad.namep, "Is compressed seekable" );
|
||||
/* multi-threaded --list is faster even with 1 thread and 1 file in archive
|
||||
but multi-threaded --diff and --extract probably need at least 2 of each.
|
||||
CWD is not per-thread; multi-threaded decode can't be used if a
|
||||
-C option appears after a file name in the command line. */
|
||||
if( cl_opts.num_workers > 0 && !c_after_name && ad.indexed &&
|
||||
ad.lzip_index.members() >= 2 ) // 2 lzip members may be 1 file + EOA
|
||||
return decode_lz( cl_opts, ad, name_pending );
|
||||
}
|
||||
|
||||
Archive_reader ar( ad ); // serial reader
|
||||
Extended extended; // metadata from extended records
|
||||
|
@ -416,7 +449,7 @@ int decode( const Cl_options & cl_opts )
|
|||
Tar_header header;
|
||||
const int ret = ar.read( header, header_size );
|
||||
if( ret != 0 ) { read_error( ar ); if( ar.fatal() ) { retval = ret; break; } }
|
||||
if( ret != 0 || !verify_ustar_chksum( header ) ) // error or EOA
|
||||
if( ret != 0 || !check_ustar_chksum( header ) ) // error or EOA
|
||||
{
|
||||
if( ret == 0 && block_is_zero( header, header_size ) ) // EOA
|
||||
{
|
||||
|
@ -461,20 +494,23 @@ int decode( const Cl_options & cl_opts )
|
|||
|
||||
extended.fill_from_ustar( header ); // copy metadata from header
|
||||
|
||||
// members without name are skipped except when listing
|
||||
if( check_skip_filename( cl_opts, name_pending, extended.path().c_str() ) )
|
||||
retval = skip_member( ar, extended, typeflag );
|
||||
else
|
||||
{
|
||||
print_removed_prefix( extended.removed_prefix );
|
||||
if( cl_opts.program_mode == m_list )
|
||||
retval = list_member( ar, extended, header );
|
||||
else if( extended.path().empty() )
|
||||
retval = skip_member( ar, extended, typeflag );
|
||||
else if( cl_opts.program_mode == m_diff )
|
||||
retval = compare_member( cl_opts, ar, extended, header );
|
||||
else retval = extract_member( cl_opts, ar, extended, header );
|
||||
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 );
|
||||
else
|
||||
{
|
||||
print_removed_prefix( extended.removed_prefix );
|
||||
if( cl_opts.program_mode == m_list )
|
||||
retval = list_member( ar, extended, header );
|
||||
else if( extended.path().empty() )
|
||||
retval = skip_member( ar, extended, typeflag );
|
||||
else if( cl_opts.program_mode == m_diff )
|
||||
retval = compare_member( cl_opts, ar, extended, header );
|
||||
else retval = extract_member( cl_opts, ar, extended, header );
|
||||
}
|
||||
}
|
||||
catch( Chdir_error & ) { retval = 1; }
|
||||
extended.reset();
|
||||
if( retval )
|
||||
{ show_error( "Error is not recoverable: exiting now." ); break; }
|
||||
|
|
6
decode.h
6
decode.h
|
@ -1,5 +1,5 @@
|
|||
/* Tarlz - Archiver with multimember lzip compression
|
||||
Copyright (C) 2013-2022 Antonio Diaz Diaz.
|
||||
Copyright (C) 2013-2023 Antonio Diaz Diaz.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -30,3 +30,7 @@ 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 chown_msg = "Can't change file owner";
|
||||
|
||||
mode_t get_umask();
|
||||
|
||||
struct Chdir_error {};
|
||||
|
|
10
decode_lz.cc
10
decode_lz.cc
|
@ -1,5 +1,5 @@
|
|||
/* Tarlz - Archiver with multimember lzip compression
|
||||
Copyright (C) 2013-2022 Antonio Diaz Diaz.
|
||||
Copyright (C) 2013-2023 Antonio Diaz Diaz.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -20,7 +20,6 @@
|
|||
#include <algorithm>
|
||||
#include <cerrno>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <queue>
|
||||
#include <pthread.h>
|
||||
#include <stdint.h> // for lzlib.h
|
||||
|
@ -37,6 +36,7 @@
|
|||
#include "arg_parser.h"
|
||||
#include "lzip_index.h"
|
||||
#include "archive_reader.h"
|
||||
#include "common_mutex.h"
|
||||
#include "decode.h"
|
||||
|
||||
/* When a problem is detected by any worker:
|
||||
|
@ -218,7 +218,7 @@ public:
|
|||
|
||||
bool reserve_name( const unsigned worker_id, const std::string & filename )
|
||||
{
|
||||
// compare the CRCs of the names, verify collisions comparing the names
|
||||
// compare the CRCs of the names; compare the names if the CRCs collide
|
||||
const unsigned crc =
|
||||
crc32c.compute_crc( (const uint8_t *)filename.c_str(), filename.size() );
|
||||
xlock( &mutex );
|
||||
|
@ -576,7 +576,7 @@ extern "C" void * dworker( void * arg )
|
|||
courier.collect_packet( i, worker_id, ar.e_msg(),
|
||||
( ret == 1 ) ? Packet::error1 : Packet::error2, ar.e_code() );
|
||||
goto done; }
|
||||
if( !verify_ustar_chksum( header ) ) // error or EOA
|
||||
if( !check_ustar_chksum( header ) ) // error or EOA
|
||||
{
|
||||
if( !courier.request_mastership( i, worker_id ) ) goto done;
|
||||
if( block_is_zero( header, header_size ) ) // EOA
|
||||
|
@ -758,6 +758,6 @@ int decode_lz( const Cl_options & cl_opts, const Archive_descriptor & ad,
|
|||
courier.ocheck_counter,
|
||||
courier.owait_counter );
|
||||
|
||||
if( !courier.finished() ) internal_error( "courier not finished." );
|
||||
if( !courier.finished() ) internal_error( conofin_msg );
|
||||
return final_exit_status( retval, cl_opts.program_mode != m_diff );
|
||||
}
|
||||
|
|
11
delete.cc
11
delete.cc
|
@ -1,5 +1,5 @@
|
|||
/* Tarlz - Archiver with multimember lzip compression
|
||||
Copyright (C) 2013-2022 Antonio Diaz Diaz.
|
||||
Copyright (C) 2013-2023 Antonio Diaz Diaz.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -20,7 +20,6 @@
|
|||
#include <cctype>
|
||||
#include <cerrno>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <stdint.h> // for lzlib.h
|
||||
#include <unistd.h>
|
||||
#include <lzlib.h>
|
||||
|
@ -101,8 +100,8 @@ int delete_members( const Cl_options & cl_opts )
|
|||
if( !ad.seekable )
|
||||
{ show_file_error( ad.namep, "Archive is not seekable." ); return 1; }
|
||||
if( ad.lzip_index.file_size() < 3 * header_size )
|
||||
{ show_file_error( ad.namep, has_lz_ext( ad.name ) ? posix_lz_msg : posix_msg );
|
||||
return 2; }
|
||||
{ show_file_error( ad.namep, has_lz_ext( ad.name ) ?
|
||||
posix_lz_msg : posix_msg ); return 2; }
|
||||
// archive is uncompressed seekable, unless compressed corrupt
|
||||
|
||||
Archive_reader ar( ad ); // serial reader
|
||||
|
@ -121,7 +120,7 @@ int delete_members( const Cl_options & cl_opts )
|
|||
Tar_header header;
|
||||
if( ( retval = ar.read( header, header_size ) ) != 0 )
|
||||
{ show_file_error( ad.namep, ar.e_msg(), ar.e_code() ); break; }
|
||||
if( !verify_ustar_chksum( header ) ) // error or EOA
|
||||
if( !check_ustar_chksum( header ) ) // error or EOA
|
||||
{
|
||||
if( block_is_zero( header, header_size ) ) // EOA
|
||||
{
|
||||
|
@ -129,7 +128,7 @@ int delete_members( const Cl_options & cl_opts )
|
|||
{ show_file_error( ad.namep, fv_msg1 ); retval = 2; }
|
||||
break;
|
||||
}
|
||||
// posix format already verified by archive reader
|
||||
// posix format already checked by archive reader
|
||||
show_file_error( ad.namep, bad_hdr_msg );
|
||||
retval = 2; break;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* Tarlz - Archiver with multimember lzip compression
|
||||
Copyright (C) 2013-2022 Antonio Diaz Diaz.
|
||||
Copyright (C) 2013-2023 Antonio Diaz Diaz.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -20,7 +20,6 @@
|
|||
#include <cctype>
|
||||
#include <cerrno>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <stdint.h> // for lzlib.h
|
||||
#include <unistd.h>
|
||||
#include <lzlib.h>
|
||||
|
@ -65,7 +64,7 @@ int delete_members_lz( const Cl_options & cl_opts,
|
|||
Tar_header header;
|
||||
if( ( retval = ar.read( header, header_size ) ) != 0 )
|
||||
{ show_file_error( ad.namep, ar.e_msg(), ar.e_code() ); goto done; }
|
||||
if( !verify_ustar_chksum( header ) ) // error or EOA
|
||||
if( !check_ustar_chksum( header ) ) // error or EOA
|
||||
{
|
||||
if( block_is_zero( header, header_size ) ) // EOA
|
||||
{
|
||||
|
@ -73,7 +72,7 @@ int delete_members_lz( const Cl_options & cl_opts,
|
|||
{ show_file_error( ad.namep, fv_msg1 ); retval = 2; }
|
||||
goto done;
|
||||
}
|
||||
// indexed archive reader does not verify posix format
|
||||
// indexed archive reader does not check posix format
|
||||
show_file_error( ad.namep, ( ar.data_pos() > header_size ) ?
|
||||
bad_hdr_msg : posix_lz_msg );
|
||||
retval = 2;
|
||||
|
@ -112,7 +111,7 @@ int delete_members_lz( const Cl_options & cl_opts,
|
|||
if( !check_skip_filename( cl_opts, name_pending, extended.path().c_str() ) )
|
||||
{
|
||||
print_removed_prefix( extended.removed_prefix );
|
||||
// verify that members match
|
||||
// check that members match
|
||||
if( member_begin != ad.lzip_index.dblock( i ).pos() || !ar.at_member_end() )
|
||||
{ show_file_error( extended.path().c_str(),
|
||||
"Can't delete: not compressed individually." );
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.16.
|
||||
.TH TARLZ "1" "September 2022" "tarlz 0.23" "User Commands"
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.2.
|
||||
.TH TARLZ "1" "September 2023" "tarlz 0.24" "User Commands"
|
||||
.SH NAME
|
||||
tarlz \- creates tar archives with multimember lzip compression
|
||||
.SH SYNOPSIS
|
||||
|
@ -80,7 +80,7 @@ follow symlinks; archive the files they point to
|
|||
set number of (de)compression threads [2]
|
||||
.TP
|
||||
\fB\-o\fR, \fB\-\-output=\fR<file>
|
||||
compress to <file>
|
||||
compress to <file> ('\-' for stdout)
|
||||
.TP
|
||||
\fB\-p\fR, \fB\-\-preserve\-permissions\fR
|
||||
don't subtract the umask on extraction
|
||||
|
@ -157,7 +157,7 @@ Report bugs to lzip\-bug@nongnu.org
|
|||
.br
|
||||
Tarlz home page: http://www.nongnu.org/lzip/tarlz.html
|
||||
.SH COPYRIGHT
|
||||
Copyright \(co 2022 Antonio Diaz Diaz.
|
||||
Copyright \(co 2023 Antonio Diaz Diaz.
|
||||
Using lzlib 1.13
|
||||
License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html>
|
||||
.br
|
||||
|
|
140
doc/tarlz.info
140
doc/tarlz.info
|
@ -11,7 +11,7 @@ File: tarlz.info, Node: Top, Next: Introduction, Up: (dir)
|
|||
Tarlz Manual
|
||||
************
|
||||
|
||||
This manual is for Tarlz (version 0.23, 23 September 2022).
|
||||
This manual is for Tarlz (version 0.24, 20 September 2023).
|
||||
|
||||
* Menu:
|
||||
|
||||
|
@ -28,7 +28,7 @@ This manual is for Tarlz (version 0.23, 23 September 2022).
|
|||
* Concept index:: Index of concepts
|
||||
|
||||
|
||||
Copyright (C) 2013-2022 Antonio Diaz Diaz.
|
||||
Copyright (C) 2013-2023 Antonio Diaz Diaz.
|
||||
|
||||
This manual is free documentation: you have unlimited permission to copy,
|
||||
distribute, and modify it.
|
||||
|
@ -58,9 +58,9 @@ plzip may even double the amount of files lost for each lzip member damaged
|
|||
because it does not keep the members aligned.
|
||||
|
||||
Tarlz can create tar archives with five levels of compression
|
||||
granularity: per file (--no-solid), per block (--bsolid, default), per
|
||||
directory (--dsolid), appendable solid (--asolid), and solid (--solid). It
|
||||
can also create uncompressed tar archives.
|
||||
granularity: per file ('--no-solid'), per block ('--bsolid', default), per
|
||||
directory ('--dsolid'), appendable solid ('--asolid'), and solid
|
||||
('--solid'). It can also create uncompressed tar archives.
|
||||
|
||||
Of course, compressing each file (or each directory) individually can't
|
||||
achieve a compression ratio as high as compressing solidly the whole tar
|
||||
|
@ -87,9 +87,9 @@ archive, but it has the following advantages:
|
|||
Tarlz protects the extended records with a Cyclic Redundancy Check (CRC)
|
||||
in a way compatible with standard tar tools. *Note crc32::.
|
||||
|
||||
Tarlz does not understand other tar formats like 'gnu', 'oldgnu', 'star'
|
||||
or 'v7'. The command 'tarlz -tf archive.tar.lz > /dev/null' can be used to
|
||||
verify that the format of the archive is compatible with tarlz.
|
||||
Tarlz does not understand other tar formats like 'gnu', 'oldgnu',
|
||||
'star', or 'v7'. The command 'tarlz -t -f archive.tar.lz > /dev/null' can
|
||||
be used to check that the format of the archive is compatible with tarlz.
|
||||
|
||||
|
||||
File: tarlz.info, Node: Invoking tarlz, Next: Portable character set, Prev: Introduction, Up: Top
|
||||
|
@ -140,7 +140,7 @@ to '-1 --solid'.
|
|||
'-A'
|
||||
'--concatenate'
|
||||
Append one or more archives to the end of an archive. If no archive is
|
||||
specified with the option '-f', the input archives are concatenated to
|
||||
specified with the option '-f', concatenate the input archives to
|
||||
standard output. All the archives involved must be regular (seekable)
|
||||
files, and must be either all compressed or all uncompressed.
|
||||
Compressed and uncompressed archives can't be mixed. Compressed
|
||||
|
@ -163,7 +163,7 @@ to '-1 --solid'.
|
|||
'-d'
|
||||
'--diff'
|
||||
Compare and report differences between archive and file system. For
|
||||
each tar member in the archive, verify that the corresponding file in
|
||||
each tar member in the archive, check that the corresponding file in
|
||||
the file system exists and is of the same type (regular file,
|
||||
directory, etc). Report on standard output the differences found in
|
||||
type, mode (permissions), owner and group IDs, modification time, file
|
||||
|
@ -224,22 +224,25 @@ to '-1 --solid'.
|
|||
directory without extracting the files under it, use
|
||||
'tarlz -xf foo --exclude='dir/*' dir'. Tarlz removes files and empty
|
||||
directories unconditionally before extracting over them. Other than
|
||||
that, it will not make any special effort to extract a file over an
|
||||
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 will usually fail.
|
||||
non-empty directory usually fails.
|
||||
|
||||
'-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). 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 using a command like
|
||||
'tar -c -Hustar foo | tarlz -z -o foo.tar.lz'. Note that tarlz only
|
||||
works reliably on archives without global headers, or with global
|
||||
headers whose content can be ignored.
|
||||
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
|
||||
'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.
|
||||
|
||||
The compression is reversible, including any garbage present after the
|
||||
end-of-archive blocks. Tarlz stops parsing after the first
|
||||
|
@ -277,18 +280,18 @@ to '-1 --solid'.
|
|||
|
||||
'-C DIR'
|
||||
'--directory=DIR'
|
||||
Change to directory DIR. When creating or appending, the position of
|
||||
each '-C' option in the command line is significant; it will change the
|
||||
current working directory for the following FILES until a new '-C'
|
||||
option appears in the command line. When extracting or comparing, all
|
||||
the '-C' options are executed in sequence before reading the archive.
|
||||
Listing ignores any '-C' options specified. DIR is relative to the
|
||||
then current working directory, perhaps changed by a previous '-C'
|
||||
Change to directory DIR. When creating, appending, comparing, or
|
||||
extracting, the position of each '-C' option in the command line is
|
||||
significant; it changes the current working directory for the following
|
||||
FILES until a new '-C' option appears in the command line. '--list'
|
||||
and '--delete' ignore any '-C' options specified. DIR is relative to
|
||||
the then current working directory, perhaps changed by a previous '-C'
|
||||
option.
|
||||
|
||||
Note that a process can only have one current working directory (CWD).
|
||||
Therefore multi-threading can't be used to create an archive if a '-C'
|
||||
option appears after a relative file name in the command line.
|
||||
Therefore multi-threading can't be used to create or decode an archive
|
||||
if a '-C' option appears after a (relative) file name in the command
|
||||
line. (All file names are made relative when decoding).
|
||||
|
||||
'-f ARCHIVE'
|
||||
'--file=ARCHIVE'
|
||||
|
@ -308,8 +311,7 @@ to '-1 --solid'.
|
|||
support". A value of 0 disables threads entirely. If this option is
|
||||
not used, tarlz tries to detect the number of processors in the system
|
||||
and use it as default value. 'tarlz --help' shows the system's default
|
||||
value. See the note about multi-threaded archive creation in the
|
||||
option '-C' above.
|
||||
value. See the note about multi-threading in the option '-C' above.
|
||||
|
||||
Note that the number of usable threads is limited during compression to
|
||||
ceil( uncompressed_size / data_size ) (*note Minimum archive sizes::),
|
||||
|
@ -360,7 +362,9 @@ to '-1 --solid'.
|
|||
With '--create', don't compress the tar archive created. Create an
|
||||
uncompressed tar archive instead. With '--append', don't compress the
|
||||
new members appended to the tar archive. Compressed members can't be
|
||||
appended to an uncompressed archive, nor vice versa.
|
||||
appended to an uncompressed archive, nor vice versa. '--uncompressed'
|
||||
can be omitted if it can be deduced from the archive name. (An
|
||||
uncompressed archive name lacks a '.lz' or '.tlz' extension).
|
||||
|
||||
'--asolid'
|
||||
When creating or appending to a compressed archive, use appendable
|
||||
|
@ -438,13 +442,13 @@ to '-1 --solid'.
|
|||
happens while extracting a file, keep the partial data extracted. Use
|
||||
this option to recover as much data as possible from each damaged
|
||||
member. It is recommended to run tarlz in single-threaded mode
|
||||
(--threads=0) when using this option.
|
||||
('--threads=0') when using this option.
|
||||
|
||||
'--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 (only limited by CRC collisions). But note that a
|
||||
corrupt 'GNU.crc32' keyword, for example 'GNU.crc33', is reported as a
|
||||
corrupt 'GNU.crc32' keyword, for example 'GNU.crc30', is reported as a
|
||||
missing CRC instead of as a corrupt record. This misleading
|
||||
'Missing CRC' message is the consequence of a flaw in the POSIX pax
|
||||
format; i.e., the lack of a mandatory check sequence of the extended
|
||||
|
@ -588,6 +592,10 @@ header block are zeroed on archive creation to prevent trouble if the
|
|||
archive is read by an ustar tool, and are ignored by tarlz on archive
|
||||
extraction. *Note flawed-compat::.
|
||||
|
||||
Tarlz limits the size of the pax extended header data so that the whole
|
||||
header set (extended header + extended data + ustar header) can be read and
|
||||
decoded in a buffer of size INT_MAX.
|
||||
|
||||
The pax extended header data consists of one or more records, each of
|
||||
them constructed as follows:
|
||||
'"%d %s=%s\n", <length>, <keyword>, <value>'
|
||||
|
@ -618,11 +626,11 @@ 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, it will be a hard link. If typeflag is 2, it will
|
||||
be a symbolic link and the linkpath value will be 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.
|
||||
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.
|
||||
|
||||
'mtime'
|
||||
The signed decimal representation of the modification time of the
|
||||
|
@ -657,7 +665,7 @@ space, equal-sign, and newline.
|
|||
CRC32-C (Castagnoli) of the extended header data excluding the 8 bytes
|
||||
representing the CRC <value> itself. The <value> 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 guarante that
|
||||
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
|
||||
|
@ -843,11 +851,11 @@ to the POSIX-2:1993 standard, POSIX.1-2008 recommends selecting extended
|
|||
header field values that allow such tar to create a regular file containing
|
||||
the extended header records as data. This approach is broken because if the
|
||||
extended header is needed because of a long file name, the fields 'name'
|
||||
and 'prefix' will be unable to contain the full file name. (Some tar
|
||||
and 'prefix' are unable to contain the full file name. (Some tar
|
||||
implementations store the truncated name in the field 'name' alone,
|
||||
truncating the name to only 100 bytes instead of 256). Therefore the files
|
||||
corresponding to both the extended header and the overridden ustar header
|
||||
will be extracted using truncated file names, perhaps overwriting existing
|
||||
are extracted using truncated file names, perhaps overwriting existing
|
||||
files or directories. It may be a security risk to extract a file with a
|
||||
truncated file name.
|
||||
|
||||
|
@ -1098,11 +1106,11 @@ multimember compressed archive.
|
|||
For this to work as expected (and roughly multiply the compression speed
|
||||
by the number of available processors), the uncompressed archive must be at
|
||||
least as large as the number of worker threads times the block size (*note
|
||||
--data-size::). Else some processors will not get any data to compress, and
|
||||
compression will be proportionally slower. The maximum speed increase
|
||||
achievable on a given archive is limited by the ratio
|
||||
(uncompressed_size / data_size). For example, a tarball the size of gcc or
|
||||
linux will scale up to 10 or 14 processors at level -9.
|
||||
--data-size::). Else some processors do not get any data to compress, and
|
||||
compression is proportionally slower. The maximum speed increase achievable
|
||||
on a given archive is limited by the ratio (uncompressed_size / data_size).
|
||||
For example, a tarball the size of gcc or linux scales up to 10 or 14
|
||||
processors at level -9.
|
||||
|
||||
The following table shows the minimum uncompressed archive size needed
|
||||
for full use of N processors at a given compression level, using the default
|
||||
|
@ -1245,24 +1253,24 @@ Concept index
|
|||
Tag Table:
|
||||
Node: Top216
|
||||
Node: Introduction1210
|
||||
Node: Invoking tarlz4029
|
||||
Ref: --data-size12880
|
||||
Ref: --bsolid17192
|
||||
Node: Portable character set22788
|
||||
Node: File format23431
|
||||
Ref: key_crc3230188
|
||||
Ref: ustar-uid-gid33452
|
||||
Ref: ustar-mtime34254
|
||||
Node: Amendments to pax format36254
|
||||
Ref: crc3236963
|
||||
Ref: flawed-compat38274
|
||||
Node: Program design42364
|
||||
Node: Multi-threaded decoding46289
|
||||
Ref: mt-extraction49570
|
||||
Node: Minimum archive sizes50876
|
||||
Node: Examples53014
|
||||
Node: Problems55381
|
||||
Node: Concept index55936
|
||||
Node: Invoking tarlz4041
|
||||
Ref: --data-size13085
|
||||
Ref: --bsolid17521
|
||||
Node: Portable character set23119
|
||||
Node: File format23762
|
||||
Ref: key_crc3230703
|
||||
Ref: ustar-uid-gid33968
|
||||
Ref: ustar-mtime34770
|
||||
Node: Amendments to pax format36770
|
||||
Ref: crc3237479
|
||||
Ref: flawed-compat38790
|
||||
Node: Program design42872
|
||||
Node: Multi-threaded decoding46797
|
||||
Ref: mt-extraction50078
|
||||
Node: Minimum archive sizes51384
|
||||
Node: Examples53511
|
||||
Node: Problems55878
|
||||
Node: Concept index56433
|
||||
|
||||
End Tag Table
|
||||
|
||||
|
|
179
doc/tarlz.texi
179
doc/tarlz.texi
|
@ -6,8 +6,8 @@
|
|||
@finalout
|
||||
@c %**end of header
|
||||
|
||||
@set UPDATED 23 September 2022
|
||||
@set VERSION 0.23
|
||||
@set UPDATED 20 September 2023
|
||||
@set VERSION 0.24
|
||||
|
||||
@dircategory Archiving
|
||||
@direntry
|
||||
|
@ -50,7 +50,7 @@ This manual is for Tarlz (version @value{VERSION}, @value{UPDATED}).
|
|||
@end menu
|
||||
|
||||
@sp 1
|
||||
Copyright @copyright{} 2013-2022 Antonio Diaz Diaz.
|
||||
Copyright @copyright{} 2013-2023 Antonio Diaz Diaz.
|
||||
|
||||
This manual is free documentation: you have unlimited permission to copy,
|
||||
distribute, and modify it.
|
||||
|
@ -81,9 +81,9 @@ plzip may even double the amount of files lost for each lzip member damaged
|
|||
because it does not keep the members aligned.
|
||||
|
||||
Tarlz can create tar archives with five levels of compression granularity:
|
||||
per file (---no-solid), per block (---bsolid, default), per directory
|
||||
(---dsolid), appendable solid (---asolid), and solid (---solid). It can also
|
||||
create uncompressed tar archives.
|
||||
per file (@option{--no-solid}), per block (@option{--bsolid}, default), per
|
||||
directory (@option{--dsolid}), appendable solid (@option{--asolid}), and
|
||||
solid (@option{--solid}). It can also create uncompressed tar archives.
|
||||
|
||||
@noindent
|
||||
Of course, compressing each file (or each directory) individually can't
|
||||
|
@ -104,7 +104,7 @@ archive. Just like an uncompressed tar archive.
|
|||
It is a safe POSIX-style backup format. In case of corruption, tarlz
|
||||
can extract all the undamaged members from the tar.lz archive,
|
||||
skipping over the damaged members, just like the standard
|
||||
(uncompressed) tar. Moreover, the option @samp{--keep-damaged} can be used
|
||||
(uncompressed) tar. Moreover, the option @option{--keep-damaged} can be used
|
||||
to recover as much data as possible from each damaged member, and
|
||||
lziprecover can be used to recover some of the damaged members.
|
||||
|
||||
|
@ -118,8 +118,8 @@ Tarlz protects the extended records with a Cyclic Redundancy Check (CRC) in
|
|||
a way compatible with standard tar tools. @xref{crc32}.
|
||||
|
||||
Tarlz does not understand other tar formats like @samp{gnu}, @samp{oldgnu},
|
||||
@samp{star} or @samp{v7}. The command
|
||||
@w{@samp{tarlz -tf archive.tar.lz > /dev/null}} can be used to verify that
|
||||
@samp{star}, or @samp{v7}. The command
|
||||
@w{@samp{tarlz -t -f archive.tar.lz > /dev/null}} can be used to check that
|
||||
the format of the archive is compatible with tarlz.
|
||||
|
||||
|
||||
|
@ -137,9 +137,9 @@ tarlz @var{operation} [@var{options}] [@var{files}]
|
|||
@end example
|
||||
|
||||
@noindent
|
||||
All operations except @samp{--concatenate} and @samp{--compress} operate on
|
||||
whole trees if any @var{file} is a directory. All operations except
|
||||
@samp{--compress} overwrite output files without warning. If no archive is
|
||||
All operations except @option{--concatenate} and @option{--compress} operate
|
||||
on whole trees if any @var{file} is a directory. All operations except
|
||||
@option{--compress} overwrite output files without warning. If no archive is
|
||||
specified, tarlz tries to read it from standard input or write it to
|
||||
standard output. Tarlz refuses to read archive data from a terminal or write
|
||||
archive data to a terminal. Tarlz detects when the archive being created or
|
||||
|
@ -147,7 +147,7 @@ enlarged is among the files to be archived, appended, or concatenated, and
|
|||
skips it.
|
||||
|
||||
Tarlz does not use absolute file names nor file names above the current
|
||||
working directory (perhaps changed by option @samp{-C}). On archive creation
|
||||
working directory (perhaps changed by option @option{-C}). On archive creation
|
||||
or appending tarlz archives the files specified, but removes from member
|
||||
names any leading and trailing slashes and any file name prefixes containing
|
||||
a @samp{..} component. On extraction, leading and trailing slashes are also
|
||||
|
@ -161,9 +161,9 @@ member names in the archive or given in the command line, so that
|
|||
@w{@samp{tarlz -xf foo ./bar baz}} extracts members @samp{bar} and
|
||||
@samp{./baz} from archive @samp{foo}.
|
||||
|
||||
If several compression levels or @samp{--*solid} options are given, the last
|
||||
setting is used. For example @w{@samp{-9 --solid --uncompressed -1}} is
|
||||
equivalent to @w{@samp{-1 --solid}}.
|
||||
If several compression levels or @option{--*solid} options are given, the last
|
||||
setting is used. For example @w{@option{-9 --solid --uncompressed -1}} is
|
||||
equivalent to @w{@option{-1 --solid}}.
|
||||
|
||||
tarlz supports the following operations:
|
||||
|
||||
|
@ -179,7 +179,7 @@ This version number should be included in all bug reports.
|
|||
@item -A
|
||||
@itemx --concatenate
|
||||
Append one or more archives to the end of an archive. If no archive is
|
||||
specified with the option @samp{-f}, the input archives are concatenated to
|
||||
specified with the option @option{-f}, concatenate the input archives to
|
||||
standard output. All the archives involved must be regular (seekable) files,
|
||||
and must be either all compressed or all uncompressed. Compressed and
|
||||
uncompressed archives can't be mixed. Compressed archives must be
|
||||
|
@ -202,23 +202,23 @@ Create a new archive from @var{files}.
|
|||
@item -d
|
||||
@itemx --diff
|
||||
Compare and report differences between archive and file system. For each tar
|
||||
member in the archive, verify that the corresponding file in the file system
|
||||
member in the archive, check that the corresponding file in the file system
|
||||
exists and is of the same type (regular file, directory, etc). Report on
|
||||
standard output the differences found in type, mode (permissions), owner and
|
||||
group IDs, modification time, file size, file contents (of regular files),
|
||||
target (of symlinks) and device number (of block/character special files).
|
||||
|
||||
As tarlz removes leading slashes from member names, the option @samp{-C} may
|
||||
be used in combination with @samp{--diff} when absolute file names were used
|
||||
As tarlz removes leading slashes from member names, the option @option{-C} may
|
||||
be used in combination with @option{--diff} when absolute file names were used
|
||||
on archive creation: @w{@samp{tarlz -C / -d}}. Alternatively, tarlz may be
|
||||
run from the root directory to perform the comparison.
|
||||
|
||||
@item --delete
|
||||
Delete files and directories from an archive in place. It currently can
|
||||
delete only from uncompressed archives and from archives with files
|
||||
compressed individually (@samp{--no-solid} archives). Note that files of
|
||||
about @samp{--data-size} or larger are compressed individually even if
|
||||
@samp{--bsolid} is used, and can therefore be deleted. Tarlz takes care to
|
||||
compressed individually (@option{--no-solid} archives). Note that files of
|
||||
about @option{--data-size} or larger are compressed individually even if
|
||||
@option{--bsolid} is used, and can therefore be deleted. Tarlz takes care to
|
||||
not delete a tar member unless it is possible to do so. For example it won't
|
||||
try to delete a tar member that is not compressed individually. Even in the
|
||||
case of finding a corrupt member after having deleted some member(s), tarlz
|
||||
|
@ -261,32 +261,36 @@ Extract files from an archive. If @var{files} are given, extract only the
|
|||
directory without extracting the files under it, use
|
||||
@w{@samp{tarlz -xf foo --exclude='dir/*' dir}}. Tarlz removes files and
|
||||
empty directories unconditionally before extracting over them. Other than
|
||||
that, it will not make any special effort to extract a file over an
|
||||
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 will usually fail.
|
||||
directory usually fails.
|
||||
|
||||
@item -z
|
||||
@itemx --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). 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 @samp{--output} is used). Tarlz can be used as compressor for GNU tar
|
||||
using a command like @w{@samp{tar -c -Hustar foo | tarlz -z -o foo.tar.lz}}.
|
||||
Note that tarlz only works reliably on archives without global headers, or
|
||||
with global headers whose content can be ignored.
|
||||
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
|
||||
@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.
|
||||
|
||||
The compression is reversible, including any garbage present after the
|
||||
end-of-archive blocks. Tarlz stops parsing after the first end-of-archive
|
||||
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 non-zero garbage
|
||||
following the end-of-archive blocks. @samp{--compress} implies plzip
|
||||
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 @samp{.lz} added unless the option @samp{--output} is
|
||||
used. When @samp{--output} is used, only one input archive can be specified.
|
||||
@samp{-f} can't be used with @samp{--compress}.
|
||||
with the extension @samp{.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}.
|
||||
|
||||
@item --check-lib
|
||||
Compare the
|
||||
|
@ -314,25 +318,25 @@ tarlz supports the following
|
|||
@anchor{--data-size}
|
||||
@item -B @var{bytes}
|
||||
@itemx --data-size=@var{bytes}
|
||||
Set target size of input data blocks for the option @samp{--bsolid}.
|
||||
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 @samp{-0} where it
|
||||
value is two times the dictionary size, except for option @option{-0} where it
|
||||
defaults to @w{1 MiB}. @xref{Minimum archive sizes}.
|
||||
|
||||
@item -C @var{dir}
|
||||
@itemx --directory=@var{dir}
|
||||
Change to directory @var{dir}. When creating or appending, the position of
|
||||
each @samp{-C} option in the command line is significant; it will change the
|
||||
current working directory for the following @var{files} until a new
|
||||
@samp{-C} option appears in the command line. When extracting or comparing,
|
||||
all the @samp{-C} options are executed in sequence before reading the
|
||||
archive. Listing ignores any @samp{-C} options specified. @var{dir} is
|
||||
relative to the then current working directory, perhaps changed by a
|
||||
previous @samp{-C} option.
|
||||
Change to directory @var{dir}. When creating, appending, comparing, or
|
||||
extracting, the position of each @option{-C} option in the command line is
|
||||
significant; it changes the current working directory for the following
|
||||
@var{files} until a new @option{-C} option appears in the command line.
|
||||
@option{--list} and @option{--delete} ignore any @option{-C} options
|
||||
specified. @var{dir} is relative to the then current working directory,
|
||||
perhaps changed by a previous @option{-C} option.
|
||||
|
||||
Note that a process can only have one current working directory (CWD).
|
||||
Therefore multi-threading can't be used to create an archive if a @samp{-C}
|
||||
option appears after a relative file name in the command line.
|
||||
Therefore multi-threading can't be used to create or decode an archive if a
|
||||
@option{-C} option appears after a (relative) file name in the command line.
|
||||
(All file names are made relative when decoding).
|
||||
|
||||
@item -f @var{archive}
|
||||
@itemx --file=@var{archive}
|
||||
|
@ -351,7 +355,7 @@ Valid values range from 0 to "as many as your system can support". A value
|
|||
of 0 disables threads entirely. If this option is not used, tarlz tries to
|
||||
detect the number of processors in the system and use it as default value.
|
||||
@w{@samp{tarlz --help}} shows the system's default value. See the note about
|
||||
multi-threaded archive creation in the option @samp{-C} above.
|
||||
multi-threading in the option @option{-C} above.
|
||||
|
||||
Note that the number of usable threads is limited during compression to
|
||||
@w{ceil( uncompressed_size / data_size )} (@pxref{Minimum archive sizes}),
|
||||
|
@ -360,9 +364,9 @@ archive, which you can find by running @w{@samp{lzip -lv archive.tar.lz}}.
|
|||
|
||||
@item -o @var{file}
|
||||
@itemx --output=@var{file}
|
||||
Write the compressed output to @var{file}. @w{@samp{-o -}} writes the
|
||||
compressed output to standard output. Currently @samp{--output} only works
|
||||
with @samp{--compress}.
|
||||
Write the compressed output to @var{file}. @w{@option{-o -}} writes the
|
||||
compressed output to standard output. Currently @option{--output} only works
|
||||
with @option{--compress}.
|
||||
|
||||
@item -p
|
||||
@itemx --preserve-permissions
|
||||
|
@ -381,8 +385,8 @@ Verbosely list files processed. Further -v's (up to 4) increase the
|
|||
verbosity level.
|
||||
|
||||
@item -0 .. -9
|
||||
Set the compression level for @samp{--create}, @samp{--append}, and
|
||||
@samp{--compress}. The default compression level is @samp{-6}. Like lzip,
|
||||
Set the compression level for @option{--create}, @option{--append}, and
|
||||
@option{--compress}. The default compression level is @option{-6}. Like lzip,
|
||||
tarlz also minimizes the dictionary size of the lzip members it creates,
|
||||
reducing the amount of memory required for decompression.
|
||||
|
||||
|
@ -401,10 +405,12 @@ reducing the amount of memory required for decompression.
|
|||
@end multitable
|
||||
|
||||
@item --uncompressed
|
||||
With @samp{--create}, don't compress the tar archive created. Create an
|
||||
uncompressed tar archive instead. With @samp{--append}, don't compress the
|
||||
With @option{--create}, don't compress the tar archive created. Create an
|
||||
uncompressed tar archive instead. With @option{--append}, don't compress the
|
||||
new members appended to the tar archive. Compressed members can't be
|
||||
appended to an uncompressed archive, nor vice versa.
|
||||
appended to an uncompressed archive, nor vice versa. @option{--uncompressed}
|
||||
can be omitted if it can be deduced from the archive name. (An uncompressed
|
||||
archive name lacks a @samp{.lz} or @samp{.tlz} extension).
|
||||
|
||||
@item --asolid
|
||||
When creating or appending to a compressed archive, use appendable solid
|
||||
|
@ -447,7 +453,7 @@ appendable. No more files can be later appended to the archive. Solid
|
|||
archives can't be created nor decoded in parallel.
|
||||
|
||||
@item --anonymous
|
||||
Equivalent to @w{@samp{--owner=root --group=root}}.
|
||||
Equivalent to @w{@option{--owner=root --group=root}}.
|
||||
|
||||
@item --owner=@var{owner}
|
||||
When creating or appending, use @var{owner} for files added to the archive.
|
||||
|
@ -465,27 +471,28 @@ to match if any component of the file name matches. For example, @samp{*.o}
|
|||
matches @samp{foo.o}, @samp{foo.o/bar} and @samp{foo/bar.o}. If
|
||||
@var{pattern} contains a @samp{/}, it matches a corresponding @samp{/} in
|
||||
the file name. For example, @samp{foo/*.o} matches @samp{foo/bar.o}.
|
||||
Multiple @samp{--exclude} options can be specified.
|
||||
Multiple @option{--exclude} options can be specified.
|
||||
|
||||
@item --ignore-ids
|
||||
Make @samp{--diff} ignore differences in owner and group IDs. This option is
|
||||
useful when comparing an @samp{--anonymous} archive.
|
||||
Make @option{--diff} ignore differences in owner and group IDs. This option is
|
||||
useful when comparing an @option{--anonymous} archive.
|
||||
|
||||
@item --ignore-overflow
|
||||
Make @samp{--diff} ignore differences in mtime caused by overflow on 32-bit
|
||||
Make @option{--diff} ignore differences in mtime caused by overflow on 32-bit
|
||||
systems with a 32-bit time_t.
|
||||
|
||||
@item --keep-damaged
|
||||
Don't delete partially extracted files. If a decompression error happens
|
||||
while extracting a file, keep the partial data extracted. Use this option to
|
||||
recover as much data as possible from each damaged member. It is recommended
|
||||
to run tarlz in single-threaded mode (---threads=0) when using this option.
|
||||
to run tarlz in single-threaded mode (@option{--threads=0}) when using this
|
||||
option.
|
||||
|
||||
@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
|
||||
(only limited by CRC collisions). But note that a corrupt @samp{GNU.crc32}
|
||||
keyword, for example @samp{GNU.crc33}, is reported as a missing CRC instead
|
||||
keyword, for example @samp{GNU.crc30}, is reported as a missing CRC instead
|
||||
of as a corrupt record. This misleading @w{@samp{Missing CRC}} message is
|
||||
the consequence of a flaw in the POSIX pax format; i.e., the lack of a
|
||||
mandatory check sequence of the extended records. @xref{crc32}.
|
||||
|
@ -606,7 +613,7 @@ Zero or more blocks that contain the contents of the file.
|
|||
@end itemize
|
||||
|
||||
Each tar member must be contiguously stored in a lzip member for the
|
||||
parallel decoding operations like @samp{--list} to work. If any tar member
|
||||
parallel decoding operations like @option{--list} to work. If any tar member
|
||||
is split over two or more lzip members, the archive must be decoded
|
||||
sequentially. @xref{Multi-threaded decoding}.
|
||||
|
||||
|
@ -639,7 +646,7 @@ tar.lz
|
|||
@end verbatim
|
||||
|
||||
@ignore
|
||||
When @samp{--permissive} is used, the following violations of the
|
||||
When @option{--permissive} is used, the following violations of the
|
||||
archive format are allowed:@*
|
||||
If several extended headers precede an ustar header, only the last
|
||||
extended header takes effect. The other extended headers are ignored.
|
||||
|
@ -660,6 +667,10 @@ fields in the pax header block are zeroed on archive creation to prevent
|
|||
trouble if the archive is read by an ustar tool, and are ignored by tarlz on
|
||||
archive extraction. @xref{flawed-compat}.
|
||||
|
||||
Tarlz limits the size of the pax extended header data so that the whole
|
||||
header set (extended header + extended data + ustar header) can be read and
|
||||
decoded in a buffer of size INT_MAX.
|
||||
|
||||
The pax extended header data consists of one or more records, each of
|
||||
them constructed as follows:@*
|
||||
@w{@samp{"%d %s=%s\n", <length>, <keyword>, <value>}}
|
||||
|
@ -689,11 +700,11 @@ greater than 2_097_151 (octal 7777777). @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, it
|
||||
will be a hard link. If typeflag is 2, it will be a symbolic link and the
|
||||
linkpath value will be 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.
|
||||
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.
|
||||
|
||||
@item mtime
|
||||
The signed decimal representation of the modification time of the following
|
||||
|
@ -728,8 +739,8 @@ CRC32-C (Castagnoli) of the extended header data excluding the 8 bytes
|
|||
representing the CRC <value> itself. The <value> 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 guarante that corruption is always detected when
|
||||
using @samp{--missing-crc} (except in case of CRC collision). A CRC was
|
||||
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.
|
||||
|
@ -878,7 +889,7 @@ character.
|
|||
Tarlz creates safe archives that allow the reliable detection of invalid or
|
||||
corrupt metadata during decoding even when the integrity checking of lzip
|
||||
can't be used because the lzip members are only decompressed partially, as
|
||||
it happens in parallel @samp{--diff}, @samp{--list}, and @samp{--extract}.
|
||||
it happens in parallel @option{--diff}, @option{--list}, and @option{--extract}.
|
||||
In order to 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.
|
||||
|
@ -919,11 +930,11 @@ to the POSIX-2:1993 standard, POSIX.1-2008 recommends selecting extended
|
|||
header field values that allow such tar to create a regular file containing
|
||||
the extended header records as data. This approach is broken because if the
|
||||
extended header is needed because of a long file name, the fields
|
||||
@samp{name} and @samp{prefix} will be unable to contain the full file name.
|
||||
@samp{name} and @samp{prefix} are unable to contain the full file name.
|
||||
(Some tar implementations store the truncated name in the field @samp{name}
|
||||
alone, truncating the name to only 100 bytes instead of 256). Therefore the
|
||||
files corresponding to both the extended header and the overridden ustar
|
||||
header will be extracted using truncated file names, perhaps overwriting
|
||||
header are extracted using truncated file names, perhaps overwriting
|
||||
existing files or directories. It may be a security risk to extract a file
|
||||
with a truncated file name.
|
||||
|
||||
|
@ -1117,9 +1128,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.
|
||||
|
||||
If the files in the archive are large, multi-threaded @samp{--list} on a
|
||||
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 @samp{--list} because, in addition to using several processors,
|
||||
sequential @option{--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:
|
||||
|
||||
|
@ -1130,7 +1141,7 @@ time plzip -cd silesia.tar.lz | tar -tf - (3.256s)
|
|||
time tarlz -tf silesia.tar.lz (0.020s)
|
||||
@end example
|
||||
|
||||
On the other hand, multi-threaded @samp{--list} won't detect corruption in
|
||||
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.
|
||||
|
@ -1176,11 +1187,11 @@ multimember compressed archive.
|
|||
For this to work as expected (and roughly multiply the compression speed by
|
||||
the number of available processors), the uncompressed archive must be at
|
||||
least as large as the number of worker threads times the block size
|
||||
(@pxref{--data-size}). Else some processors will not get any data to
|
||||
compress, and compression will be proportionally slower. The maximum speed
|
||||
increase achievable on a given archive is limited by the ratio
|
||||
(@pxref{--data-size}). Else some processors do not get any data to compress,
|
||||
and compression is proportionally slower. The maximum speed increase
|
||||
achievable on a given archive is limited by the ratio
|
||||
@w{(uncompressed_size / data_size)}. For example, a tarball the size of gcc
|
||||
or linux will scale up to 10 or 14 processors at level -9.
|
||||
or linux scales up to 10 or 14 processors at level -9.
|
||||
|
||||
The following table shows the minimum uncompressed archive size needed for
|
||||
full use of N processors at a given compression level, using the default
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* Tarlz - Archiver with multimember lzip compression
|
||||
Copyright (C) 2013-2022 Antonio Diaz Diaz.
|
||||
Copyright (C) 2013-2023 Antonio Diaz Diaz.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -17,7 +17,6 @@
|
|||
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
|
||||
#include <cstdlib>
|
||||
#include <fnmatch.h>
|
||||
|
||||
#include "tarlz.h"
|
||||
|
|
139
extended.cc
139
extended.cc
|
@ -1,5 +1,5 @@
|
|||
/* Tarlz - Archiver with multimember lzip compression
|
||||
Copyright (C) 2013-2022 Antonio Diaz Diaz.
|
||||
Copyright (C) 2013-2023 Antonio Diaz Diaz.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -20,7 +20,6 @@
|
|||
#include <cctype>
|
||||
#include <cerrno>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "tarlz.h"
|
||||
|
||||
|
@ -30,34 +29,31 @@ const CRC32 crc32c( true );
|
|||
|
||||
namespace {
|
||||
|
||||
unsigned long long record_size( const unsigned keyword_size,
|
||||
const unsigned long value_size )
|
||||
unsigned record_size( const unsigned keyword_size, const unsigned value_size )
|
||||
{
|
||||
/* length + ' ' + keyword + '=' + value + '\n'
|
||||
minimize length; prefer "99<97_bytes>" to "100<97_bytes>" */
|
||||
unsigned long long size = 1 + keyword_size + 1 + value_size + 1;
|
||||
unsigned size = 1 + keyword_size + 1 + value_size + 1;
|
||||
size += decimal_digits( decimal_digits( size ) + size );
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
unsigned long long parse_decimal( const char * const ptr,
|
||||
const char ** const tailp,
|
||||
const unsigned long long size )
|
||||
long long parse_decimal( const char * const ptr, const char ** const tailp,
|
||||
const int size, const unsigned long long limit = LLONG_MAX )
|
||||
{
|
||||
unsigned long long result = 0;
|
||||
unsigned long long i = 0;
|
||||
int i = 0;
|
||||
while( i < size && std::isspace( (unsigned char)ptr[i] ) ) ++i;
|
||||
if( !std::isdigit( (unsigned char)ptr[i] ) )
|
||||
{ if( tailp ) *tailp = ptr; return 0; }
|
||||
if( !std::isdigit( (unsigned char)ptr[i] ) ) { *tailp = ptr; return -1; }
|
||||
for( ; i < size && std::isdigit( (unsigned char)ptr[i] ); ++i )
|
||||
{
|
||||
const unsigned long long prev = result;
|
||||
result *= 10; result += ptr[i] - '0';
|
||||
if( result < prev || result > LLONG_MAX ) // overflow
|
||||
{ if( tailp ) *tailp = ptr; return 0; }
|
||||
if( result < prev || result > limit || result > LLONG_MAX ) // overflow
|
||||
{ *tailp = ptr; return -1; }
|
||||
}
|
||||
if( tailp ) *tailp = ptr + i;
|
||||
*tailp = ptr + i;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -77,7 +73,7 @@ uint32_t parse_record_crc( const char * const ptr )
|
|||
}
|
||||
|
||||
|
||||
unsigned char xdigit( const unsigned value )
|
||||
unsigned char xdigit( const unsigned value ) // hex digit for 'value'
|
||||
{
|
||||
if( value <= 9 ) return '0' + value;
|
||||
if( value <= 15 ) return 'A' + value - 10;
|
||||
|
@ -90,27 +86,26 @@ void print_hex( char * const buf, int size, unsigned long long num )
|
|||
void print_decimal( char * const buf, int size, unsigned long long num )
|
||||
{ while( --size >= 0 ) { buf[size] = num % 10 + '0'; num /= 10; } }
|
||||
|
||||
unsigned long long print_size_keyword( char * const buf,
|
||||
const unsigned long long size, const char * keyword )
|
||||
int print_size_keyword( char * const buf, const int size, const char * keyword )
|
||||
{
|
||||
// "size keyword=value\n"
|
||||
unsigned long long pos = decimal_digits( size );
|
||||
int pos = decimal_digits( size );
|
||||
print_decimal( buf, pos, size ); buf[pos++] = ' ';
|
||||
while( *keyword ) { buf[pos++] = *keyword; ++keyword; } buf[pos++] = '=';
|
||||
return pos;
|
||||
}
|
||||
|
||||
bool print_record( char * const buf, const unsigned long long size,
|
||||
const char * keyword, const std::string & value )
|
||||
bool print_record( char * const buf, const int size, const char * keyword,
|
||||
const std::string & value )
|
||||
{
|
||||
unsigned long long pos = print_size_keyword( buf, size, keyword );
|
||||
int pos = print_size_keyword( buf, size, keyword );
|
||||
std::memcpy( buf + pos, value.c_str(), value.size() );
|
||||
pos += value.size(); buf[pos++] = '\n';
|
||||
return pos == size;
|
||||
}
|
||||
|
||||
bool print_record( char * const buf, const int size,
|
||||
const char * keyword, const unsigned long long value )
|
||||
bool print_record( char * const buf, const int size, const char * keyword,
|
||||
const unsigned long long value )
|
||||
{
|
||||
int pos = print_size_keyword( buf, size, keyword );
|
||||
const int vd = decimal_digits( value );
|
||||
|
@ -118,8 +113,8 @@ bool print_record( char * const buf, const int size,
|
|||
return pos == size;
|
||||
}
|
||||
|
||||
bool print_record( char * const buf, const int size,
|
||||
const char * keyword, const Etime & value )
|
||||
bool print_record( char * const buf, const int size, const char * keyword,
|
||||
const Etime & value )
|
||||
{
|
||||
int pos = print_size_keyword( buf, size, keyword );
|
||||
pos += value.print( buf + pos ); buf[pos++] = '\n';
|
||||
|
@ -154,12 +149,12 @@ unsigned Etime::print( char * const buf ) const
|
|||
}
|
||||
|
||||
bool Etime::parse( const char * const ptr, const char ** const tailp,
|
||||
const long long size )
|
||||
const int size )
|
||||
{
|
||||
char * tail;
|
||||
errno = 0;
|
||||
long long s = strtoll( ptr, &tail, 10 );
|
||||
if( tail == ptr || errno ||
|
||||
if( tail == ptr || tail - ptr > size || errno ||
|
||||
( *tail != 0 && *tail != '\n' && *tail != '.' ) ) return false;
|
||||
int ns = 0;
|
||||
if( *tail == '.' ) // parse nanoseconds and any extra digits
|
||||
|
@ -182,6 +177,8 @@ const std::string Extended::crc_record( "22 GNU.crc32=00000000\n" );
|
|||
|
||||
void Extended::calculate_sizes() const
|
||||
{
|
||||
if( linkpath_.size() > max_edata_size || path_.size() > max_edata_size )
|
||||
{ full_size_ = -3; return; }
|
||||
linkpath_recsize_ = linkpath_.size() ? record_size( 8, linkpath_.size() ) : 0;
|
||||
path_recsize_ = path_.size() ? record_size( 4, path_.size() ) : 0;
|
||||
file_size_recsize_ =
|
||||
|
@ -192,19 +189,21 @@ void Extended::calculate_sizes() const
|
|||
atime_.out_of_ustar_range() ? record_size( 5, atime_.decimal_size() ) : 0;
|
||||
mtime_recsize_ =
|
||||
mtime_.out_of_ustar_range() ? record_size( 5, mtime_.decimal_size() ) : 0;
|
||||
edsize_ = linkpath_recsize_ + path_recsize_ + file_size_recsize_ +
|
||||
uid_recsize_ + gid_recsize_ + atime_recsize_ + mtime_recsize_ +
|
||||
crc_record.size();
|
||||
const long long tmp = linkpath_recsize_ + path_recsize_ +
|
||||
file_size_recsize_ + uid_recsize_ + gid_recsize_ +
|
||||
atime_recsize_ + mtime_recsize_ + crc_record.size();
|
||||
if( tmp > max_edata_size ) { full_size_ = -3; return; }
|
||||
edsize_ = tmp;
|
||||
padded_edsize_ = round_up( edsize_ );
|
||||
if( padded_edsize_ > max_edata_size ) { full_size_ = -3; return; }
|
||||
full_size_ = header_size + padded_edsize_;
|
||||
}
|
||||
|
||||
|
||||
// print a diagnostic for each unknown keyword once per keyword
|
||||
void Extended::unknown_keyword( const char * const buf,
|
||||
const unsigned long long size ) const
|
||||
void Extended::unknown_keyword( const char * const buf, const int size ) const
|
||||
{
|
||||
unsigned long long eq_pos = 0; // position of '=' in buf
|
||||
int eq_pos = 0; // position of '=' in buf
|
||||
while( eq_pos < size && buf[eq_pos] != '=' ) ++eq_pos;
|
||||
const std::string keyword( buf, eq_pos );
|
||||
for( unsigned i = 0; i < unknown_keywords.size(); ++i )
|
||||
|
@ -215,13 +214,12 @@ void Extended::unknown_keyword( const char * const buf,
|
|||
}
|
||||
|
||||
|
||||
// Return the size of the extended block, -1 if error, -2 if out of memory.
|
||||
long long Extended::format_block( Resizable_buffer & rbuf ) const
|
||||
/* Return the size of the extended block, or 0 if empty.
|
||||
Return -1 if error, -2 if out of memory, -3 if block too long. */
|
||||
int Extended::format_block( Resizable_buffer & rbuf ) const
|
||||
{
|
||||
if( empty() ) return 0; // no extended data
|
||||
const unsigned long long bufsize = full_size(); // recalculate sizes
|
||||
if( edsize_ <= 0 ) return 0; // no extended data
|
||||
if( edsize_ >= 1LL << 33 ) return -1; // too much extended data
|
||||
const int bufsize = full_size(); // recalculate sizes if needed
|
||||
if( bufsize <= 0 ) return bufsize; // error or no extended data
|
||||
if( !rbuf.resize( bufsize ) ) return -2; // extended block buffer
|
||||
uint8_t * const header = rbuf.u8(); // extended header
|
||||
char * const buf = rbuf() + header_size; // extended records
|
||||
|
@ -232,7 +230,7 @@ long long Extended::format_block( Resizable_buffer & rbuf ) const
|
|||
|
||||
if( path_recsize_ && !print_record( buf, path_recsize_, "path", path_ ) )
|
||||
return -1;
|
||||
long long pos = path_recsize_;
|
||||
int pos = path_recsize_;
|
||||
if( linkpath_recsize_ &&
|
||||
!print_record( buf + pos, linkpath_recsize_, "linkpath", linkpath_ ) )
|
||||
return -1;
|
||||
|
@ -268,24 +266,37 @@ long long Extended::format_block( Resizable_buffer & rbuf ) const
|
|||
}
|
||||
|
||||
|
||||
bool Extended::parse( const char * const buf, const unsigned long long edsize,
|
||||
const char * Extended::full_size_error() const
|
||||
{
|
||||
const char * const eferec_msg = "Error formatting extended records.";
|
||||
switch( full_size_ )
|
||||
{
|
||||
case -1: return eferec_msg;
|
||||
case -2: return mem_msg2;
|
||||
case -3: return longrec_msg;
|
||||
default: internal_error( "invalid call to full_size_error." );
|
||||
return 0; // keep compiler quiet
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool Extended::parse( const char * const buf, const int edsize,
|
||||
const bool permissive )
|
||||
{
|
||||
reset(); full_size_ = -1; // invalidate cached sizes
|
||||
for( unsigned long long pos = 0; pos < edsize; ) // parse records
|
||||
reset(); full_size_ = -4; // invalidate cached sizes
|
||||
for( int pos = 0; pos < edsize; ) // parse records
|
||||
{
|
||||
const char * tail;
|
||||
const unsigned long long rsize =
|
||||
parse_decimal( buf + pos, &tail, edsize - pos );
|
||||
if( rsize == 0 || rsize > edsize - pos ||
|
||||
tail[0] != ' ' || buf[pos+rsize-1] != '\n' ) return false;
|
||||
const int rsize =
|
||||
parse_decimal( buf + pos, &tail, edsize - pos, edsize - pos );
|
||||
if( rsize <= 0 || tail[0] != ' ' || buf[pos+rsize-1] != '\n' ) return false;
|
||||
++tail; // point to keyword
|
||||
// rest = length of (keyword + '=' + value) without the final newline
|
||||
const unsigned long long rest = ( buf + ( pos + rsize - 1 ) ) - tail;
|
||||
const int rest = ( buf + ( pos + rsize - 1 ) ) - tail;
|
||||
if( rest > 5 && std::memcmp( tail, "path=", 5 ) == 0 )
|
||||
{
|
||||
if( path_.size() && !permissive ) return false;
|
||||
unsigned long long len = rest - 5;
|
||||
int len = rest - 5;
|
||||
while( len > 1 && tail[5+len-1] == '/' ) --len; // trailing '/'
|
||||
path_.assign( tail + 5, len );
|
||||
// this also truncates path_ at the first embedded null character
|
||||
|
@ -294,30 +305,30 @@ bool Extended::parse( const char * const buf, const unsigned long long edsize,
|
|||
else if( rest > 9 && std::memcmp( tail, "linkpath=", 9 ) == 0 )
|
||||
{
|
||||
if( linkpath_.size() && !permissive ) return false;
|
||||
unsigned long long len = rest - 9;
|
||||
int len = rest - 9;
|
||||
while( len > 1 && tail[9+len-1] == '/' ) --len; // trailing '/'
|
||||
linkpath_.assign( tail + 9, len );
|
||||
}
|
||||
else if( rest > 5 && std::memcmp( tail, "size=", 5 ) == 0 )
|
||||
{
|
||||
if( file_size_ != 0 && !permissive ) return false;
|
||||
file_size_ = parse_decimal( tail + 5, &tail, rest - 5 );
|
||||
// parse error or size fits in ustar header
|
||||
if( file_size_ < 1LL << 33 || file_size_ > max_file_size ||
|
||||
tail != buf + ( pos + rsize - 1 ) ) return false;
|
||||
file_size_ = parse_decimal( tail + 5, &tail, rest - 5, max_file_size );
|
||||
// overflow, parse error, or size fits in ustar header
|
||||
if( file_size_ < 1LL << 33 || tail != buf + ( pos + rsize - 1 ) )
|
||||
return false;
|
||||
}
|
||||
else if( rest > 4 && std::memcmp( tail, "uid=", 4 ) == 0 )
|
||||
{
|
||||
if( uid_ >= 0 && !permissive ) return false;
|
||||
uid_ = parse_decimal( tail + 4, &tail, rest - 4 );
|
||||
// parse error or uid fits in ustar header
|
||||
// overflow, parse error, or uid fits in ustar header
|
||||
if( uid_ < 1 << 21 || tail != buf + ( pos + rsize - 1 ) ) return false;
|
||||
}
|
||||
else if( rest > 4 && std::memcmp( tail, "gid=", 4 ) == 0 )
|
||||
{
|
||||
if( gid_ >= 0 && !permissive ) return false;
|
||||
gid_ = parse_decimal( tail + 4, &tail, rest - 4 );
|
||||
// parse error or gid fits in ustar header
|
||||
// overflow, parse error, or gid fits in ustar header
|
||||
if( gid_ < 1 << 21 || tail != buf + ( pos + rsize - 1 ) ) return false;
|
||||
}
|
||||
else if( rest > 6 && std::memcmp( tail, "atime=", 6 ) == 0 )
|
||||
|
@ -335,7 +346,7 @@ bool Extended::parse( const char * const buf, const unsigned long long edsize,
|
|||
else if( rest > 10 && std::memcmp( tail, "GNU.crc32=", 10 ) == 0 )
|
||||
{
|
||||
if( crc_present_ && !permissive ) return false;
|
||||
if( rsize != crc_record.size() ) 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 =
|
||||
|
@ -367,7 +378,7 @@ void Extended::fill_from_ustar( const Tar_header header )
|
|||
if( len > 0 )
|
||||
{
|
||||
linkpath_.assign( (const char *)header + linkname_o, len );
|
||||
full_size_ = -1;
|
||||
full_size_ = -4;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -399,17 +410,13 @@ void Extended::fill_from_ustar( const Tar_header header )
|
|||
|
||||
|
||||
/* Return file size from record or from ustar header, and reset file_size_.
|
||||
Used for fast parsing of headers in uncompressed archives.
|
||||
*/
|
||||
Used for fast parsing of headers in uncompressed archives. */
|
||||
long long Extended::get_file_size_and_reset( const Tar_header header )
|
||||
{
|
||||
const long long tmp = file_size_;
|
||||
file_size( 0 ); // reset full_size_
|
||||
const Typeflag typeflag = (Typeflag)header[typeflag_o];
|
||||
if( typeflag == tf_regular || typeflag == tf_hiperf )
|
||||
{
|
||||
if( tmp == 0 ) return parse_octal( header + size_o, size_l );
|
||||
else return tmp;
|
||||
}
|
||||
return 0;
|
||||
if( typeflag != tf_regular && typeflag != tf_hiperf ) return 0;
|
||||
if( tmp > 0 ) return tmp;
|
||||
return parse_octal( header + size_o, size_l );
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* Tarlz - Archiver with multimember lzip compression
|
||||
Copyright (C) 2013-2022 Antonio Diaz Diaz.
|
||||
Copyright (C) 2013-2023 Antonio Diaz Diaz.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -50,10 +50,10 @@ const char * bad_version( const unsigned version )
|
|||
bool Lzip_index::check_header_error( const Lzip_header & header,
|
||||
const bool first )
|
||||
{
|
||||
if( !header.verify_magic() )
|
||||
if( !header.check_magic() )
|
||||
{ error_ = bad_magic_msg; retval_ = 2; if( first ) bad_magic_ = true;
|
||||
return true; }
|
||||
if( !header.verify_version() )
|
||||
if( !header.check_version() )
|
||||
{ error_ = bad_version( header.version() ); retval_ = 2; return true; }
|
||||
if( !isvalid_ds( header.dictionary_size() ) )
|
||||
{ error_ = bad_dict_msg; retval_ = 2; return true; }
|
||||
|
@ -112,21 +112,21 @@ bool Lzip_index::skip_trailing_data( const int fd, unsigned long long & pos,
|
|||
const unsigned long long member_size = trailer.member_size();
|
||||
if( member_size == 0 ) // skip trailing zeros
|
||||
{ while( i > Lzip_trailer::size && buffer[i-9] == 0 ) --i; continue; }
|
||||
if( member_size > ipos + i || !trailer.verify_consistency() )
|
||||
if( member_size > ipos + i || !trailer.check_consistency() )
|
||||
continue;
|
||||
Lzip_header header;
|
||||
if( !read_header( fd, header, ipos + i - member_size ) ) return false;
|
||||
if( !header.verify() ) continue;
|
||||
if( !header.check() ) continue;
|
||||
const Lzip_header & header2 = *(const Lzip_header *)( buffer + i );
|
||||
const bool full_h2 = bsize - i >= Lzip_header::size;
|
||||
if( header2.verify_prefix( bsize - i ) ) // last member
|
||||
if( header2.check_prefix( bsize - i ) ) // last member
|
||||
{
|
||||
if( !full_h2 ) error_ = "Last member in input file is truncated.";
|
||||
else if( !check_header_error( header2, false ) )
|
||||
error_ = "Last member in input file is truncated or corrupt.";
|
||||
retval_ = 2; return false;
|
||||
}
|
||||
if( !loose_trailing && full_h2 && header2.verify_corrupt() )
|
||||
if( !loose_trailing && full_h2 && header2.check_corrupt() )
|
||||
{ error_ = corrupt_mm_msg; retval_ = 2; return false; }
|
||||
if( !ignore_trailing )
|
||||
{ error_ = trailing_msg; retval_ = 2; return false; }
|
||||
|
@ -175,7 +175,7 @@ Lzip_index::Lzip_index( const int infd, const bool ignore_trailing,
|
|||
pos - Lzip_trailer::size ) != Lzip_trailer::size )
|
||||
{ set_errno_error( "Error reading member trailer: " ); break; }
|
||||
const unsigned long long member_size = trailer.member_size();
|
||||
if( member_size > pos || !trailer.verify_consistency() ) // bad trailer
|
||||
if( member_size > pos || !trailer.check_consistency() ) // bad trailer
|
||||
{
|
||||
if( member_vector.empty() )
|
||||
{ if( skip_trailing_data( infd, pos, ignore_trailing, loose_trailing ) )
|
||||
|
@ -184,7 +184,7 @@ Lzip_index::Lzip_index( const int infd, const bool ignore_trailing,
|
|||
break;
|
||||
}
|
||||
if( !read_header( infd, header, pos - member_size ) ) break;
|
||||
if( !header.verify() ) // bad header
|
||||
if( !header.check() ) // bad header
|
||||
{
|
||||
if( member_vector.empty() )
|
||||
{ if( skip_trailing_data( infd, pos, ignore_trailing, loose_trailing ) )
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* Tarlz - Archiver with multimember lzip compression
|
||||
Copyright (C) 2013-2022 Antonio Diaz Diaz.
|
||||
Copyright (C) 2013-2023 Antonio Diaz Diaz.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
|
58
main.cc
58
main.cc
|
@ -1,5 +1,5 @@
|
|||
/* Tarlz - Archiver with multimember lzip compression
|
||||
Copyright (C) 2013-2022 Antonio Diaz Diaz.
|
||||
Copyright (C) 2013-2023 Antonio Diaz Diaz.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -27,10 +27,9 @@
|
|||
#include <cerrno>
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <ctime>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <pthread.h> // for pthread_t
|
||||
#include <stdint.h> // for lzlib.h
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
|
@ -57,7 +56,7 @@ const char * const program_name = "tarlz";
|
|||
|
||||
namespace {
|
||||
|
||||
const char * const program_year = "2022";
|
||||
const char * const program_year = "2023";
|
||||
const char * invocation_name = program_name; // default value
|
||||
|
||||
|
||||
|
@ -101,7 +100,7 @@ void show_help( const long num_online )
|
|||
" -f, --file=<archive> use archive file <archive>\n"
|
||||
" -h, --dereference follow symlinks; archive the files they point to\n"
|
||||
" -n, --threads=<n> set number of (de)compression threads [%ld]\n"
|
||||
" -o, --output=<file> compress to <file>\n"
|
||||
" -o, --output=<file> compress to <file> ('-' for stdout)\n"
|
||||
" -p, --preserve-permissions don't subtract the umask on extraction\n"
|
||||
" -q, --quiet suppress all messages\n"
|
||||
" -v, --verbose verbosely list files processed\n"
|
||||
|
@ -209,11 +208,11 @@ int check_lib()
|
|||
}
|
||||
|
||||
|
||||
// separate numbers of 6 or more digits in groups of 3 digits using '_'
|
||||
// separate numbers of 5 or more digits in groups of 3 digits using '_'
|
||||
const char * format_num3( long long num )
|
||||
{
|
||||
const char * const si_prefix = "kMGTPEZY";
|
||||
const char * const binary_prefix = "KMGTPEZY";
|
||||
const char * const si_prefix = "kMGTPEZYRQ";
|
||||
const char * const binary_prefix = "KMGTPEZYRQ";
|
||||
enum { buffers = 8, bufsize = 4 * sizeof num };
|
||||
static char buffer[buffers][bufsize]; // circle of static buffers for printf
|
||||
static int current = 0;
|
||||
|
@ -230,12 +229,12 @@ const char * format_num3( long long num )
|
|||
for( int i = 0; i < 8 && num != 0 && ( num / 1000 ) * 1000 == num; ++i )
|
||||
{ num /= 1000; prefix = si_prefix[i]; }
|
||||
if( prefix ) *(--p) = prefix;
|
||||
const bool split = num >= 100000 || num <= -100000;
|
||||
const bool split = num >= 10000 || num <= -10000;
|
||||
|
||||
for( int i = 0; ; )
|
||||
{
|
||||
long long onum = num; num /= 10;
|
||||
*(--p) = llabs( onum - 10 * num ) + '0'; if( num == 0 ) break;
|
||||
const long long onum = num; num /= 10;
|
||||
*(--p) = llabs( onum - ( 10 * num ) ) + '0'; if( num == 0 ) break;
|
||||
if( split && ++i >= 3 ) { i = 0; *(--p) = '_'; }
|
||||
}
|
||||
if( negative ) *(--p) = '-';
|
||||
|
@ -252,6 +251,8 @@ void show_option_error( const char * const arg, const char * const msg,
|
|||
}
|
||||
|
||||
|
||||
// Recognized formats: <num>[QRYZEPTGM][i], <num>k, <num>Ki
|
||||
//
|
||||
long long getnum( const char * const arg, const char * const option_name,
|
||||
const long long llimit = LLONG_MIN,
|
||||
const long long ulimit = LLONG_MAX )
|
||||
|
@ -269,6 +270,8 @@ long long getnum( const char * const arg, const char * const option_name,
|
|||
int exponent = 0; // 0 = bad multiplier
|
||||
switch( tail[0] )
|
||||
{
|
||||
case 'Q': exponent = 10; break;
|
||||
case 'R': exponent = 9; break;
|
||||
case 'Y': exponent = 8; break;
|
||||
case 'Z': exponent = 7; break;
|
||||
case 'E': exponent = 6; break;
|
||||
|
@ -327,7 +330,7 @@ void set_mode( Program_mode & program_mode, const Program_mode new_mode )
|
|||
// parse time as 'long long' even if time_t is 32-bit
|
||||
long long parse_mtime( const char * arg, const char * const pn )
|
||||
{
|
||||
if( *arg == '@' ) return getnum( arg + 1, pn );
|
||||
if( *arg == '@' ) return getnum( arg + 1, pn ); // seconds since the epoch
|
||||
else if( *arg == '.' || *arg == '/' )
|
||||
{
|
||||
struct stat st;
|
||||
|
@ -389,6 +392,10 @@ int hstat( const char * const filename, struct stat * const st,
|
|||
{ return dereference ? stat( filename, st ) : lstat( filename, st ); }
|
||||
|
||||
|
||||
bool nonempty_arg( const Arg_parser & parser, const int i )
|
||||
{ return ( parser.code( i ) == 0 && !parser.argument( i ).empty() ); }
|
||||
|
||||
|
||||
int open_instream( const std::string & name )
|
||||
{
|
||||
const int infd = open( name.c_str(), O_RDONLY | O_BINARY );
|
||||
|
@ -422,20 +429,6 @@ int open_outstream( const std::string & name, const bool create,
|
|||
}
|
||||
|
||||
|
||||
/* This can be called from any thread, main thread or sub-threads alike,
|
||||
since they all call common helper functions that call exit_fail_mt()
|
||||
in case of an error.
|
||||
*/
|
||||
void exit_fail_mt( const int retval )
|
||||
{
|
||||
// calling 'exit' more than once results in undefined behavior
|
||||
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
pthread_mutex_lock( &mutex ); // ignore errors to avoid loop
|
||||
std::exit( retval );
|
||||
}
|
||||
|
||||
|
||||
void show_error( const char * const msg, const int errcode, const bool help )
|
||||
{
|
||||
if( verbosity < 0 ) return;
|
||||
|
@ -617,7 +610,7 @@ int main( const int argc, const char * const argv[] )
|
|||
{
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9':
|
||||
cl_opts.level = code - '0'; break;
|
||||
cl_opts.set_level( code - '0' ); break;
|
||||
case 'A': set_mode( cl_opts.program_mode, m_concatenate ); break;
|
||||
case 'B': cl_opts.data_size =
|
||||
getnum( arg, pn, min_data_size, max_data_size ); break;
|
||||
|
@ -659,7 +652,7 @@ int main( const int argc, const char * const argv[] )
|
|||
case opt_own: cl_opts.uid = parse_owner( arg, pn ); break;
|
||||
case opt_per: cl_opts.permissive = true; break;
|
||||
case opt_sol: cl_opts.solidity = solid; break;
|
||||
case opt_un: cl_opts.level = -1; break;
|
||||
case opt_un: cl_opts.set_level( -1 ); break;
|
||||
case opt_wn: cl_opts.warn_newer = true; break;
|
||||
default : internal_error( "uncaught option" );
|
||||
}
|
||||
|
@ -679,8 +672,7 @@ int main( const int argc, const char * const argv[] )
|
|||
program_name, f_pn, z_pn );
|
||||
return 1;
|
||||
}
|
||||
if( cl_opts.program_mode == m_compress &&
|
||||
( cl_opts.level < 0 || cl_opts.level > 9 ) )
|
||||
if( cl_opts.program_mode == m_compress && cl_opts.uncompressed() )
|
||||
{
|
||||
if( verbosity >= 0 )
|
||||
std::fprintf( stderr, "%s: Option '--uncompressed' can't be used with '%s'.\n",
|
||||
|
@ -700,7 +692,7 @@ int main( const int argc, const char * const argv[] )
|
|||
setmode( STDOUT_FILENO, O_BINARY );
|
||||
#endif
|
||||
|
||||
if( cl_opts.data_size <= 0 && cl_opts.level >= 0 && cl_opts.level <= 9 )
|
||||
if( cl_opts.data_size <= 0 && !cl_opts.uncompressed() )
|
||||
{
|
||||
if( cl_opts.level == 0 ) cl_opts.data_size = 1 << 20;
|
||||
else cl_opts.data_size = 2 * option_mapping[cl_opts.level].dictionary_size;
|
||||
|
@ -715,9 +707,9 @@ int main( const int argc, const char * const argv[] )
|
|||
case m_create: return encode( cl_opts );
|
||||
case m_compress: return compress( cl_opts );
|
||||
case m_concatenate: return concatenate( cl_opts );
|
||||
case m_delete: return delete_members( cl_opts );
|
||||
case m_delete: tzset(); return delete_members( cl_opts );
|
||||
case m_diff:
|
||||
case m_extract:
|
||||
case m_list: return decode( cl_opts );
|
||||
case m_list: tzset(); return decode( cl_opts );
|
||||
}
|
||||
}
|
||||
|
|
112
tarlz.h
112
tarlz.h
|
@ -1,5 +1,5 @@
|
|||
/* Tarlz - Archiver with multimember lzip compression
|
||||
Copyright (C) 2013-2022 Antonio Diaz Diaz.
|
||||
Copyright (C) 2013-2023 Antonio Diaz Diaz.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -16,14 +16,15 @@
|
|||
*/
|
||||
|
||||
#include <climits>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#define max_file_size ( LLONG_MAX - header_size )
|
||||
enum { header_size = 512 };
|
||||
enum { header_size = 512,
|
||||
max_edata_size = ( INT_MAX / header_size - 2 ) * header_size };
|
||||
typedef uint8_t Tar_header[header_size];
|
||||
|
||||
enum Offsets {
|
||||
|
@ -46,7 +47,7 @@ enum Typeflag {
|
|||
const uint8_t ustar_magic[magic_l] =
|
||||
{ 0x75, 0x73, 0x74, 0x61, 0x72, 0 }; // "ustar\0"
|
||||
|
||||
inline bool verify_ustar_magic( const Tar_header header )
|
||||
inline bool check_ustar_magic( const Tar_header header )
|
||||
{ return std::memcmp( header + magic_o, ustar_magic, magic_l ) == 0; }
|
||||
|
||||
inline void init_tar_header( Tar_header header ) // set magic and version
|
||||
|
@ -154,7 +155,7 @@ public:
|
|||
unsigned decimal_size() const;
|
||||
unsigned print( char * const buf ) const;
|
||||
bool parse( const char * const ptr, const char ** const tailp,
|
||||
const long long size );
|
||||
const int size );
|
||||
};
|
||||
|
||||
|
||||
|
@ -167,12 +168,12 @@ class Extended // stores metadata from/for extended records
|
|||
long long uid_, gid_; // may not fit in unsigned int
|
||||
Etime atime_, mtime_;
|
||||
|
||||
// cached sizes; if full_size_ < 0 they must be recalculated
|
||||
mutable long long edsize_; // extended data size
|
||||
mutable long long padded_edsize_; // edsize rounded up
|
||||
mutable long long full_size_; // header + padded edsize
|
||||
mutable long long linkpath_recsize_;
|
||||
mutable long long path_recsize_;
|
||||
// cached sizes; if full_size_ <= -4 they must be recalculated
|
||||
mutable int edsize_; // extended data size
|
||||
mutable int padded_edsize_; // edsize rounded up
|
||||
mutable int full_size_; // header + padded edsize
|
||||
mutable int linkpath_recsize_;
|
||||
mutable int path_recsize_;
|
||||
mutable int file_size_recsize_;
|
||||
mutable int uid_recsize_;
|
||||
mutable int gid_recsize_;
|
||||
|
@ -183,8 +184,7 @@ class Extended // stores metadata from/for extended records
|
|||
mutable bool crc_present_;
|
||||
|
||||
void calculate_sizes() const;
|
||||
void unknown_keyword( const char * const buf,
|
||||
const unsigned long long size ) const;
|
||||
void unknown_keyword( const char * const buf, const int size ) const;
|
||||
|
||||
public:
|
||||
static const std::string crc_record;
|
||||
|
@ -205,11 +205,6 @@ public:
|
|||
atime_recsize_ = 0; mtime_recsize_ = 0; crc_present_ = false;
|
||||
removed_prefix.clear(); }
|
||||
|
||||
bool empty() const
|
||||
{ return linkpath_.empty() && path_.empty() && file_size_ == 0 &&
|
||||
uid_ < 0 && gid_ < 0 &&
|
||||
!atime_.out_of_ustar_range() && !mtime_.out_of_ustar_range(); }
|
||||
|
||||
const std::string & linkpath() const { return linkpath_; }
|
||||
const std::string & path() const { return path_; }
|
||||
long long file_size() const { return file_size_; }
|
||||
|
@ -219,23 +214,26 @@ public:
|
|||
const Etime & atime() const { return atime_; }
|
||||
const Etime & mtime() const { return mtime_; }
|
||||
|
||||
void linkpath( const char * const lp ) { linkpath_ = lp; full_size_ = -1; }
|
||||
void path( const char * const p ) { path_ = p; full_size_ = -1; }
|
||||
void file_size( const long long fs ) { full_size_ = -1;
|
||||
void linkpath( const char * const lp ) { linkpath_ = lp; full_size_ = -4; }
|
||||
void path( const char * const p ) { path_ = p; full_size_ = -4; }
|
||||
void file_size( const long long fs ) { full_size_ = -4;
|
||||
file_size_ = ( fs >= 0 && fs <= max_file_size ) ? fs : 0; }
|
||||
bool set_uid( const long long id )
|
||||
{ if( id >= 0 ) { uid_ = id; full_size_ = -1; } return id >= 0; }
|
||||
{ if( id >= 0 ) { uid_ = id; full_size_ = -4; } return id >= 0; }
|
||||
bool set_gid( const long long id )
|
||||
{ if( id >= 0 ) { gid_ = id; full_size_ = -1; } return id >= 0; }
|
||||
void set_atime( const long long s ) { atime_.set( s ); full_size_ = -1; }
|
||||
void set_mtime( const long long s ) { mtime_.set( s ); full_size_ = -1; }
|
||||
{ if( id >= 0 ) { gid_ = id; full_size_ = -4; } return id >= 0; }
|
||||
void set_atime( const long long s ) { atime_.set( s ); full_size_ = -4; }
|
||||
void set_mtime( const long long s ) { mtime_.set( s ); full_size_ = -4; }
|
||||
|
||||
long long full_size() const
|
||||
{ if( full_size_ < 0 ) calculate_sizes(); return full_size_; }
|
||||
/* Return the size of the extended block, or 0 if empty.
|
||||
Return -1 if error, -2 if out of memory, -3 if block too long. */
|
||||
int full_size() const
|
||||
{ if( full_size_ <= -4 ) calculate_sizes(); return full_size_; }
|
||||
int format_block( Resizable_buffer & rbuf ) const;
|
||||
const char * full_size_error() const;
|
||||
|
||||
bool crc_present() const { return crc_present_; }
|
||||
long long format_block( Resizable_buffer & rbuf ) const;
|
||||
bool parse( const char * const buf, const unsigned long long edsize,
|
||||
bool parse( const char * const buf, const int edsize,
|
||||
const bool permissive );
|
||||
void fill_from_ustar( const Tar_header header );
|
||||
};
|
||||
|
@ -337,17 +335,17 @@ struct Lzip_header
|
|||
// 4 version
|
||||
// 5 coded dictionary size
|
||||
|
||||
bool verify_magic() const
|
||||
bool check_magic() const
|
||||
{ return ( std::memcmp( data, lzip_magic, 4 ) == 0 ); }
|
||||
|
||||
bool verify_prefix( const int sz ) const // detect (truncated) header
|
||||
bool check_prefix( const int sz ) const // detect (truncated) header
|
||||
{
|
||||
for( int i = 0; i < sz && i < 4; ++i )
|
||||
if( data[i] != lzip_magic[i] ) return false;
|
||||
return ( sz > 0 );
|
||||
}
|
||||
|
||||
bool verify_corrupt() const // detect corrupt header
|
||||
bool check_corrupt() const // detect corrupt header
|
||||
{
|
||||
int matches = 0;
|
||||
for( int i = 0; i < 4; ++i )
|
||||
|
@ -356,7 +354,7 @@ struct Lzip_header
|
|||
}
|
||||
|
||||
uint8_t version() const { return data[4]; }
|
||||
bool verify_version() const { return ( data[4] == 1 ); }
|
||||
bool check_version() const { return ( data[4] == 1 ); }
|
||||
|
||||
unsigned dictionary_size() const
|
||||
{
|
||||
|
@ -366,8 +364,8 @@ struct Lzip_header
|
|||
return sz;
|
||||
}
|
||||
|
||||
bool verify() const
|
||||
{ return verify_magic() && verify_version() &&
|
||||
bool check() const
|
||||
{ return check_magic() && check_version() &&
|
||||
isvalid_ds( dictionary_size() ); }
|
||||
};
|
||||
|
||||
|
@ -400,7 +398,7 @@ struct Lzip_trailer
|
|||
return tmp;
|
||||
}
|
||||
|
||||
bool verify_consistency() const // check internal consistency
|
||||
bool check_consistency() const // check internal consistency
|
||||
{
|
||||
const unsigned crc = data_crc();
|
||||
const unsigned long long dsize = data_size();
|
||||
|
@ -442,6 +440,7 @@ struct Cl_options // command line options
|
|||
bool ignore_ids;
|
||||
bool ignore_overflow;
|
||||
bool keep_damaged;
|
||||
bool level_set; // compression level set in command line
|
||||
bool missing_crc;
|
||||
bool mtime_set;
|
||||
bool permissive;
|
||||
|
@ -453,9 +452,14 @@ struct Cl_options // command line options
|
|||
solidity( bsolid ), data_size( 0 ), debug_level( 0 ), level( 6 ),
|
||||
num_files( 0 ), num_workers( -1 ), out_slots( 64 ), dereference( false ),
|
||||
filenames_given( false ), ignore_ids( false ), ignore_overflow( false ),
|
||||
keep_damaged( false ), missing_crc( false ), mtime_set( false ),
|
||||
permissive( false ), preserve_permissions( false ), warn_newer( false ) {}
|
||||
keep_damaged( false ), level_set( false ), missing_crc( false ),
|
||||
mtime_set( false ), permissive( false ), preserve_permissions( false ),
|
||||
warn_newer( false ) {}
|
||||
|
||||
void set_level( const int l ) { level = l; level_set = true; }
|
||||
|
||||
int compressed() const; // tri-state bool with error (-2)
|
||||
bool uncompressed() const { return level < 0 || level > 9; }
|
||||
bool to_stdout() const { return output_filename == "-"; }
|
||||
};
|
||||
|
||||
|
@ -488,19 +492,9 @@ const char * const werr_msg = "Write error";
|
|||
const char * const chdir_msg = "Error changing working directory";
|
||||
|
||||
// defined in common.cc
|
||||
void xinit_mutex( pthread_mutex_t * const mutex );
|
||||
void xinit_cond( pthread_cond_t * const cond );
|
||||
void xdestroy_mutex( pthread_mutex_t * const mutex );
|
||||
void xdestroy_cond( pthread_cond_t * const cond );
|
||||
void xlock( pthread_mutex_t * const mutex );
|
||||
void xunlock( pthread_mutex_t * const mutex );
|
||||
void xwait( pthread_cond_t * const cond, pthread_mutex_t * const mutex );
|
||||
void xsignal( pthread_cond_t * const cond );
|
||||
void xbroadcast( pthread_cond_t * const cond );
|
||||
unsigned long long parse_octal( const uint8_t * const ptr, const int size );
|
||||
int readblock( const int fd, uint8_t * const buf, const int size );
|
||||
int writeblock( const int fd, const uint8_t * const buf, const int size );
|
||||
bool nonempty_arg( const Arg_parser & parser, const int i );
|
||||
|
||||
// defined in common_decode.cc
|
||||
bool block_is_zero( const uint8_t * const buf, const int size );
|
||||
|
@ -510,11 +504,19 @@ 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 );
|
||||
mode_t get_umask();
|
||||
const char * const filename, const int chdir_fd = -1 );
|
||||
bool make_path( const std::string & name );
|
||||
|
||||
// defined in common_mutex.cc
|
||||
void exit_fail_mt( const int retval = 1 ); // terminate the program
|
||||
bool print_removed_prefix( const std::string & prefix,
|
||||
std::string * const msgp = 0 );
|
||||
void set_error_status( const int retval );
|
||||
int final_exit_status( int retval, const bool show_msg = true );
|
||||
|
||||
// defined in compress.cc
|
||||
void show_atpos_error( const char * const filename, const long long pos,
|
||||
const bool isarchive );
|
||||
int compress( const Cl_options & cl_opts );
|
||||
|
||||
// defined in create.cc
|
||||
|
@ -524,18 +526,14 @@ bool writeblock_wrapper( const int outfd, const uint8_t * const buffer,
|
|||
bool write_eoa_records( const int outfd, const bool compressed );
|
||||
const char * remove_leading_dotslash( const char * const filename,
|
||||
std::string * const removed_prefixp, const bool dotdot = false );
|
||||
bool print_removed_prefix( const std::string & prefix,
|
||||
std::string * const msgp = 0 );
|
||||
bool fill_headers( const char * const filename, Extended & extended,
|
||||
Tar_header header, long long & file_size, const int flag );
|
||||
bool block_is_full( const long long extended_size,
|
||||
bool block_is_full( const int extended_size,
|
||||
const unsigned long long file_size,
|
||||
const unsigned long long target_size,
|
||||
unsigned long long & partial_data_size );
|
||||
void set_error_status( const int retval );
|
||||
int final_exit_status( int retval, const bool show_msg = true );
|
||||
unsigned ustar_chksum( const Tar_header header );
|
||||
bool verify_ustar_chksum( const Tar_header header );
|
||||
bool check_ustar_chksum( const Tar_header header );
|
||||
bool has_lz_ext( const std::string & name );
|
||||
int concatenate( const Cl_options & cl_opts );
|
||||
int encode( const Cl_options & cl_opts );
|
||||
|
@ -591,10 +589,10 @@ extern const char * const program_name;
|
|||
struct stat;
|
||||
int hstat( const char * const filename, struct stat * const st,
|
||||
const bool dereference );
|
||||
bool nonempty_arg( const Arg_parser & parser, const int i );
|
||||
int open_instream( const std::string & name );
|
||||
int open_outstream( const std::string & name, const bool create = true,
|
||||
Resizable_buffer * const rbufp = 0, const bool force = true );
|
||||
void exit_fail_mt( const int retval = 1 ); // terminate the program
|
||||
void show_error( const char * const msg, const int errcode = 0,
|
||||
const bool help = false );
|
||||
bool format_error( Resizable_buffer & rbuf, const int errcode,
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
#! /bin/sh
|
||||
# check script for Tarlz - Archiver with multimember lzip compression
|
||||
# Copyright (C) 2013-2022 Antonio Diaz Diaz.
|
||||
# Copyright (C) 2013-2023 Antonio Diaz Diaz.
|
||||
#
|
||||
# This script is free software: you have unlimited permission
|
||||
# to copy, distribute, and modify it.
|
||||
|
||||
LC_ALL=C
|
||||
export LC_ALL
|
||||
objdir=`pwd`
|
||||
testdir=`cd "$1" ; pwd`
|
||||
objdir="`pwd`"
|
||||
testdir="`cd "$1" ; pwd`"
|
||||
TARLZ="${objdir}"/tarlz
|
||||
framework_failure() { echo "failure in testing framework" ; exit 1 ; }
|
||||
|
||||
|
@ -59,6 +59,8 @@ eoa_lz="${testdir}"/eoa_blocks.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 ] ; }
|
||||
cyg_symlink() { [ ${lwarnc} = 0 ] &&
|
||||
printf "\nwarning: your OS follows symbolic links to directories even when tarlz asks it not to\n$1"
|
||||
lwarnc=1 ; }
|
||||
|
@ -116,6 +118,7 @@ cyg_symlink() { [ ${lwarnc} = 0 ] &&
|
|||
|
||||
"${TARLZ}" --check-lib # just print warning
|
||||
[ $? != 2 ] || test_failed $LINENO # unless bad lzlib.h
|
||||
|
||||
printf "testing tarlz-%s..." "$2"
|
||||
|
||||
"${TARLZ}" -q -tf "${in}"
|
||||
|
@ -131,22 +134,20 @@ printf "testing tarlz-%s..." "$2"
|
|||
"${TARLZ}" -q -cf out.tar.lz
|
||||
[ $? = 1 ] || test_failed $LINENO
|
||||
[ ! -e out.tar.lz ] || test_failed $LINENO
|
||||
"${TARLZ}" -rf out.tar.lz || test_failed $LINENO
|
||||
[ ! -e out.tar.lz ] || test_failed $LINENO
|
||||
"${TARLZ}" -r || test_failed $LINENO
|
||||
"${TARLZ}" --uncompressed -q -rf out.tar "${in}"
|
||||
"${TARLZ}" -q -cf out.tar
|
||||
[ $? = 1 ] || test_failed $LINENO
|
||||
[ ! -e out.tar ] || test_failed $LINENO
|
||||
"${TARLZ}" -rf out.tar.lz || test_failed $LINENO
|
||||
[ ! -e out.tar.lz ] || test_failed $LINENO
|
||||
"${TARLZ}" -rf out.tar || test_failed $LINENO
|
||||
[ ! -e out.tar ] || test_failed $LINENO
|
||||
"${TARLZ}" -r || test_failed $LINENO
|
||||
"${TARLZ}" -q -rf out.tar.lz "${in}"
|
||||
[ $? = 1 ] || test_failed $LINENO
|
||||
[ ! -e out.tar.lz ] || test_failed $LINENO
|
||||
"${TARLZ}" -q -rf out.tar "${in}"
|
||||
[ $? = 1 ] || test_failed $LINENO
|
||||
[ ! -e out.tar ] || test_failed $LINENO
|
||||
cat "${test3_lz}" > test.tar.lz || framework_failure
|
||||
"${TARLZ}" --uncompressed -q -rf test.tar.lz "${in}"
|
||||
[ $? = 2 ] || test_failed $LINENO
|
||||
cmp "${test3_lz}" test.tar.lz || test_failed $LINENO
|
||||
rm -f test.tar.lz || framework_failure
|
||||
cat "${test3}" > test.tar || framework_failure
|
||||
"${TARLZ}" -q -rf test.tar "${in}"
|
||||
[ $? = 2 ] || test_failed $LINENO
|
||||
cmp "${test3}" test.tar || test_failed $LINENO
|
||||
rm -f test.tar || framework_failure
|
||||
"${TARLZ}" -q -c "${in}" nx_file > /dev/null
|
||||
[ $? = 1 ] || test_failed $LINENO
|
||||
"${TARLZ}" -q -c -C nx_dir "${in}"
|
||||
|
@ -159,6 +160,11 @@ touch empty.tar.lz empty.tlz # 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 # compress an empty archive
|
||||
"${TARLZ}" -q -z empty.tar
|
||||
[ $? = 2 ] || test_failed $LINENO
|
||||
[ ! -e empty.tar.lz ] || test_failed $LINENO
|
||||
rm -f empty.tar empty.tar.lz || framework_failure
|
||||
"${TARLZ}" -q -cd # test mixed operations
|
||||
[ $? = 1 ] || test_failed $LINENO
|
||||
"${TARLZ}" -q -cr
|
||||
|
@ -239,6 +245,9 @@ rm -f foo bar baz || framework_failure
|
|||
cmp cfoo foo || test_failed $LINENO
|
||||
cmp cbar bar || test_failed $LINENO
|
||||
cmp cbaz baz || test_failed $LINENO
|
||||
if "${TARLZ}" -df "${test3}" --ignore-ids ; then d_works=yes
|
||||
else printf "warning: some '--diff' tests will be skipped.\n"
|
||||
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
|
||||
|
@ -247,7 +256,7 @@ for i in 0 2 6 ; do
|
|||
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 ||
|
||||
test_failed $LINENO $i
|
||||
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
|
||||
|
@ -273,6 +282,34 @@ for i in 0 2 6 ; do
|
|||
rm -f foo bar baz || framework_failure
|
||||
done
|
||||
|
||||
# test -C in --diff and --extract
|
||||
for i in "${test3}" "${test3_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"
|
||||
cmp cfoo dir1/foo || test_failed $LINENO "$i"
|
||||
cmp cbar dir2/bar || test_failed $LINENO "$i"
|
||||
cmp cbaz dir3/baz || test_failed $LINENO "$i"
|
||||
"${TARLZ}" -q -df "$i" -C dir1 foo -C ../dir2 --ignore-ids bar \
|
||||
-C ../dir3 baz || test_failed $LINENO "$i"
|
||||
"${TARLZ}" -q -df "$i" -C dir3 baz -C ../dir2 bar -C ../dir1 foo \
|
||||
--ignore-ids || test_failed $LINENO "$i"
|
||||
rm -rf dir1 dir2 dir3 || framework_failure
|
||||
done
|
||||
for i in "${test3dir}" "${test3dir_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"
|
||||
cmp cfoo dir1/dir/foo || test_failed $LINENO "$i"
|
||||
cmp cbar dir2/dir/bar || test_failed $LINENO "$i"
|
||||
cmp cbaz dir3/dir/baz || test_failed $LINENO "$i"
|
||||
"${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 \
|
||||
--ignore-ids -C ../../dir3/dir baz || test_failed $LINENO "$i"
|
||||
rm -rf dir1 dir2 dir3 || framework_failure
|
||||
done
|
||||
|
||||
for i in "${test3dir}" "${test3dir_lz}" ; do
|
||||
"${TARLZ}" -q -tf "$i" --missing-crc || test_failed $LINENO "$i"
|
||||
"${TARLZ}" -q -xf "$i" --missing-crc || test_failed $LINENO "$i"
|
||||
|
@ -409,9 +446,9 @@ for i in 0 2 6 ; do
|
|||
rm -f foo bar baz || framework_failure
|
||||
done
|
||||
"${TARLZ}" -n0 -xf "${testdir}"/test3_eoa3.tar.lz || test_failed $LINENO
|
||||
cmp cfoo foo || test_failed $LINENO $i
|
||||
[ ! -e bar ] || test_failed $LINENO $i
|
||||
[ ! -e baz ] || test_failed $LINENO $i
|
||||
cmp cfoo foo || test_failed $LINENO
|
||||
[ ! -e bar ] || test_failed $LINENO
|
||||
[ ! -e baz ] || test_failed $LINENO
|
||||
rm -f foo bar baz || framework_failure
|
||||
|
||||
# test --list and --extract tar in tar.lz
|
||||
|
@ -480,6 +517,10 @@ cat "${in}" > out.tar.lz || framework_failure # invalid tar.lz
|
|||
"${TARLZ}" -Aqf out.tar.lz "${test3_lz}"
|
||||
[ $? = 2 ] || test_failed $LINENO
|
||||
cat "${in_tar_lz}" > out.tar.lz || framework_failure
|
||||
"${TARLZ}" -q --un -Af out.tar.lz "${test3_lz}" # contradictory ext
|
||||
[ $? = 1 ] || test_failed $LINENO
|
||||
cmp "${in_tar_lz}" out.tar.lz || test_failed $LINENO
|
||||
cat "${in_tar_lz}" > out.tar.lz || framework_failure
|
||||
"${TARLZ}" -Af out.tar.lz "${test3_lz}" || test_failed $LINENO
|
||||
"${TARLZ}" -xf out.tar.lz || test_failed $LINENO
|
||||
cmp "${in}" test.txt || test_failed $LINENO
|
||||
|
@ -524,6 +565,10 @@ cat "${in}" > out.tar || framework_failure # invalid tar
|
|||
"${TARLZ}" -Aqf out.tar "${test3}"
|
||||
[ $? = 2 ] || test_failed $LINENO
|
||||
cat "${in_tar}" > out.tar || framework_failure
|
||||
"${TARLZ}" -q -0 -Af out.tar "${test3}" # contradictory ext
|
||||
[ $? = 1 ] || test_failed $LINENO
|
||||
cmp "${in_tar}" out.tar || test_failed $LINENO
|
||||
cat "${in_tar}" > out.tar || framework_failure
|
||||
"${TARLZ}" -Af out.tar "${test3}" || test_failed $LINENO
|
||||
"${TARLZ}" -xf out.tar || test_failed $LINENO
|
||||
cmp "${in}" test.txt || test_failed $LINENO
|
||||
|
@ -568,11 +613,13 @@ printf "\ntesting --create..."
|
|||
# test --create
|
||||
cat "${in}" > test.txt || framework_failure
|
||||
"${TARLZ}" --warn-newer -0 -cf out.tar.lz test.txt || test_failed $LINENO
|
||||
is_compressed out.tar.lz || test_failed $LINENO
|
||||
rm -f test.txt || framework_failure
|
||||
"${TARLZ}" -xf out.tar.lz --missing-crc || test_failed $LINENO
|
||||
cmp "${in}" test.txt || test_failed $LINENO
|
||||
cat "${in}" > test.txt || framework_failure
|
||||
"${TARLZ}" --warn-newer --uncompressed -cf out.tar test.txt || test_failed $LINENO
|
||||
"${TARLZ}" --warn-newer --un -cf out.tar test.txt || test_failed $LINENO
|
||||
is_uncompressed out.tar || test_failed $LINENO
|
||||
rm -f test.txt || framework_failure
|
||||
"${TARLZ}" -xf out.tar --missing-crc || test_failed $LINENO
|
||||
cmp "${in}" test.txt || test_failed $LINENO
|
||||
|
@ -639,7 +686,7 @@ cmp cfoo dir1/foo || test_failed $LINENO
|
|||
cmp cbar dir1/bar || test_failed $LINENO
|
||||
cmp cbaz dir1/baz || test_failed $LINENO
|
||||
rm -f dir1/foo dir1/bar dir1/baz || framework_failure
|
||||
"${TARLZ}" -0 -c foo bar baz | "${TARLZ}" -x foo bar baz -C dir1 ||
|
||||
"${TARLZ}" -0 -c foo bar baz | "${TARLZ}" -x -C dir1 foo bar baz ||
|
||||
test_failed $LINENO
|
||||
cmp cfoo dir1/foo || test_failed $LINENO
|
||||
cmp cbar dir1/bar || test_failed $LINENO
|
||||
|
@ -670,7 +717,8 @@ rm -f out.tar.lz foo bar baz || framework_failure
|
|||
cat cfoo > foo || framework_failure
|
||||
cat cbar > bar || framework_failure
|
||||
cat cbaz > baz || framework_failure
|
||||
"${TARLZ}" --un -cf out.tar foo bar baz --exclude 'ba*' || test_failed $LINENO
|
||||
"${TARLZ}" -cf out.tar foo bar baz --exclude 'ba*' || test_failed $LINENO
|
||||
is_uncompressed out.tar || test_failed $LINENO
|
||||
rm -f foo bar baz || framework_failure
|
||||
"${TARLZ}" -xf out.tar || test_failed $LINENO
|
||||
cmp cfoo foo || test_failed $LINENO
|
||||
|
@ -697,10 +745,13 @@ touch -d 2022-01-05T12:22:13 bar || framework_failure
|
|||
for i in ${dates} @-8Ei '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
|
||||
touch foo || framework_failure
|
||||
"${TARLZ}" --un -cf out.tar --mtime="$i" foo || test_failed $LINENO "$i"
|
||||
"${TARLZ}" -cf out.tar --mtime="$i" foo || test_failed $LINENO "$i"
|
||||
is_uncompressed out.tar || test_failed $LINENO "$i"
|
||||
"${TARLZ}" -q -df out.tar && test_failed $LINENO "$i"
|
||||
"${TARLZ}" -xf out.tar || test_failed $LINENO "$i"
|
||||
"${TARLZ}" -df out.tar --ignore-overflow || test_failed $LINENO "$i"
|
||||
if [ "${d_works}" = yes ] ; then
|
||||
"${TARLZ}" -df out.tar --ignore-overflow || test_failed $LINENO "$i"
|
||||
fi
|
||||
done
|
||||
rm -f out.tar foo bar || framework_failure
|
||||
|
||||
|
@ -708,9 +759,10 @@ mkdir dir || framework_failure
|
|||
for i in ${dates} ; do
|
||||
# Skip a time stamp $i if it's out of range for this platform,
|
||||
# of if it uses a notation that this platform does not recognize.
|
||||
touch -d $i dir/f$i >/dev/null 2>&1 || continue
|
||||
touch -d "$i" "dir/f$i" >/dev/null 2>&1 || continue
|
||||
done
|
||||
"${TARLZ}" --uncompressed -cf out.tar dir || test_failed $LINENO
|
||||
"${TARLZ}" -cf out.tar dir || test_failed $LINENO
|
||||
is_uncompressed out.tar || test_failed $LINENO
|
||||
"${TARLZ}" -df out.tar || test_failed $LINENO
|
||||
rm -rf out.tar dir || framework_failure
|
||||
|
||||
|
@ -718,8 +770,10 @@ printf "\ntesting --diff..."
|
|||
|
||||
# test --diff
|
||||
"${TARLZ}" -xf "${test3_lz}" || test_failed $LINENO
|
||||
"${TARLZ}" --uncompressed -cf out.tar foo || test_failed $LINENO
|
||||
"${TARLZ}" --uncompressed -cf aout.tar foo --anonymous || 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
|
||||
is_uncompressed aout.tar || test_failed $LINENO
|
||||
if cmp out.tar aout.tar > /dev/null ; then
|
||||
printf "\nwarning: '--diff' test can't be run as root.\n"
|
||||
else
|
||||
|
@ -731,7 +785,7 @@ else
|
|||
"${TARLZ}" -n$i -df "${test3_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}" foo baz --ignore-ids ||
|
||||
"${TARLZ}" -n$i -df "${test3_lz}" --ignore-ids foo baz ||
|
||||
test_failed $LINENO $i
|
||||
"${TARLZ}" -n$i -df "${test3_lz}" --exclude bar --ignore-ids ||
|
||||
test_failed $LINENO $i
|
||||
|
@ -879,12 +933,14 @@ if [ "${ln_works}" = yes ] ; then
|
|||
cat cbar > dir/bar || framework_failure
|
||||
cat cbaz > dir/baz || framework_failure
|
||||
ln -s dir dir_link || framework_failure
|
||||
"${TARLZ}" -0 -cf out1 dir_link || test_failed $LINENO
|
||||
"${TARLZ}" --un -cf out2 dir_link || test_failed $LINENO
|
||||
"${TARLZ}" -0 -n0 -cf out3 dir_link || test_failed $LINENO
|
||||
"${TARLZ}" -0 -h -cf hout1 dir_link || test_failed $LINENO
|
||||
"${TARLZ}" --un -h -cf hout2 dir_link || test_failed $LINENO
|
||||
"${TARLZ}" -0 -n0 -h -cf hout3 dir_link || test_failed $LINENO
|
||||
"${TARLZ}" -0 -c dir_link > out1 || test_failed $LINENO
|
||||
is_compressed out1 || test_failed $LINENO
|
||||
"${TARLZ}" --un -c dir_link > out2 || test_failed $LINENO
|
||||
is_uncompressed out2 || test_failed $LINENO
|
||||
"${TARLZ}" -0 -n0 -c dir_link > out3 || test_failed $LINENO
|
||||
"${TARLZ}" -0 -h -c dir_link > hout1 || test_failed $LINENO
|
||||
"${TARLZ}" --un -h -c dir_link > hout2 || test_failed $LINENO
|
||||
"${TARLZ}" -0 -n0 -h -c dir_link > hout3 || test_failed $LINENO
|
||||
rm -rf dir dir_link || framework_failure
|
||||
for i in 1 2 3 ; do
|
||||
"${TARLZ}" -xf out$i --exclude='dir_link/*' dir_link ||
|
||||
|
@ -915,10 +971,6 @@ cat cbaz > baz || framework_failure
|
|||
"${TARLZ}" -0 -rf aout.tar.lz bar baz --no-solid || test_failed $LINENO
|
||||
cmp nout.tar.lz aout.tar.lz || test_failed $LINENO
|
||||
rm -f nout.tar.lz aout.tar.lz || framework_failure
|
||||
touch aout.tar || framework_failure # wrong extension empty file
|
||||
"${TARLZ}" -0 -rf aout.tar foo bar baz || test_failed $LINENO
|
||||
cmp out.tar.lz aout.tar || test_failed $LINENO
|
||||
rm -f aout.tar || framework_failure
|
||||
touch aout.tar.lz || framework_failure # append to empty file
|
||||
"${TARLZ}" -0 -rf aout.tar.lz foo bar baz || test_failed $LINENO
|
||||
cmp out.tar.lz aout.tar.lz || test_failed $LINENO
|
||||
|
@ -933,50 +985,45 @@ cmp out.tar.lz aout.tar.lz || test_failed $LINENO
|
|||
cmp out.tar.lz aout.tar.lz || test_failed $LINENO
|
||||
"${TARLZ}" -0 -r foo bar baz > aout.tar.lz || test_failed $LINENO # to stdout
|
||||
cmp out.tar.lz aout.tar.lz || test_failed $LINENO
|
||||
"${TARLZ}" --un -q -rf aout.tar.lz foo bar baz # wrong extension archive
|
||||
[ $? = 2 ] || 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
|
||||
cat "${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
|
||||
"${TARLZ}" --un -q -rf aout.tar.lz foo bar baz # wrong extension empty archive
|
||||
[ $? = 2 ] || test_failed $LINENO
|
||||
cmp out.tar.lz aout.tar.lz || test_failed $LINENO
|
||||
rm -f out.tar.lz aout.tar.lz || framework_failure
|
||||
|
||||
# test --append --uncompressed
|
||||
"${TARLZ}" --un -cf out.tar foo bar baz || test_failed $LINENO
|
||||
"${TARLZ}" --un -cf aout.tar foo || test_failed $LINENO
|
||||
"${TARLZ}" --un -rf aout.tar foo bar baz --exclude foo || test_failed $LINENO
|
||||
"${TARLZ}" -cf out.tar foo bar baz || test_failed $LINENO
|
||||
"${TARLZ}" -cf aout.tar foo || test_failed $LINENO
|
||||
"${TARLZ}" -rf aout.tar foo bar baz --exclude foo || test_failed $LINENO
|
||||
is_uncompressed out.tar || test_failed $LINENO
|
||||
cmp out.tar aout.tar || test_failed $LINENO
|
||||
rm -f aout.tar || framework_failure
|
||||
touch aout.tar.lz empty || framework_failure # wrong extension empty file
|
||||
"${TARLZ}" --un -q -rf aout.tar.lz foo bar baz
|
||||
[ $? = 2 ] || test_failed $LINENO
|
||||
cmp aout.tar.lz empty || test_failed $LINENO
|
||||
rm -f aout.tar.lz empty || framework_failure
|
||||
touch aout.tar empty || framework_failure # contradictory ext empty file
|
||||
"${TARLZ}" -0 -q -rf aout.tar foo bar baz
|
||||
[ $? = 1 ] || test_failed $LINENO
|
||||
cmp aout.tar empty || test_failed $LINENO
|
||||
rm -f aout.tar empty || framework_failure
|
||||
touch aout.tar || framework_failure # append to empty file
|
||||
"${TARLZ}" --un -rf aout.tar foo bar baz || test_failed $LINENO
|
||||
"${TARLZ}" -rf aout.tar foo bar baz || test_failed $LINENO
|
||||
cmp out.tar aout.tar || test_failed $LINENO
|
||||
"${TARLZ}" --un -rf aout.tar || test_failed $LINENO # append nothing
|
||||
"${TARLZ}" -rf aout.tar || test_failed $LINENO # append nothing
|
||||
cmp out.tar aout.tar || test_failed $LINENO
|
||||
"${TARLZ}" --un -rf aout.tar -C nx_dir || test_failed $LINENO
|
||||
"${TARLZ}" -rf aout.tar -C nx_dir || test_failed $LINENO
|
||||
cmp out.tar aout.tar || test_failed $LINENO
|
||||
"${TARLZ}" --un -q -rf aout.tar nx_file
|
||||
"${TARLZ}" -q -rf aout.tar nx_file
|
||||
[ $? = 1 ] || test_failed $LINENO
|
||||
cmp out.tar aout.tar || test_failed $LINENO
|
||||
"${TARLZ}" --un -q -rf aout.tar aout.tar || test_failed $LINENO
|
||||
"${TARLZ}" -q -rf aout.tar aout.tar || test_failed $LINENO
|
||||
cmp out.tar aout.tar || test_failed $LINENO
|
||||
"${TARLZ}" --un -r foo bar baz > aout.tar || test_failed $LINENO # to stdout
|
||||
cmp out.tar aout.tar || test_failed $LINENO
|
||||
"${TARLZ}" -0 -q -rf aout.tar foo bar baz # wrong extension archive
|
||||
[ $? = 2 ] || 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
|
||||
cat "${eoa}" > aout.tar || framework_failure # append to empty archive
|
||||
"${TARLZ}" --un -rf aout.tar foo bar baz || test_failed $LINENO
|
||||
cmp out.tar aout.tar || test_failed $LINENO
|
||||
"${TARLZ}" -0 -q -rf aout.tar foo bar baz # wrong extension empty archive
|
||||
[ $? = 2 ] || test_failed $LINENO
|
||||
"${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
|
||||
|
||||
|
@ -1015,7 +1062,8 @@ rmdir dir1 || framework_failure
|
|||
rmdir dir1
|
||||
rm -f out.tar.lz || framework_failure
|
||||
mkdir dir1 || framework_failure
|
||||
"${TARLZ}" --uncompressed -cf out.tar dir1 || test_failed $LINENO
|
||||
"${TARLZ}" -cf out.tar dir1 || test_failed $LINENO
|
||||
is_uncompressed out.tar || test_failed $LINENO
|
||||
rmdir dir1 || framework_failure
|
||||
"${TARLZ}" -xf out.tar || test_failed $LINENO
|
||||
[ -d dir1 ] || test_failed $LINENO
|
||||
|
@ -1110,8 +1158,8 @@ cat cfoo > foo || framework_failure
|
|||
cat cbar > bar || framework_failure
|
||||
cat cbaz > baz || framework_failure
|
||||
cat "${in}" > test.txt || framework_failure
|
||||
"${TARLZ}" --un -cf out.tar test.txt foo bar baz test.txt || test_failed $LINENO
|
||||
"${TARLZ}" --un -cf out3.tar foo bar baz || test_failed $LINENO
|
||||
"${TARLZ}" -cf out.tar test.txt foo bar baz test.txt || test_failed $LINENO
|
||||
"${TARLZ}" -cf out3.tar foo bar baz || test_failed $LINENO
|
||||
cat out.tar > outz.tar || framework_failure
|
||||
cat out3.tar > out3z.tar || framework_failure
|
||||
#
|
||||
|
@ -1164,12 +1212,15 @@ for i in --asolid --bsolid --dsolid ; do
|
|||
rm -f out outz.tar.lz out3z.tar.lz || framework_failure
|
||||
done
|
||||
# concatenate and compress
|
||||
"${TARLZ}" --un -cf foo.tar foo || test_failed $LINENO
|
||||
"${TARLZ}" --un -cf bar.tar bar || test_failed $LINENO
|
||||
"${TARLZ}" --un -cf baz.tar baz || test_failed $LINENO
|
||||
"${TARLZ}" -cf foo.tar foo || test_failed $LINENO
|
||||
"${TARLZ}" -cf bar.tar bar || test_failed $LINENO
|
||||
"${TARLZ}" -cf baz.tar baz || test_failed $LINENO
|
||||
"${TARLZ}" -A foo.tar bar.tar baz.tar | "${TARLZ}" -0 -z -o foobarbaz.tar.lz ||
|
||||
test_failed $LINENO
|
||||
cmp out3.tar.lz foobarbaz.tar.lz || test_failed $LINENO
|
||||
"${TARLZ}" -A foo.tar bar.tar baz.tar | "${TARLZ}" -0 -z > foobarbaz.tar.lz ||
|
||||
test_failed $LINENO
|
||||
cmp out3.tar.lz foobarbaz.tar.lz || test_failed $LINENO
|
||||
# compress and concatenate
|
||||
"${TARLZ}" -0 -z foo.tar bar.tar baz.tar || test_failed $LINENO
|
||||
"${TARLZ}" -A foo.tar.lz bar.tar.lz baz.tar.lz > foobarbaz.tar.lz ||
|
||||
|
@ -1177,8 +1228,8 @@ cmp out3.tar.lz foobarbaz.tar.lz || test_failed $LINENO
|
|||
"${TARLZ}" -0 -n0 --no-solid -c foo bar baz | cmp foobarbaz.tar.lz - ||
|
||||
test_failed $LINENO
|
||||
rm -f foo bar baz test.txt out.tar.lz out.tar outz.tar foobarbaz.tar.lz \
|
||||
out3.tar out3.tar.lz out3z.tar foo.tar bar.tar baz.tar \
|
||||
foo.tar.lz bar.tar.lz baz.tar.lz || framework_failure
|
||||
out3.tar out3.tar.lz out3z.tar foo.tar bar.tar baz.tar \
|
||||
foo.tar.lz bar.tar.lz baz.tar.lz || framework_failure
|
||||
|
||||
printf "\ntesting bad input..."
|
||||
|
||||
|
@ -1260,19 +1311,23 @@ 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
|
||||
"${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
|
||||
"${TARLZ}" -n0 -df "$i" --ignore-ids || test_failed $LINENO $i
|
||||
cmp cfoo foo || test_failed $LINENO $i
|
||||
[ ! -e bar ] || test_failed $LINENO $i
|
||||
cmp cbaz baz || test_failed $LINENO $i
|
||||
"${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"
|
||||
if [ "${d_works}" = yes ] ; then
|
||||
"${TARLZ}" -n0 -df "$i" --ignore-ids || test_failed $LINENO "$i"
|
||||
fi
|
||||
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 -n4 -xf "$i" || test_failed $LINENO $i
|
||||
"${TARLZ}" -n4 -df "$i" --ignore-ids || test_failed $LINENO $i
|
||||
cmp cfoo foo || test_failed $LINENO $i
|
||||
[ ! -e bar ] || test_failed $LINENO $i
|
||||
cmp cbaz baz || test_failed $LINENO $i
|
||||
"${TARLZ}" -q -n4 -xf "$i" || test_failed $LINENO "$i"
|
||||
if [ "${d_works}" = yes ] ; then
|
||||
"${TARLZ}" -n4 -df "$i" --ignore-ids || test_failed $LINENO "$i"
|
||||
fi
|
||||
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
|
||||
done
|
||||
|
||||
|
@ -1288,7 +1343,7 @@ 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 2> /dev/null || test_failed $LINENO "$i"
|
||||
rm -f test.txt || framework_failure
|
||||
done
|
||||
#
|
||||
|
|
Loading…
Add table
Reference in a new issue