Adding upstream version 0.24.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
9a8733dd3b
commit
4f5d0de2b2
33 changed files with 905 additions and 882 deletions
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>
|
2022-09-23 Antonio Diaz Diaz <antonio@gnu.org>
|
||||||
|
|
||||||
* Version 0.23 released.
|
* Version 0.23 released.
|
||||||
|
@ -40,9 +52,8 @@
|
||||||
decode_lz.cc (dworker): Likewise. (Reported by Florian Schmaus).
|
decode_lz.cc (dworker): Likewise. (Reported by Florian Schmaus).
|
||||||
* New options '-z, --compress' and '-o, --output'.
|
* New options '-z, --compress' and '-o, --output'.
|
||||||
* New option '--warn-newer'.
|
* New option '--warn-newer'.
|
||||||
* tarlz.texi (Portable character set): Link to moe section on Unicode.
|
* tarlz.texi (Invoking tarlz): Document concatenation to stdout.
|
||||||
(Invoking tarlz): Document concatenation to standard output.
|
* check.sh: Fix the '--diff' test on OS/2. (Reported by Elbert Pol).
|
||||||
* check.sh: Fix the '--diff' test on OS/2.
|
|
||||||
|
|
||||||
2021-01-08 Antonio Diaz Diaz <antonio@gnu.org>
|
2021-01-08 Antonio Diaz Diaz <antonio@gnu.org>
|
||||||
|
|
||||||
|
@ -154,7 +165,7 @@
|
||||||
* New option '--keep-damaged'.
|
* New option '--keep-damaged'.
|
||||||
* New option '--no-solid'.
|
* New option '--no-solid'.
|
||||||
* create.cc (archive_write): Minimize dictionary size.
|
* 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.
|
* main.cc (show_version): Show the version of lzlib being used.
|
||||||
|
|
||||||
2018-10-19 Antonio Diaz Diaz <antonio@gnu.org>
|
2018-10-19 Antonio Diaz Diaz <antonio@gnu.org>
|
||||||
|
@ -162,7 +173,7 @@
|
||||||
* Version 0.6 released.
|
* Version 0.6 released.
|
||||||
* New option '-A, --concatenate'.
|
* New option '-A, --concatenate'.
|
||||||
* Option '--ignore-crc' replaced with '--missing-crc'.
|
* 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.
|
and devminor are in ustar range.
|
||||||
* configure: Accept appending to CXXFLAGS; 'CXXFLAGS+=OPTIONS'.
|
* configure: Accept appending to CXXFLAGS; 'CXXFLAGS+=OPTIONS'.
|
||||||
* Makefile.in: Use tarlz in target 'dist'.
|
* Makefile.in: Use tarlz in target 'dist'.
|
||||||
|
@ -208,7 +219,7 @@
|
||||||
* Version 0.1 released.
|
* 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,
|
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
|
but just in case, you have unlimited permission to copy, distribute, and
|
||||||
|
|
13
INSTALL
13
INSTALL
|
@ -23,8 +23,8 @@ Procedure
|
||||||
or
|
or
|
||||||
lzip -cd tarlz[version].tar.lz | tar -xf -
|
lzip -cd tarlz[version].tar.lz | tar -xf -
|
||||||
|
|
||||||
This creates the directory ./tarlz[version] containing the source from
|
This creates the directory ./tarlz[version] containing the source code
|
||||||
the main archive.
|
extracted from the archive.
|
||||||
|
|
||||||
2. Change to tarlz directory and run configure.
|
2. Change to tarlz directory and run configure.
|
||||||
(Try 'configure --help' for usage instructions).
|
(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.
|
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
|
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
|
Or type 'make install-compress', which additionally compresses the
|
||||||
info manual and the man page after installation.
|
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
|
'configure' automatically checks for the source code in '.', in '..', and
|
||||||
in the directory that 'configure' is in.
|
in the directory that 'configure' is in.
|
||||||
|
|
||||||
'configure' recognizes the option '--srcdir=DIR' to control where to
|
'configure' recognizes the option '--srcdir=DIR' to control where to look
|
||||||
look for the sources. Usually 'configure' can determine that directory
|
for the source code. Usually 'configure' can determine that directory
|
||||||
automatically.
|
automatically.
|
||||||
|
|
||||||
After running 'configure', you can run 'make' and 'make install' as
|
After running 'configure', you can run 'make' and 'make install' as
|
||||||
explained above.
|
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,
|
This file is free documentation: you have unlimited permission to copy,
|
||||||
distribute, and modify it.
|
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
|
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 \
|
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 \
|
common_mutex.o compress.o create.o create_lz.o decode.o decode_lz.o \
|
||||||
delete_lz.o exclude.o extended.o main.o
|
delete.o delete_lz.o exclude.o extended.o main.o
|
||||||
|
|
||||||
|
|
||||||
.PHONY : all install install-bin install-info install-man \
|
.PHONY : all install install-bin install-info install-man \
|
||||||
|
@ -32,13 +32,15 @@ main.o : main.cc
|
||||||
$(objs) : Makefile
|
$(objs) : Makefile
|
||||||
arg_parser.o : arg_parser.h
|
arg_parser.o : arg_parser.h
|
||||||
archive_reader.o : tarlz.h lzip_index.h archive_reader.h
|
archive_reader.o : tarlz.h lzip_index.h archive_reader.h
|
||||||
common.o : tarlz.h arg_parser.h
|
common.o : tarlz.h
|
||||||
common_decode.o : tarlz.h arg_parser.h
|
common_decode.o : tarlz.h arg_parser.h decode.h
|
||||||
|
common_mutex.o : common_mutex.h
|
||||||
compress.o : tarlz.h arg_parser.h
|
compress.o : tarlz.h arg_parser.h
|
||||||
create.o : tarlz.h arg_parser.h create.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.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.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
|
delete_lz.o : tarlz.h arg_parser.h lzip_index.h archive_reader.h
|
||||||
exclude.o : tarlz.h
|
exclude.o : tarlz.h
|
||||||
|
@ -52,7 +54,7 @@ doc : info man
|
||||||
info : $(VPATH)/doc/$(pkgname).info
|
info : $(VPATH)/doc/$(pkgname).info
|
||||||
|
|
||||||
$(VPATH)/doc/$(pkgname).info : $(VPATH)/doc/$(pkgname).texi
|
$(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
|
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',
|
The option '-C, --directory' is now position-dependent also for diff and
|
||||||
allowing times beyond the ustar range (before 1970-01-01 00:00:00 UTC or
|
extract. (Reported by Devon Sean McCullough).
|
||||||
after 2242-03-16 12:56:31 UTC).
|
|
||||||
|
|
||||||
Tarlz now can create and decode the extended records 'uid' and 'gid',
|
Option '--uncompressed' can now be omitted if it can be deduced from the
|
||||||
allowing user and group IDs beyond the ustar limit of 2_097_151.
|
archive name. (An uncompressed archive name lacks a '.lz' or '.tlz' extension).
|
||||||
|
|
||||||
The new option '--ignore-overflow', which makes '-d, --diff' ignore
|
The diagnostic shown when a file being archived or an archive being
|
||||||
differences in mtime caused by overflow on 32-bit systems, has been added.
|
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
|
'-z, --compress' now exits with error status 2 if any input archive is an
|
||||||
to a terminal. (Reported by DustDFG).
|
empty file.
|
||||||
|
|
||||||
In the date format of option '--mtime' the time of day 'HH:MM:SS' is now
|
A failure in the '--diff' test of the testsuite on OS/2 has been fixed.
|
||||||
optional and defaults to '00:00:00'. Both space and 'T' are now accepted as
|
(Reported by Elbert Pol).
|
||||||
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.
|
|
||||||
|
|
6
README
6
README
|
@ -67,8 +67,8 @@ place.
|
||||||
Because of the above, tarlz protects the extended records with a Cyclic
|
Because of the above, tarlz protects the extended records with a Cyclic
|
||||||
Redundancy Check (CRC) in a way compatible with standard tar tools.
|
Redundancy Check (CRC) in a way compatible with standard tar tools.
|
||||||
|
|
||||||
Tarlz does not understand other tar formats like gnu, oldgnu, star or v7.
|
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
|
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.
|
that the format of the archive is compatible with tarlz.
|
||||||
|
|
||||||
The diagram below shows the correspondence between each tar member (formed
|
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,
|
This file is free documentation: you have unlimited permission to copy,
|
||||||
distribute, and modify it.
|
distribute, and modify it.
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/* Tarlz - Archiver with multimember lzip compression
|
/* 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
|
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
|
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 edsize = parse_octal( header + size_o, size_l );
|
||||||
const long long bufsize = round_up( edsize );
|
const long long bufsize = round_up( edsize );
|
||||||
if( edsize <= 0 ) return err( 2, misrec_msg ); // no extended records
|
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
|
return err( -2, longrec_msg ); // records too long
|
||||||
if( !rbuf.resize( bufsize ) ) return err( -1, mem_msg );
|
if( !rbuf.resize( bufsize ) ) return err( -1, mem_msg );
|
||||||
e_msg_ = ""; e_code_ = 0;
|
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 );
|
const int rd = readblock( ad.infd, buf, size );
|
||||||
if( rd != size && errno ) return err( -1, rdaerr_msg, errno, rd );
|
if( rd != size && errno ) return err( -1, rdaerr_msg, errno, rd );
|
||||||
const Lzip_header & header = (*(const Lzip_header *)buf);
|
const Lzip_header & header = (*(const Lzip_header *)buf);
|
||||||
const bool islz = ( rd >= min_member_size && header.verify_magic() &&
|
const bool islz = ( rd >= min_member_size && header.check_magic() &&
|
||||||
header.verify_version() &&
|
header.check_version() &&
|
||||||
isvalid_ds( header.dictionary_size() ) );
|
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 =
|
const bool iseoa =
|
||||||
( !islz && !istar && rd == size && block_is_zero( buf, size ) );
|
( !islz && !istar && rd == size && block_is_zero( buf, size ) );
|
||||||
bool maybe_lz = islz; // maybe corrupt tar.lz
|
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 ); }
|
{ LZ_decompress_close( decoder ); decoder = 0; return err( -1, mem_msg ); }
|
||||||
xLZ_decompress_write( decoder, buf, rd );
|
xLZ_decompress_write( decoder, buf, rd );
|
||||||
const int ret = read( buf, size ); if( ret != 0 ) return ret;
|
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 : "" );
|
return err( 2, islz ? posix_lz_msg : "" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/* Tarlz - Archiver with multimember lzip compression
|
/* 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
|
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
|
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)
|
/* 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
|
This library is free software. Redistribution and use in source and
|
||||||
binary forms, with or without modification, are permitted provided
|
binary forms, with or without modification, are permitted provided
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/* Arg_parser - POSIX/GNU command line argument parser. (C++ version)
|
/* 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
|
This library is free software. Redistribution and use in source and
|
||||||
binary forms, with or without modification, are permitted provided
|
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
|
/* 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
|
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
|
it under the terms of the GNU General Public License as published by
|
||||||
|
@ -19,82 +19,9 @@
|
||||||
|
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
#include <cstdlib>
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "tarlz.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 )
|
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;
|
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
|
/* 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
|
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
|
it under the terms of the GNU General Public License as published by
|
||||||
|
@ -19,12 +19,13 @@
|
||||||
|
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdlib>
|
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
|
#include <unistd.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
#include "tarlz.h"
|
#include "tarlz.h"
|
||||||
#include "arg_parser.h"
|
#include "arg_parser.h"
|
||||||
|
#include "decode.h"
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -125,7 +126,7 @@ bool format_member_name( const Extended & extended, const Tar_header header,
|
||||||
const time_t mtime = extended.mtime().sec();
|
const time_t mtime = extended.mtime().sec();
|
||||||
struct tm t;
|
struct tm t;
|
||||||
if( !localtime_r( &mtime, &t ) ) // if local time fails
|
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; } }
|
{ 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 Typeflag typeflag = (Typeflag)header[typeflag_o];
|
||||||
const bool islink = ( typeflag == tf_link || typeflag == tf_symlink );
|
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,
|
bool check_skip_filename( const Cl_options & cl_opts,
|
||||||
std::vector< char > & name_pending,
|
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
|
if( Exclude::excluded( filename ) ) return true; // skip excluded files
|
||||||
bool skip = cl_opts.num_files > 0; // if no files specified, skip nothing
|
if( cl_opts.num_files <= 0 ) return false; // no files specified, no skip
|
||||||
if( skip ) // else skip all but the files (or trees) specified
|
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 )
|
for( int i = 0; i < cl_opts.parser.arguments(); ++i )
|
||||||
if( nonempty_arg( cl_opts.parser, 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;
|
std::string removed_prefix;
|
||||||
const char * const name = remove_leading_dotslash(
|
const char * const name = remove_leading_dotslash(
|
||||||
cl_opts.parser.argument( i ).c_str(), &removed_prefix );
|
cl_opts.parser.argument( i ).c_str(), &removed_prefix );
|
||||||
if( compare_prefix_dir( name, filename ) ||
|
if( compare_prefix_dir( name, filename ) ||
|
||||||
compare_tslash( name, filename ) )
|
compare_tslash( name, filename ) )
|
||||||
{ print_removed_prefix( removed_prefix );
|
{
|
||||||
skip = false; name_pending[i] = false; break; }
|
print_removed_prefix( removed_prefix );
|
||||||
|
skip = false; name_pending[i] = false;
|
||||||
|
if( chdir_pending && chdir_fd >= 0 )
|
||||||
|
{
|
||||||
|
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;
|
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 )
|
bool make_path( const std::string & name )
|
||||||
{
|
{
|
||||||
const mode_t mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
|
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
|
/* 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
|
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
|
it under the terms of the GNU General Public License as published by
|
||||||
|
@ -20,7 +20,6 @@
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
#include <csignal>
|
#include <csignal>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdlib>
|
|
||||||
#include <stdint.h> // for lzlib.h
|
#include <stdint.h> // for lzlib.h
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <utime.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 )
|
LZ_Encoder * const encoder )
|
||||||
{
|
{
|
||||||
static bool flushed = true; // avoid flushing empty lzip members
|
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 );
|
flushed = ( size <= 0 );
|
||||||
enum { obuf_size = 65536 };
|
enum { obuf_size = 65536 };
|
||||||
uint8_t obuf[obuf_size];
|
uint8_t obuf[obuf_size];
|
||||||
long long sz = 0;
|
int sz = 0;
|
||||||
if( flushed ) LZ_compress_finish( encoder ); // flush encoder
|
if( flushed ) LZ_compress_finish( encoder ); // flush encoder
|
||||||
while( sz < size || flushed )
|
while( sz < size || flushed )
|
||||||
{
|
{
|
||||||
if( sz < size )
|
if( sz < size )
|
||||||
{ const int wr = LZ_compress_write( encoder, buf + sz,
|
{ const int wr = LZ_compress_write( encoder, buf + sz, size - sz );
|
||||||
std::min( size - sz, (long long)max_dictionary_size ) );
|
|
||||||
if( wr < 0 ) internal_error( "library error (LZ_compress_write)." );
|
if( wr < 0 ) internal_error( "library error (LZ_compress_write)." );
|
||||||
sz += wr; }
|
sz += wr; }
|
||||||
if( sz >= size && !flushed ) break; // minimize dictionary size
|
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
|
Resizable_buffer rbuf; // headers and extended records buffer
|
||||||
if( !rbuf.size() ) { show_error( mem_msg ); return 1; }
|
if( !rbuf.size() ) { show_error( mem_msg ); return 1; }
|
||||||
const char * const rderr_msg = "Read error";
|
const char * const rderr_msg = "Read error";
|
||||||
|
bool first_header = true;
|
||||||
|
|
||||||
while( true ) // process one tar member per iteration
|
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 );
|
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 )
|
if( rd != header_size )
|
||||||
{ show_file_error( filename, rderr_msg, errno ); close( infd ); return 1; }
|
{ 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 );
|
outfd = open_outstream( output_filename, true, 0, false );
|
||||||
// check tty only once and don't try to delete a tty
|
// check tty only once and don't try to delete a tty
|
||||||
if( outfd < 0 || !check_tty_out() ) { close( infd ); return 1; }
|
if( outfd < 0 || !check_tty_out() ) { close( infd ); return 1; }
|
||||||
delete_output_on_interrupt = true;
|
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; }
|
{ tail_compress( cl_opts, infd, rbuf.u8(), encoder ); break; }
|
||||||
show_file_error( filename, bad_hdr_msg ); close( infd ); return 2;
|
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 edsize = parse_octal( rbuf.u8() + size_o, size_l );
|
||||||
const long long bufsize = round_up( edsize );
|
const long long bufsize = round_up( edsize );
|
||||||
// overflow or no extended data
|
// 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; }
|
{ show_file_error( filename, bad_hdr_msg ); close( infd ); return 2; }
|
||||||
if( !rbuf.resize( total_header_size + bufsize ) )
|
if( !rbuf.resize( total_header_size + bufsize ) )
|
||||||
{ show_file_error( filename, mem_msg ); close( infd ); return 1; }
|
{ 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 )
|
if( readblock( infd, rbuf.u8() + total_header_size, header_size ) != header_size )
|
||||||
{ show_file_error( filename, errno ? rderr_msg : end_msg, errno );
|
{ show_file_error( filename, errno ? rderr_msg : end_msg, errno );
|
||||||
close( infd ); return errno ? 1 : 2; }
|
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; }
|
{ show_file_error( filename, bad_hdr_msg ); close( infd ); return 2; }
|
||||||
const Typeflag typeflag2 = (Typeflag)(rbuf() + total_header_size)[typeflag_o];
|
const Typeflag typeflag2 = (Typeflag)(rbuf() + total_header_size)[typeflag_o];
|
||||||
if( typeflag2 == tf_extended || typeflag2 == tf_global )
|
if( typeflag2 == tf_extended || typeflag2 == tf_global )
|
||||||
|
@ -294,9 +300,7 @@ int compress_archive( const Cl_options & cl_opts,
|
||||||
rest -= rd;
|
rest -= rd;
|
||||||
if( rd != size )
|
if( rd != size )
|
||||||
{
|
{
|
||||||
if( verbosity >= 0 )
|
show_atpos_error( filename, file_size - rest, true );
|
||||||
std::fprintf( stderr, "'%s' ends unexpectedly at pos %llu\n",
|
|
||||||
filename, file_size - rest );
|
|
||||||
close( infd ); return 1;
|
close( infd ); return 1;
|
||||||
}
|
}
|
||||||
if( !archive_write( buf, size, encoder ) ) { 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
|
} // 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 )
|
int compress( const Cl_options & cl_opts )
|
||||||
{
|
{
|
||||||
if( cl_opts.num_files > 1 && cl_opts.output_filename.size() )
|
if( cl_opts.num_files > 1 && cl_opts.output_filename.size() )
|
||||||
|
|
67
configure
vendored
67
configure
vendored
|
@ -1,12 +1,12 @@
|
||||||
#! /bin/sh
|
#! /bin/sh
|
||||||
# configure script for Tarlz - Archiver with multimember lzip compression
|
# 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
|
# This configure script is free software: you have unlimited permission
|
||||||
# to copy, distribute, and modify it.
|
# to copy, distribute, and modify it.
|
||||||
|
|
||||||
pkgname=tarlz
|
pkgname=tarlz
|
||||||
pkgversion=0.23
|
pkgversion=0.24
|
||||||
progname=tarlz
|
progname=tarlz
|
||||||
srctrigger=doc/${pkgname}.texi
|
srctrigger=doc/${pkgname}.texi
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@ CPPFLAGS=
|
||||||
CXXFLAGS='-Wall -W -O2'
|
CXXFLAGS='-Wall -W -O2'
|
||||||
LDFLAGS=
|
LDFLAGS=
|
||||||
LIBS='-llz -lpthread'
|
LIBS='-llz -lpthread'
|
||||||
|
MAKEINFO=makeinfo
|
||||||
|
|
||||||
# checking whether we are using GNU C++.
|
# checking whether we are using GNU C++.
|
||||||
/bin/sh -c "${CXX} --version" > /dev/null 2>&1 || { CXX=c++ ; CXXFLAGS=-O2 ; }
|
/bin/sh -c "${CXX} --version" > /dev/null 2>&1 || { CXX=c++ ; CXXFLAGS=-O2 ; }
|
||||||
|
@ -35,7 +36,7 @@ no_create=
|
||||||
while [ $# != 0 ] ; do
|
while [ $# != 0 ] ; do
|
||||||
|
|
||||||
# Get the first arg, and shuffle
|
# Get the first arg, and shuffle
|
||||||
option=$1 ; arg2=no
|
option="$1" ; arg2=no
|
||||||
shift
|
shift
|
||||||
|
|
||||||
# Add the argument quoted to args
|
# Add the argument quoted to args
|
||||||
|
@ -43,12 +44,12 @@ while [ $# != 0 ] ; do
|
||||||
else args="${args} \"${option}\"" ; fi
|
else args="${args} \"${option}\"" ; fi
|
||||||
|
|
||||||
# Split out the argument for options that take them
|
# Split out the argument for options that take them
|
||||||
case ${option} in
|
case "${option}" in
|
||||||
*=*) optarg=`echo "${option}" | sed -e 's,^[^=]*=,,;s,/$,,'` ;;
|
*=*) optarg="`echo "${option}" | sed -e 's,^[^=]*=,,;s,/$,,'`" ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
# Process the options
|
# Process the options
|
||||||
case ${option} in
|
case "${option}" in
|
||||||
--help | -h)
|
--help | -h)
|
||||||
echo "Usage: $0 [OPTION]... [VAR=VALUE]..."
|
echo "Usage: $0 [OPTION]... [VAR=VALUE]..."
|
||||||
echo
|
echo
|
||||||
|
@ -58,7 +59,7 @@ while [ $# != 0 ] ; do
|
||||||
echo "Options and variables: [defaults in brackets]"
|
echo "Options and variables: [defaults in brackets]"
|
||||||
echo " -h, --help display this help and exit"
|
echo " -h, --help display this help and exit"
|
||||||
echo " -V, --version output version information 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 " --prefix=DIR install into DIR [${prefix}]"
|
||||||
echo " --exec-prefix=DIR base directory for arch-dependent files [${exec_prefix}]"
|
echo " --exec-prefix=DIR base directory for arch-dependent files [${exec_prefix}]"
|
||||||
echo " --bindir=DIR user executables directory [${bindir}]"
|
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 " CXXFLAGS+=OPTIONS append options to the current value of CXXFLAGS"
|
||||||
echo " LDFLAGS=OPTIONS command line options for the linker [${LDFLAGS}]"
|
echo " LDFLAGS=OPTIONS command line options for the linker [${LDFLAGS}]"
|
||||||
echo " LIBS=OPTIONS libraries to pass to the linker [${LIBS}]"
|
echo " LIBS=OPTIONS libraries to pass to the linker [${LIBS}]"
|
||||||
|
echo " MAKEINFO=NAME makeinfo program to use [${MAKEINFO}]"
|
||||||
echo
|
echo
|
||||||
exit 0 ;;
|
exit 0 ;;
|
||||||
--version | -V)
|
--version | -V)
|
||||||
echo "Configure script for ${pkgname} version ${pkgversion}"
|
echo "Configure script for ${pkgname} version ${pkgversion}"
|
||||||
exit 0 ;;
|
exit 0 ;;
|
||||||
--srcdir) srcdir=$1 ; arg2=yes ;;
|
--srcdir) srcdir="$1" ; arg2=yes ;;
|
||||||
--prefix) prefix=$1 ; arg2=yes ;;
|
--prefix) prefix="$1" ; arg2=yes ;;
|
||||||
--exec-prefix) exec_prefix=$1 ; arg2=yes ;;
|
--exec-prefix) exec_prefix="$1" ; arg2=yes ;;
|
||||||
--bindir) bindir=$1 ; arg2=yes ;;
|
--bindir) bindir="$1" ; arg2=yes ;;
|
||||||
--datarootdir) datarootdir=$1 ; arg2=yes ;;
|
--datarootdir) datarootdir="$1" ; arg2=yes ;;
|
||||||
--infodir) infodir=$1 ; arg2=yes ;;
|
--infodir) infodir="$1" ; arg2=yes ;;
|
||||||
--mandir) mandir=$1 ; arg2=yes ;;
|
--mandir) mandir="$1" ; arg2=yes ;;
|
||||||
|
|
||||||
--srcdir=*) srcdir=${optarg} ;;
|
--srcdir=*) srcdir="${optarg}" ;;
|
||||||
--prefix=*) prefix=${optarg} ;;
|
--prefix=*) prefix="${optarg}" ;;
|
||||||
--exec-prefix=*) exec_prefix=${optarg} ;;
|
--exec-prefix=*) exec_prefix="${optarg}" ;;
|
||||||
--bindir=*) bindir=${optarg} ;;
|
--bindir=*) bindir="${optarg}" ;;
|
||||||
--datarootdir=*) datarootdir=${optarg} ;;
|
--datarootdir=*) datarootdir="${optarg}" ;;
|
||||||
--infodir=*) infodir=${optarg} ;;
|
--infodir=*) infodir="${optarg}" ;;
|
||||||
--mandir=*) mandir=${optarg} ;;
|
--mandir=*) mandir="${optarg}" ;;
|
||||||
--no-create) no_create=yes ;;
|
--no-create) no_create=yes ;;
|
||||||
|
|
||||||
CXX=*) CXX=${optarg} ;;
|
CXX=*) CXX="${optarg}" ;;
|
||||||
CPPFLAGS=*) CPPFLAGS=${optarg} ;;
|
CPPFLAGS=*) CPPFLAGS="${optarg}" ;;
|
||||||
CXXFLAGS=*) CXXFLAGS=${optarg} ;;
|
CXXFLAGS=*) CXXFLAGS="${optarg}" ;;
|
||||||
CXXFLAGS+=*) CXXFLAGS="${CXXFLAGS} ${optarg}" ;;
|
CXXFLAGS+=*) CXXFLAGS="${CXXFLAGS} ${optarg}" ;;
|
||||||
LDFLAGS=*) LDFLAGS=${optarg} ;;
|
LDFLAGS=*) LDFLAGS="${optarg}" ;;
|
||||||
LIBS=*) LIBS="${optarg} ${LIBS}" ;;
|
LIBS=*) LIBS="${optarg} ${LIBS}" ;;
|
||||||
|
MAKEINFO=*) MAKEINFO="${optarg}" ;;
|
||||||
|
|
||||||
--*)
|
--*)
|
||||||
echo "configure: WARNING: unrecognized option: '${option}'" 1>&2 ;;
|
echo "configure: WARNING: unrecognized option: '${option}'" 1>&2 ;;
|
||||||
|
@ -118,19 +121,19 @@ while [ $# != 0 ] ; do
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# Find the source files, if location was not specified.
|
# Find the source code, if location was not specified.
|
||||||
srcdirtext=
|
srcdirtext=
|
||||||
if [ -z "${srcdir}" ] ; then
|
if [ -z "${srcdir}" ] ; then
|
||||||
srcdirtext="or . or .." ; srcdir=.
|
srcdirtext="or . or .." ; srcdir=.
|
||||||
if [ ! -r "${srcdir}/${srctrigger}" ] ; then srcdir=.. ; fi
|
if [ ! -r "${srcdir}/${srctrigger}" ] ; then srcdir=.. ; fi
|
||||||
if [ ! -r "${srcdir}/${srctrigger}" ] ; then
|
if [ ! -r "${srcdir}/${srctrigger}" ] ; then
|
||||||
## the sed command below emulates the dirname command
|
## 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
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ ! -r "${srcdir}/${srctrigger}" ] ; then
|
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
|
echo "configure: (At least ${srctrigger} is missing)." 1>&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
@ -150,7 +153,7 @@ if [ -z "${no_create}" ] ; then
|
||||||
# This script is free software: you have unlimited permission
|
# This script is free software: you have unlimited permission
|
||||||
# to copy, distribute, and modify it.
|
# to copy, distribute, and modify it.
|
||||||
|
|
||||||
exec /bin/sh $0 ${args} --no-create
|
exec /bin/sh "$0" ${args} --no-create
|
||||||
EOF
|
EOF
|
||||||
chmod +x config.status
|
chmod +x config.status
|
||||||
fi
|
fi
|
||||||
|
@ -168,10 +171,11 @@ echo "CPPFLAGS = ${CPPFLAGS}"
|
||||||
echo "CXXFLAGS = ${CXXFLAGS}"
|
echo "CXXFLAGS = ${CXXFLAGS}"
|
||||||
echo "LDFLAGS = ${LDFLAGS}"
|
echo "LDFLAGS = ${LDFLAGS}"
|
||||||
echo "LIBS = ${LIBS}"
|
echo "LIBS = ${LIBS}"
|
||||||
|
echo "MAKEINFO = ${MAKEINFO}"
|
||||||
rm -f Makefile
|
rm -f Makefile
|
||||||
cat > Makefile << EOF
|
cat > Makefile << EOF
|
||||||
# Makefile for Tarlz - Archiver with multimember lzip compression
|
# 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 file was generated automatically by configure. Don't edit.
|
||||||
#
|
#
|
||||||
# This Makefile is free software: you have unlimited permission
|
# This Makefile is free software: you have unlimited permission
|
||||||
|
@ -192,9 +196,10 @@ CPPFLAGS = ${CPPFLAGS}
|
||||||
CXXFLAGS = ${CXXFLAGS}
|
CXXFLAGS = ${CXXFLAGS}
|
||||||
LDFLAGS = ${LDFLAGS}
|
LDFLAGS = ${LDFLAGS}
|
||||||
LIBS = ${LIBS}
|
LIBS = ${LIBS}
|
||||||
|
MAKEINFO = ${MAKEINFO}
|
||||||
EOF
|
EOF
|
||||||
cat "${srcdir}/Makefile.in" >> Makefile
|
cat "${srcdir}/Makefile.in" >> Makefile
|
||||||
|
|
||||||
echo "OK. Now you can run make."
|
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)."
|
echo "installed (see INSTALL)."
|
||||||
|
|
166
create.cc
166
create.cc
|
@ -1,5 +1,5 @@
|
||||||
/* Tarlz - Archiver with multimember lzip compression
|
/* 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
|
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
|
it under the terms of the GNU General Public License as published by
|
||||||
|
@ -20,8 +20,6 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdlib>
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <stdint.h> // for lzlib.h
|
#include <stdint.h> // for lzlib.h
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <sys/stat.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
|
unsigned long long partial_data_size = 0; // size of current block
|
||||||
Resizable_buffer grbuf; // extended header + data
|
Resizable_buffer grbuf; // extended header + data
|
||||||
int goutfd = -1;
|
int goutfd = -1;
|
||||||
int error_status = 0;
|
|
||||||
|
|
||||||
|
|
||||||
bool option_C_after_relative_filename( const Arg_parser & parser )
|
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 == 0 && errno == 0 ) return 0; // append to empty archive
|
||||||
if( rd < min_member_size || ( rd != bufsize && errno ) ) return -1;
|
if( rd < min_member_size || ( rd != bufsize && errno ) ) return -1;
|
||||||
const Lzip_header * const p = (const Lzip_header *)buf; // shut up gcc
|
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
|
LZ_Decoder * decoder = LZ_decompress_open(); // decompress first header
|
||||||
if( !decoder || LZ_decompress_errno( decoder ) != LZ_ok ||
|
if( !decoder || LZ_decompress_errno( decoder ) != LZ_ok ||
|
||||||
LZ_decompress_write( decoder, buf, rd ) != rd ||
|
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 ); return -1; }
|
||||||
LZ_decompress_close( decoder );
|
LZ_decompress_close( decoder );
|
||||||
const bool maybe_eoa = block_is_zero( buf, header_size );
|
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 );
|
const long long end = lseek( fd, 0, SEEK_END );
|
||||||
if( end < min_member_size ) return -1;
|
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
|
Lzip_header header; // read last header
|
||||||
if( seek_read( fd, header.data, Lzip_header::size,
|
if( seek_read( fd, header.data, Lzip_header::size,
|
||||||
end - member_size ) != Lzip_header::size ) return -1;
|
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;
|
!isvalid_ds( header.dictionary_size() ) ) return -1;
|
||||||
|
|
||||||
// EOA marker in last member must contain between 512 and 32256 zeros alone
|
// 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 );
|
const int rd = readblock( fd, header, header_size );
|
||||||
if( rd == 0 && errno == 0 ) break; // missing EOA blocks
|
if( rd == 0 && errno == 0 ) break; // missing EOA blocks
|
||||||
if( rd != header_size ) return -1;
|
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; }
|
{ if( block_is_zero( header, header_size ) ) break; else return -1; }
|
||||||
const Typeflag typeflag = (Typeflag)header[typeflag_o];
|
const Typeflag typeflag = (Typeflag)header[typeflag_o];
|
||||||
if( typeflag == tf_extended || typeflag == tf_global )
|
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;
|
if( prev_extended ) return -1;
|
||||||
const long long edsize = parse_octal( header + size_o, size_l );
|
const long long edsize = parse_octal( header + size_o, size_l );
|
||||||
const long long bufsize = round_up( edsize );
|
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
|
return -1; // overflow or no extended data
|
||||||
if( !rbuf.resize( bufsize ) ) return -2;
|
if( !rbuf.resize( bufsize ) ) return -2;
|
||||||
if( readblock( fd, rbuf.u8(), bufsize ) != bufsize )
|
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.
|
// Return true if it stores filename in the ustar header.
|
||||||
bool store_name( const char * const filename, Extended & extended,
|
bool store_name( const char * const filename, Extended & extended,
|
||||||
Tar_header header, const bool force_extended_name )
|
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;
|
const int infd = file_size ? open_instream( filename ) : -1;
|
||||||
if( file_size && infd < 0 ) { set_error_status( 1 ); return 0; }
|
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 &&
|
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;
|
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 )
|
if( file_size )
|
||||||
{
|
{
|
||||||
const long long bufsize = 32 * header_size;
|
const long long bufsize = 32 * header_size;
|
||||||
|
@ -278,9 +263,7 @@ int add_member( const char * const filename, const struct stat *,
|
||||||
rest -= rd;
|
rest -= rd;
|
||||||
if( rd != size )
|
if( rd != size )
|
||||||
{
|
{
|
||||||
if( verbosity >= 0 )
|
show_atpos_error( filename, file_size - rest, false );
|
||||||
std::fprintf( stderr, "File '%s' ends unexpectedly at pos %llu\n",
|
|
||||||
filename, file_size - rest );
|
|
||||||
close( infd ); return 1;
|
close( infd ); return 1;
|
||||||
}
|
}
|
||||||
if( rest == 0 ) // last read
|
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
|
// set file_size != 0 only for regular files
|
||||||
bool fill_headers( const char * const filename, Extended & extended,
|
bool fill_headers( const char * const filename, Extended & extended,
|
||||||
Tar_header header, long long & file_size, const int flag )
|
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 file_size,
|
||||||
const unsigned long long target_size,
|
const unsigned long long target_size,
|
||||||
unsigned long long & partial_data_size )
|
unsigned long long & partial_data_size )
|
||||||
{
|
{
|
||||||
const unsigned long long member_size = // may overflow 'long long'
|
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 ||
|
if( partial_data_size >= target_size ||
|
||||||
( partial_data_size >= min_data_size &&
|
( partial_data_size >= min_data_size &&
|
||||||
partial_data_size + member_size / 2 > target_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 ustar_chksum( const Tar_header header )
|
||||||
{
|
{
|
||||||
unsigned chksum = chksum_l * 0x20; // treat chksum field as spaces
|
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 )
|
bool check_ustar_chksum( const Tar_header header )
|
||||||
{ return ( verify_ustar_magic( header ) &&
|
{ return ( check_ustar_magic( header ) &&
|
||||||
ustar_chksum( header ) == parse_octal( header + chksum_o, chksum_l ) ); }
|
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 )
|
int concatenate( const Cl_options & cl_opts )
|
||||||
{
|
{
|
||||||
if( cl_opts.num_files <= 0 )
|
if( cl_opts.num_files <= 0 )
|
||||||
{ if( verbosity >= 1 ) show_error( "Nothing to concatenate." ); return 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();
|
const bool to_stdout = cl_opts.archive_name.empty();
|
||||||
archive_namep = to_stdout ? "(stdout)" : cl_opts.archive_name.c_str();
|
archive_namep = to_stdout ? "(stdout)" : cl_opts.archive_name.c_str();
|
||||||
const int outfd =
|
const int outfd =
|
||||||
|
@ -604,25 +558,18 @@ int concatenate( const Cl_options & cl_opts )
|
||||||
{ close( outfd ); return 1; }
|
{ close( outfd ); return 1; }
|
||||||
if( !to_stdout && !archive_attrs.init( outfd ) )
|
if( !to_stdout && !archive_attrs.init( outfd ) )
|
||||||
{ show_file_error( archive_namep, "Can't stat", errno ); return 1; }
|
{ show_file_error( archive_namep, "Can't stat", errno ); return 1; }
|
||||||
int compressed; // tri-state bool
|
if( !to_stdout && compressed >= 0 ) // level or ext are set in cl
|
||||||
if( to_stdout ) compressed = -1; // unknown
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
compressed = has_lz_ext( cl_opts.archive_name ); // default value
|
const long long pos = compressed ?
|
||||||
long long pos = check_compressed_appendable( outfd, true );
|
check_compressed_appendable( outfd, true ) :
|
||||||
if( pos > 0 ) compressed = true;
|
check_uncompressed_appendable( outfd, true );
|
||||||
else if( pos < 0 )
|
if( pos == -2 ) { show_error( mem_msg ); close( outfd ); return 1; }
|
||||||
{
|
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 ?
|
{ show_file_error( archive_namep, compressed ?
|
||||||
"This does not look like an appendable tar.lz archive." :
|
"This does not look like an appendable tar.lz archive." :
|
||||||
"This does not look like an appendable tar archive." );
|
"This does not look like an appendable tar archive." );
|
||||||
close( outfd ); return 2; }
|
close( outfd ); return 2; }
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
int retval = 0;
|
int retval = 0;
|
||||||
bool eoa_pending = false;
|
bool eoa_pending = false;
|
||||||
|
@ -634,9 +581,10 @@ int concatenate( const Cl_options & cl_opts )
|
||||||
const int infd = open_instream( filename );
|
const int infd = open_instream( filename );
|
||||||
if( infd < 0 ) { retval = 1; break; }
|
if( infd < 0 ) { retval = 1; break; }
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if( !to_stdout && fstat( infd, &st ) == 0 && archive_attrs.is_the_archive( st ) )
|
if( !to_stdout && fstat( infd, &st ) == 0 &&
|
||||||
{ show_file_error( filename, "Archive can't contain itself; not concatenated." );
|
archive_attrs.is_the_archive( st ) )
|
||||||
close( infd ); continue; }
|
{ show_file_error( filename, "Archive can't contain itself; "
|
||||||
|
"not concatenated." ); close( infd ); continue; }
|
||||||
long long size;
|
long long size;
|
||||||
if( compressed < 0 ) // not initialized yet
|
if( compressed < 0 ) // not initialized yet
|
||||||
{
|
{
|
||||||
|
@ -644,7 +592,7 @@ int concatenate( const Cl_options & cl_opts )
|
||||||
compressed = true;
|
compressed = true;
|
||||||
else if( ( size = check_uncompressed_appendable( infd, false ) ) > 0 )
|
else if( ( size = check_uncompressed_appendable( infd, false ) ) > 0 )
|
||||||
compressed = false;
|
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 ) :
|
else size = compressed ? check_compressed_appendable( infd, false ) :
|
||||||
check_uncompressed_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 )
|
int encode( const Cl_options & cl_opts )
|
||||||
{
|
{
|
||||||
if( !grbuf.size() ) { show_error( mem_msg ); return 1; }
|
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();
|
const bool to_stdout = cl_opts.archive_name.empty();
|
||||||
archive_namep = to_stdout ? "(stdout)" : cl_opts.archive_name.c_str();
|
archive_namep = to_stdout ? "(stdout)" : cl_opts.archive_name.c_str();
|
||||||
gcl_opts = &cl_opts;
|
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;
|
const bool append = cl_opts.program_mode == m_append;
|
||||||
if( cl_opts.num_files <= 0 )
|
if( cl_opts.num_files <= 0 )
|
||||||
{
|
{
|
||||||
|
@ -701,19 +646,24 @@ int encode( const Cl_options & cl_opts )
|
||||||
{ close( goutfd ); return 1; }
|
{ close( goutfd ); return 1; }
|
||||||
if( append && !to_stdout )
|
if( append && !to_stdout )
|
||||||
{
|
{
|
||||||
if( compressed && check_compressed_appendable( goutfd, true ) < 0 )
|
long long pos;
|
||||||
{ show_file_error( archive_namep,
|
if( compressed < 0 ) // not initialized yet
|
||||||
"This does not look like an appendable tar.lz archive." );
|
|
||||||
close( goutfd ); return 2; }
|
|
||||||
if( !compressed )
|
|
||||||
{
|
{
|
||||||
const long long pos = check_uncompressed_appendable( goutfd, true );
|
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 == -2 ) { show_error( mem_msg ); close( goutfd ); return 1; }
|
||||||
if( pos < 0 ) { show_file_error( archive_namep,
|
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." );
|
"This does not look like an appendable tar archive." );
|
||||||
close( goutfd ); return 2; }
|
close( goutfd ); return 2; }
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if( !archive_attrs.init( goutfd ) )
|
if( !archive_attrs.init( goutfd ) )
|
||||||
{ show_file_error( archive_namep, "Can't stat", errno );
|
{ show_file_error( archive_namep, "Can't stat", errno );
|
||||||
|
|
3
create.h
3
create.h
|
@ -1,5 +1,5 @@
|
||||||
/* Tarlz - Archiver with multimember lzip compression
|
/* 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
|
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
|
it under the terms of the GNU General Public License as published by
|
||||||
|
@ -45,4 +45,3 @@ public:
|
||||||
extern Archive_attrs archive_attrs;
|
extern Archive_attrs archive_attrs;
|
||||||
|
|
||||||
const char * const cant_stat = "Can't stat input file";
|
const char * const cant_stat = "Can't stat input file";
|
||||||
const char * const eferec_msg = "Error formatting extended records.";
|
|
||||||
|
|
44
create_lz.cc
44
create_lz.cc
|
@ -1,5 +1,5 @@
|
||||||
/* Tarlz - Archiver with multimember lzip compression
|
/* 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
|
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
|
it under the terms of the GNU General Public License as published by
|
||||||
|
@ -20,7 +20,6 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdlib>
|
|
||||||
#include <queue>
|
#include <queue>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <stdint.h> // for lzlib.h
|
#include <stdint.h> // for lzlib.h
|
||||||
|
@ -31,6 +30,7 @@
|
||||||
|
|
||||||
#include "tarlz.h"
|
#include "tarlz.h"
|
||||||
#include "arg_parser.h"
|
#include "arg_parser.h"
|
||||||
|
#include "common_mutex.h"
|
||||||
#include "create.h"
|
#include "create.h"
|
||||||
|
|
||||||
|
|
||||||
|
@ -87,9 +87,9 @@ struct Ipacket // filename, file size and headers
|
||||||
const uint8_t * const header;
|
const uint8_t * const header;
|
||||||
|
|
||||||
Ipacket() : file_size( 0 ), extended( 0 ), header( 0 ) {}
|
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 )
|
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
|
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; }
|
{ delete[] header; delete extended; return 0; }
|
||||||
print_removed_prefix( extended->removed_prefix );
|
print_removed_prefix( extended->removed_prefix );
|
||||||
|
|
||||||
if( gcl_opts->solidity == bsolid &&
|
if( gcl_opts->solidity == bsolid )
|
||||||
block_is_full( extended->full_size(), file_size, gcl_opts->data_size,
|
{
|
||||||
|
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 ) )
|
partial_data_size ) )
|
||||||
courierp->receive_packet( new Ipacket ); // end of group
|
courierp->receive_packet( new Ipacket ); // end of group
|
||||||
|
}
|
||||||
courierp->receive_packet( new Ipacket( filename, file_size, extended, header ) );
|
courierp->receive_packet( new Ipacket( filename, file_size, extended, header ) );
|
||||||
|
|
||||||
if( gcl_opts->solidity == no_solid ) // one tar member per group
|
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
|
/* Write ibuf to encoder. To minimize dictionary size, do not read from
|
||||||
from encoder until encoder's input buffer is full or finish is true.
|
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.
|
Send opacket to courier and allocate new obuf each time obuf is full.
|
||||||
*/
|
*/
|
||||||
void loop_encode( const uint8_t * const ibuf, const int isize,
|
void loop_encode( const uint8_t * const ibuf, const int isize,
|
||||||
uint8_t * & obuf, int & opos, Packet_courier & courier,
|
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 char * const filename = ipacket->filename.c_str();
|
||||||
const int infd =
|
const int infd = ipacket->file_size ? open_instream( filename ) : -1;
|
||||||
ipacket->file_size ? open_instream( filename ) : -1;
|
|
||||||
if( ipacket->file_size && infd < 0 ) // can't read file data
|
if( ipacket->file_size && infd < 0 ) // can't read file data
|
||||||
{ delete[] ipacket->header; delete ipacket->extended; delete ipacket;
|
{ delete[] ipacket->header; delete ipacket->extended; delete ipacket;
|
||||||
set_error_status( 1 ); continue; } // skip file
|
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 int ebsize = ipacket->extended->format_block( rbuf ); // may be 0
|
||||||
{
|
|
||||||
const long long ebsize = ipacket->extended->format_block( rbuf );
|
|
||||||
if( ebsize < 0 )
|
if( ebsize < 0 )
|
||||||
{ show_error( ( ebsize == -2 ) ? mem_msg2 : eferec_msg ); exit_fail_mt(); }
|
{ show_error( ipacket->extended->full_size_error() ); exit_fail_mt(); }
|
||||||
/* Limit the size of the extended block to INT_MAX - 1 so that it can
|
if( ebsize > 0 ) // compress extended block
|
||||||
be fed to lzlib as one buffer. */
|
|
||||||
if( ebsize >= INT_MAX )
|
|
||||||
{ show_error( "Extended records size >= INT_MAX." ); exit_fail_mt(); }
|
|
||||||
loop_encode( rbuf.u8(), ebsize, data, opos, courier, encoder, worker_id );
|
loop_encode( rbuf.u8(), ebsize, data, opos, courier, encoder, worker_id );
|
||||||
}
|
|
||||||
// compress ustar header
|
// compress ustar header
|
||||||
loop_encode( ipacket->header, header_size, data, opos, courier,
|
loop_encode( ipacket->header, header_size, data, opos, courier,
|
||||||
encoder, worker_id );
|
encoder, worker_id );
|
||||||
|
@ -472,9 +468,7 @@ extern "C" void * cworker( void * arg )
|
||||||
rest -= rd;
|
rest -= rd;
|
||||||
if( rd != size )
|
if( rd != size )
|
||||||
{
|
{
|
||||||
if( verbosity >= 0 )
|
show_atpos_error( filename, ipacket->file_size - rest, false );
|
||||||
std::fprintf( stderr, "File '%s' ends unexpectedly at pos %llu\n",
|
|
||||||
filename, ipacket->file_size - rest );
|
|
||||||
close( infd ); exit_fail_mt();
|
close( infd ); exit_fail_mt();
|
||||||
}
|
}
|
||||||
if( rest == 0 ) // last read
|
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.ocheck_counter,
|
||||||
courier.owait_counter );
|
courier.owait_counter );
|
||||||
|
|
||||||
if( !courier.finished() ) internal_error( "courier not finished." );
|
if( !courier.finished() ) internal_error( conofin_msg );
|
||||||
return final_exit_status( retval );
|
return final_exit_status( retval );
|
||||||
}
|
}
|
||||||
|
|
78
decode.cc
78
decode.cc
|
@ -1,5 +1,5 @@
|
||||||
/* Tarlz - Archiver with multimember lzip compression
|
/* 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
|
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
|
it under the terms of the GNU General Public License as published by
|
||||||
|
@ -21,7 +21,7 @@
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdlib>
|
#include <fcntl.h>
|
||||||
#include <stdint.h> // for lzlib.h
|
#include <stdint.h> // for lzlib.h
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <utime.h>
|
#include <utime.h>
|
||||||
|
@ -246,9 +246,37 @@ void format_file_diff( std::string & ostr, const char * const filename,
|
||||||
{ if( verbosity >= 0 )
|
{ if( verbosity >= 0 )
|
||||||
{ ostr += filename; ostr += ": "; ostr += msg; ostr += '\n'; } }
|
{ 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
|
} // 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,
|
bool compare_file_type( std::string & estr, std::string & ostr,
|
||||||
const Cl_options & cl_opts,
|
const Cl_options & cl_opts,
|
||||||
const Extended & extended, const Tar_header header )
|
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 );
|
const Archive_descriptor ad( cl_opts.archive_name );
|
||||||
if( ad.infd < 0 ) return 1;
|
if( ad.infd < 0 ) return 1;
|
||||||
|
|
||||||
// Execute -C options and mark filenames to be compared, extracted or listed.
|
const bool c_present = option_C_present( cl_opts.parser ) &&
|
||||||
// name_pending is of type char instead of bool to allow concurrent update.
|
cl_opts.program_mode != m_list;
|
||||||
std::vector< char > name_pending( cl_opts.parser.arguments(), false );
|
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 )
|
for( int i = 0; i < cl_opts.parser.arguments(); ++i )
|
||||||
{
|
{
|
||||||
const int code = cl_opts.parser.code( i );
|
if( cl_opts.parser.code( i ) != 'C' ) continue;
|
||||||
if( code == 'C' && cl_opts.program_mode != m_list )
|
|
||||||
{
|
|
||||||
const char * const dir = cl_opts.parser.argument( i ).c_str();
|
const char * const dir = cl_opts.parser.argument( i ).c_str();
|
||||||
if( chdir( dir ) != 0 )
|
if( chdir( dir ) != 0 )
|
||||||
{ show_file_error( dir, chdir_msg, errno ); return 1; }
|
{ 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() ) )
|
!Exclude::excluded( cl_opts.parser.argument( i ).c_str() ) )
|
||||||
name_pending[i] = true;
|
name_pending[i] = true;
|
||||||
}
|
|
||||||
|
|
||||||
// multi-threaded --list is faster even with 1 thread and 1 file in archive
|
/* 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
|
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 ||
|
CWD is not per-thread; multi-threaded decode can't be used if a
|
||||||
cl_opts.program_mode == m_extract ) && cl_opts.num_workers > 0 &&
|
-C option appears after a file name in the command line. */
|
||||||
ad.indexed && ad.lzip_index.members() >= 2 ) // one file + EOA
|
if( cl_opts.num_workers > 0 && !c_after_name && ad.indexed &&
|
||||||
{
|
ad.lzip_index.members() >= 2 ) // 2 lzip members may be 1 file + EOA
|
||||||
// show_file_error( ad.namep, "Is compressed seekable" );
|
|
||||||
return decode_lz( cl_opts, ad, name_pending );
|
return decode_lz( cl_opts, ad, name_pending );
|
||||||
}
|
|
||||||
|
|
||||||
Archive_reader ar( ad ); // serial reader
|
Archive_reader ar( ad ); // serial reader
|
||||||
Extended extended; // metadata from extended records
|
Extended extended; // metadata from extended records
|
||||||
|
@ -416,7 +449,7 @@ int decode( const Cl_options & cl_opts )
|
||||||
Tar_header header;
|
Tar_header header;
|
||||||
const int ret = ar.read( header, header_size );
|
const int ret = ar.read( header, header_size );
|
||||||
if( ret != 0 ) { read_error( ar ); if( ar.fatal() ) { retval = ret; break; } }
|
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
|
if( ret == 0 && block_is_zero( header, header_size ) ) // EOA
|
||||||
{
|
{
|
||||||
|
@ -461,9 +494,10 @@ int decode( const Cl_options & cl_opts )
|
||||||
|
|
||||||
extended.fill_from_ustar( header ); // copy metadata from header
|
extended.fill_from_ustar( header ); // copy metadata from header
|
||||||
|
|
||||||
|
try {
|
||||||
// members without name are skipped except when listing
|
// members without name are skipped except when listing
|
||||||
if( check_skip_filename( cl_opts, name_pending, extended.path().c_str() ) )
|
if( check_skip_filename( cl_opts, name_pending, extended.path().c_str(),
|
||||||
retval = skip_member( ar, extended, typeflag );
|
chdir_fd ) ) retval = skip_member( ar, extended, typeflag );
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
print_removed_prefix( extended.removed_prefix );
|
print_removed_prefix( extended.removed_prefix );
|
||||||
|
@ -475,6 +509,8 @@ int decode( const Cl_options & cl_opts )
|
||||||
retval = compare_member( cl_opts, ar, extended, header );
|
retval = compare_member( cl_opts, ar, extended, header );
|
||||||
else retval = extract_member( cl_opts, ar, extended, header );
|
else retval = extract_member( cl_opts, ar, extended, header );
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
catch( Chdir_error & ) { retval = 1; }
|
||||||
extended.reset();
|
extended.reset();
|
||||||
if( retval )
|
if( retval )
|
||||||
{ show_error( "Error is not recoverable: exiting now." ); break; }
|
{ 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
|
/* 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
|
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
|
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 mkfifo_msg = "Can't create FIFO file";
|
||||||
const char * const uftype_msg = "%s: Unknown file type '%c', skipping.";
|
const char * const uftype_msg = "%s: Unknown file type '%c', skipping.";
|
||||||
const char * const chown_msg = "Can't change file owner";
|
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
|
/* 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
|
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
|
it under the terms of the GNU General Public License as published by
|
||||||
|
@ -20,7 +20,6 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdlib>
|
|
||||||
#include <queue>
|
#include <queue>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <stdint.h> // for lzlib.h
|
#include <stdint.h> // for lzlib.h
|
||||||
|
@ -37,6 +36,7 @@
|
||||||
#include "arg_parser.h"
|
#include "arg_parser.h"
|
||||||
#include "lzip_index.h"
|
#include "lzip_index.h"
|
||||||
#include "archive_reader.h"
|
#include "archive_reader.h"
|
||||||
|
#include "common_mutex.h"
|
||||||
#include "decode.h"
|
#include "decode.h"
|
||||||
|
|
||||||
/* When a problem is detected by any worker:
|
/* When a problem is detected by any worker:
|
||||||
|
@ -218,7 +218,7 @@ public:
|
||||||
|
|
||||||
bool reserve_name( const unsigned worker_id, const std::string & filename )
|
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 =
|
const unsigned crc =
|
||||||
crc32c.compute_crc( (const uint8_t *)filename.c_str(), filename.size() );
|
crc32c.compute_crc( (const uint8_t *)filename.c_str(), filename.size() );
|
||||||
xlock( &mutex );
|
xlock( &mutex );
|
||||||
|
@ -576,7 +576,7 @@ extern "C" void * dworker( void * arg )
|
||||||
courier.collect_packet( i, worker_id, ar.e_msg(),
|
courier.collect_packet( i, worker_id, ar.e_msg(),
|
||||||
( ret == 1 ) ? Packet::error1 : Packet::error2, ar.e_code() );
|
( ret == 1 ) ? Packet::error1 : Packet::error2, ar.e_code() );
|
||||||
goto done; }
|
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( !courier.request_mastership( i, worker_id ) ) goto done;
|
||||||
if( block_is_zero( header, header_size ) ) // EOA
|
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.ocheck_counter,
|
||||||
courier.owait_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 );
|
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
|
/* 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
|
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
|
it under the terms of the GNU General Public License as published by
|
||||||
|
@ -20,7 +20,6 @@
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdlib>
|
|
||||||
#include <stdint.h> // for lzlib.h
|
#include <stdint.h> // for lzlib.h
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <lzlib.h>
|
#include <lzlib.h>
|
||||||
|
@ -101,8 +100,8 @@ int delete_members( const Cl_options & cl_opts )
|
||||||
if( !ad.seekable )
|
if( !ad.seekable )
|
||||||
{ show_file_error( ad.namep, "Archive is not seekable." ); return 1; }
|
{ show_file_error( ad.namep, "Archive is not seekable." ); return 1; }
|
||||||
if( ad.lzip_index.file_size() < 3 * header_size )
|
if( ad.lzip_index.file_size() < 3 * header_size )
|
||||||
{ show_file_error( ad.namep, has_lz_ext( ad.name ) ? posix_lz_msg : posix_msg );
|
{ show_file_error( ad.namep, has_lz_ext( ad.name ) ?
|
||||||
return 2; }
|
posix_lz_msg : posix_msg ); return 2; }
|
||||||
// archive is uncompressed seekable, unless compressed corrupt
|
// archive is uncompressed seekable, unless compressed corrupt
|
||||||
|
|
||||||
Archive_reader ar( ad ); // serial reader
|
Archive_reader ar( ad ); // serial reader
|
||||||
|
@ -121,7 +120,7 @@ int delete_members( const Cl_options & cl_opts )
|
||||||
Tar_header header;
|
Tar_header header;
|
||||||
if( ( retval = ar.read( header, header_size ) ) != 0 )
|
if( ( retval = ar.read( header, header_size ) ) != 0 )
|
||||||
{ show_file_error( ad.namep, ar.e_msg(), ar.e_code() ); break; }
|
{ 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
|
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; }
|
{ show_file_error( ad.namep, fv_msg1 ); retval = 2; }
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// posix format already verified by archive reader
|
// posix format already checked by archive reader
|
||||||
show_file_error( ad.namep, bad_hdr_msg );
|
show_file_error( ad.namep, bad_hdr_msg );
|
||||||
retval = 2; break;
|
retval = 2; break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/* Tarlz - Archiver with multimember lzip compression
|
/* 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
|
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
|
it under the terms of the GNU General Public License as published by
|
||||||
|
@ -20,7 +20,6 @@
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdlib>
|
|
||||||
#include <stdint.h> // for lzlib.h
|
#include <stdint.h> // for lzlib.h
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <lzlib.h>
|
#include <lzlib.h>
|
||||||
|
@ -65,7 +64,7 @@ int delete_members_lz( const Cl_options & cl_opts,
|
||||||
Tar_header header;
|
Tar_header header;
|
||||||
if( ( retval = ar.read( header, header_size ) ) != 0 )
|
if( ( retval = ar.read( header, header_size ) ) != 0 )
|
||||||
{ show_file_error( ad.namep, ar.e_msg(), ar.e_code() ); goto done; }
|
{ 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
|
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; }
|
{ show_file_error( ad.namep, fv_msg1 ); retval = 2; }
|
||||||
goto done;
|
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 ) ?
|
show_file_error( ad.namep, ( ar.data_pos() > header_size ) ?
|
||||||
bad_hdr_msg : posix_lz_msg );
|
bad_hdr_msg : posix_lz_msg );
|
||||||
retval = 2;
|
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() ) )
|
if( !check_skip_filename( cl_opts, name_pending, extended.path().c_str() ) )
|
||||||
{
|
{
|
||||||
print_removed_prefix( extended.removed_prefix );
|
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() )
|
if( member_begin != ad.lzip_index.dblock( i ).pos() || !ar.at_member_end() )
|
||||||
{ show_file_error( extended.path().c_str(),
|
{ show_file_error( extended.path().c_str(),
|
||||||
"Can't delete: not compressed individually." );
|
"Can't delete: not compressed individually." );
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.16.
|
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.2.
|
||||||
.TH TARLZ "1" "September 2022" "tarlz 0.23" "User Commands"
|
.TH TARLZ "1" "September 2023" "tarlz 0.24" "User Commands"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
tarlz \- creates tar archives with multimember lzip compression
|
tarlz \- creates tar archives with multimember lzip compression
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
|
@ -80,7 +80,7 @@ follow symlinks; archive the files they point to
|
||||||
set number of (de)compression threads [2]
|
set number of (de)compression threads [2]
|
||||||
.TP
|
.TP
|
||||||
\fB\-o\fR, \fB\-\-output=\fR<file>
|
\fB\-o\fR, \fB\-\-output=\fR<file>
|
||||||
compress to <file>
|
compress to <file> ('\-' for stdout)
|
||||||
.TP
|
.TP
|
||||||
\fB\-p\fR, \fB\-\-preserve\-permissions\fR
|
\fB\-p\fR, \fB\-\-preserve\-permissions\fR
|
||||||
don't subtract the umask on extraction
|
don't subtract the umask on extraction
|
||||||
|
@ -157,7 +157,7 @@ Report bugs to lzip\-bug@nongnu.org
|
||||||
.br
|
.br
|
||||||
Tarlz home page: http://www.nongnu.org/lzip/tarlz.html
|
Tarlz home page: http://www.nongnu.org/lzip/tarlz.html
|
||||||
.SH COPYRIGHT
|
.SH COPYRIGHT
|
||||||
Copyright \(co 2022 Antonio Diaz Diaz.
|
Copyright \(co 2023 Antonio Diaz Diaz.
|
||||||
Using lzlib 1.13
|
Using lzlib 1.13
|
||||||
License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html>
|
License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html>
|
||||||
.br
|
.br
|
||||||
|
|
140
doc/tarlz.info
140
doc/tarlz.info
|
@ -11,7 +11,7 @@ File: tarlz.info, Node: Top, Next: Introduction, Up: (dir)
|
||||||
Tarlz Manual
|
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:
|
* Menu:
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ This manual is for Tarlz (version 0.23, 23 September 2022).
|
||||||
* Concept index:: Index of concepts
|
* 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,
|
This manual is free documentation: you have unlimited permission to copy,
|
||||||
distribute, and modify it.
|
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.
|
because it does not keep the members aligned.
|
||||||
|
|
||||||
Tarlz can create tar archives with five levels of compression
|
Tarlz can create tar archives with five levels of compression
|
||||||
granularity: per file (--no-solid), per block (--bsolid, default), per
|
granularity: per file ('--no-solid'), per block ('--bsolid', default), per
|
||||||
directory (--dsolid), appendable solid (--asolid), and solid (--solid). It
|
directory ('--dsolid'), appendable solid ('--asolid'), and solid
|
||||||
can also create uncompressed tar archives.
|
('--solid'). It can also create uncompressed tar archives.
|
||||||
|
|
||||||
Of course, compressing each file (or each directory) individually can't
|
Of course, compressing each file (or each directory) individually can't
|
||||||
achieve a compression ratio as high as compressing solidly the whole tar
|
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)
|
Tarlz protects the extended records with a Cyclic Redundancy Check (CRC)
|
||||||
in a way compatible with standard tar tools. *Note crc32::.
|
in a way compatible with standard tar tools. *Note crc32::.
|
||||||
|
|
||||||
Tarlz does not understand other tar formats like 'gnu', 'oldgnu', 'star'
|
Tarlz does not understand other tar formats like 'gnu', 'oldgnu',
|
||||||
or 'v7'. The command 'tarlz -tf archive.tar.lz > /dev/null' can be used to
|
'star', or 'v7'. The command 'tarlz -t -f archive.tar.lz > /dev/null' can
|
||||||
verify that the format of the archive is compatible with tarlz.
|
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
|
File: tarlz.info, Node: Invoking tarlz, Next: Portable character set, Prev: Introduction, Up: Top
|
||||||
|
@ -140,7 +140,7 @@ to '-1 --solid'.
|
||||||
'-A'
|
'-A'
|
||||||
'--concatenate'
|
'--concatenate'
|
||||||
Append one or more archives to the end of an archive. If no archive is
|
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)
|
standard output. All the archives involved must be regular (seekable)
|
||||||
files, and must be either all compressed or all uncompressed.
|
files, and must be either all compressed or all uncompressed.
|
||||||
Compressed and uncompressed archives can't be mixed. Compressed
|
Compressed and uncompressed archives can't be mixed. Compressed
|
||||||
|
@ -163,7 +163,7 @@ to '-1 --solid'.
|
||||||
'-d'
|
'-d'
|
||||||
'--diff'
|
'--diff'
|
||||||
Compare and report differences between archive and file system. For
|
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,
|
the file system exists and is of the same type (regular file,
|
||||||
directory, etc). Report on standard output the differences found in
|
directory, etc). Report on standard output the differences found in
|
||||||
type, mode (permissions), owner and group IDs, modification time, file
|
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
|
directory without extracting the files under it, use
|
||||||
'tarlz -xf foo --exclude='dir/*' dir'. Tarlz removes files and empty
|
'tarlz -xf foo --exclude='dir/*' dir'. Tarlz removes files and empty
|
||||||
directories unconditionally before extracting over them. Other than
|
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
|
incompatible type of file. For example, extracting a file over a
|
||||||
non-empty directory will usually fail.
|
non-empty directory usually fails.
|
||||||
|
|
||||||
'-z'
|
'-z'
|
||||||
'--compress'
|
'--compress'
|
||||||
Compress existing POSIX tar archives aligning the lzip members to the
|
Compress existing POSIX tar archives aligning the lzip members to the
|
||||||
tar members with choice of granularity (--bsolid by default, --dsolid
|
tar members with choice of granularity ('--bsolid' by default,
|
||||||
works like --asolid). The input archives are kept unchanged. Existing
|
'--dsolid' works like '--asolid'). Exit with error status 2 if any
|
||||||
compressed archives are not overwritten. A hyphen '-' used as the name
|
input archive is an empty file. The input archives are kept unchanged.
|
||||||
of an input archive reads from standard input and writes to standard
|
Existing compressed archives are not overwritten. A hyphen '-' used as
|
||||||
output (unless the option '--output' is used). Tarlz can be used as
|
the name of an input archive reads from standard input and writes to
|
||||||
compressor for GNU tar using a command like
|
standard output (unless the option '--output' is used). Tarlz can be
|
||||||
'tar -c -Hustar foo | tarlz -z -o foo.tar.lz'. Note that tarlz only
|
used as compressor for GNU tar by using a command like
|
||||||
works reliably on archives without global headers, or with global
|
'tar -c -Hustar foo | tarlz -z -o foo.tar.lz'. Tarlz can be used as
|
||||||
headers whose content can be ignored.
|
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
|
The compression is reversible, including any garbage present after the
|
||||||
end-of-archive blocks. Tarlz stops parsing after the first
|
end-of-archive blocks. Tarlz stops parsing after the first
|
||||||
|
@ -277,18 +280,18 @@ to '-1 --solid'.
|
||||||
|
|
||||||
'-C DIR'
|
'-C DIR'
|
||||||
'--directory=DIR'
|
'--directory=DIR'
|
||||||
Change to directory DIR. When creating or appending, the position of
|
Change to directory DIR. When creating, appending, comparing, or
|
||||||
each '-C' option in the command line is significant; it will change the
|
extracting, the position of each '-C' option in the command line is
|
||||||
current working directory for the following FILES until a new '-C'
|
significant; it changes the current working directory for the following
|
||||||
option appears in the command line. When extracting or comparing, all
|
FILES until a new '-C' option appears in the command line. '--list'
|
||||||
the '-C' options are executed in sequence before reading the archive.
|
and '--delete' ignore any '-C' options specified. DIR is relative to
|
||||||
Listing ignores any '-C' options specified. DIR is relative to the
|
the then current working directory, perhaps changed by a previous '-C'
|
||||||
then current working directory, perhaps changed by a previous '-C'
|
|
||||||
option.
|
option.
|
||||||
|
|
||||||
Note that a process can only have one current working directory (CWD).
|
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'
|
Therefore multi-threading can't be used to create or decode an archive
|
||||||
option appears after a relative file name in the command line.
|
if a '-C' option appears after a (relative) file name in the command
|
||||||
|
line. (All file names are made relative when decoding).
|
||||||
|
|
||||||
'-f ARCHIVE'
|
'-f ARCHIVE'
|
||||||
'--file=ARCHIVE'
|
'--file=ARCHIVE'
|
||||||
|
@ -308,8 +311,7 @@ to '-1 --solid'.
|
||||||
support". A value of 0 disables threads entirely. If this option is
|
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
|
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
|
and use it as default value. 'tarlz --help' shows the system's default
|
||||||
value. See the note about multi-threaded archive creation in the
|
value. See the note about multi-threading in the option '-C' above.
|
||||||
option '-C' above.
|
|
||||||
|
|
||||||
Note that the number of usable threads is limited during compression to
|
Note that the number of usable threads is limited during compression to
|
||||||
ceil( uncompressed_size / data_size ) (*note Minimum archive sizes::),
|
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
|
With '--create', don't compress the tar archive created. Create an
|
||||||
uncompressed tar archive instead. With '--append', don't compress the
|
uncompressed tar archive instead. With '--append', don't compress the
|
||||||
new members appended to the tar archive. Compressed members can't be
|
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'
|
'--asolid'
|
||||||
When creating or appending to a compressed archive, use appendable
|
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
|
happens while extracting a file, keep the partial data extracted. Use
|
||||||
this option to recover as much data as possible from each damaged
|
this option to recover as much data as possible from each damaged
|
||||||
member. It is recommended to run tarlz in single-threaded mode
|
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'
|
'--missing-crc'
|
||||||
Exit with error status 2 if the CRC of the extended records is
|
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
|
missing. When this option is used, tarlz detects any corruption in the
|
||||||
extended records (only limited by CRC collisions). But note that a
|
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 instead of as a corrupt record. This misleading
|
||||||
'Missing CRC' message is the consequence of a flaw in the POSIX pax
|
'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
|
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
|
archive is read by an ustar tool, and are ignored by tarlz on archive
|
||||||
extraction. *Note flawed-compat::.
|
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
|
The pax extended header data consists of one or more records, each of
|
||||||
them constructed as follows:
|
them constructed as follows:
|
||||||
'"%d %s=%s\n", <length>, <keyword>, <value>'
|
'"%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
|
previously archived. This record overrides the field 'linkname' in the
|
||||||
following ustar header block. The following ustar header block
|
following ustar header block. The following ustar header block
|
||||||
determines the type of link created. If typeflag of the following
|
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
|
header block is 1, a hard link is created. If typeflag is 2, a
|
||||||
be a symbolic link and the linkpath value will be used as the contents
|
symbolic link is created and the linkpath value is used as the
|
||||||
of the symbolic link. The linkpath record is created only for links
|
contents of the symbolic link. The linkpath record is created only for
|
||||||
with a link name that does not fit in the space provided by the ustar
|
links with a link name that does not fit in the space provided by the
|
||||||
header.
|
ustar header.
|
||||||
|
|
||||||
'mtime'
|
'mtime'
|
||||||
The signed decimal representation of the modification time of the
|
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
|
CRC32-C (Castagnoli) of the extended header data excluding the 8 bytes
|
||||||
representing the CRC <value> itself. The <value> is represented as 8
|
representing the CRC <value> itself. The <value> is represented as 8
|
||||||
hexadecimal digits in big endian order, '22 GNU.crc32=00000000\n'. The
|
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
|
corruption is always detected when using '--missing-crc' (except in
|
||||||
case of CRC collision). A CRC was chosen because a checksum is too
|
case of CRC collision). A CRC was chosen because a checksum is too
|
||||||
weak for a potentially large list of variable sized records. A
|
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
|
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
|
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'
|
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,
|
implementations store the truncated name in the field 'name' alone,
|
||||||
truncating the name to only 100 bytes instead of 256). Therefore the files
|
truncating the name to only 100 bytes instead of 256). Therefore the files
|
||||||
corresponding to both the extended header and the overridden ustar header
|
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
|
files or directories. It may be a security risk to extract a file with a
|
||||||
truncated file name.
|
truncated file name.
|
||||||
|
|
||||||
|
@ -1098,11 +1106,11 @@ multimember compressed archive.
|
||||||
For this to work as expected (and roughly multiply the compression speed
|
For this to work as expected (and roughly multiply the compression speed
|
||||||
by the number of available processors), the uncompressed archive must be at
|
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
|
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
|
--data-size::). Else some processors do not get any data to compress, and
|
||||||
compression will be proportionally slower. The maximum speed increase
|
compression is proportionally slower. The maximum speed increase achievable
|
||||||
achievable on a given archive is limited by the ratio
|
on a given archive is limited by the ratio (uncompressed_size / data_size).
|
||||||
(uncompressed_size / data_size). For example, a tarball the size of gcc or
|
For example, a tarball the size of gcc or linux scales up to 10 or 14
|
||||||
linux will scale up to 10 or 14 processors at level -9.
|
processors at level -9.
|
||||||
|
|
||||||
The following table shows the minimum uncompressed archive size needed
|
The following table shows the minimum uncompressed archive size needed
|
||||||
for full use of N processors at a given compression level, using the default
|
for full use of N processors at a given compression level, using the default
|
||||||
|
@ -1245,24 +1253,24 @@ Concept index
|
||||||
Tag Table:
|
Tag Table:
|
||||||
Node: Top216
|
Node: Top216
|
||||||
Node: Introduction1210
|
Node: Introduction1210
|
||||||
Node: Invoking tarlz4029
|
Node: Invoking tarlz4041
|
||||||
Ref: --data-size12880
|
Ref: --data-size13085
|
||||||
Ref: --bsolid17192
|
Ref: --bsolid17521
|
||||||
Node: Portable character set22788
|
Node: Portable character set23119
|
||||||
Node: File format23431
|
Node: File format23762
|
||||||
Ref: key_crc3230188
|
Ref: key_crc3230703
|
||||||
Ref: ustar-uid-gid33452
|
Ref: ustar-uid-gid33968
|
||||||
Ref: ustar-mtime34254
|
Ref: ustar-mtime34770
|
||||||
Node: Amendments to pax format36254
|
Node: Amendments to pax format36770
|
||||||
Ref: crc3236963
|
Ref: crc3237479
|
||||||
Ref: flawed-compat38274
|
Ref: flawed-compat38790
|
||||||
Node: Program design42364
|
Node: Program design42872
|
||||||
Node: Multi-threaded decoding46289
|
Node: Multi-threaded decoding46797
|
||||||
Ref: mt-extraction49570
|
Ref: mt-extraction50078
|
||||||
Node: Minimum archive sizes50876
|
Node: Minimum archive sizes51384
|
||||||
Node: Examples53014
|
Node: Examples53511
|
||||||
Node: Problems55381
|
Node: Problems55878
|
||||||
Node: Concept index55936
|
Node: Concept index56433
|
||||||
|
|
||||||
End Tag Table
|
End Tag Table
|
||||||
|
|
||||||
|
|
179
doc/tarlz.texi
179
doc/tarlz.texi
|
@ -6,8 +6,8 @@
|
||||||
@finalout
|
@finalout
|
||||||
@c %**end of header
|
@c %**end of header
|
||||||
|
|
||||||
@set UPDATED 23 September 2022
|
@set UPDATED 20 September 2023
|
||||||
@set VERSION 0.23
|
@set VERSION 0.24
|
||||||
|
|
||||||
@dircategory Archiving
|
@dircategory Archiving
|
||||||
@direntry
|
@direntry
|
||||||
|
@ -50,7 +50,7 @@ This manual is for Tarlz (version @value{VERSION}, @value{UPDATED}).
|
||||||
@end menu
|
@end menu
|
||||||
|
|
||||||
@sp 1
|
@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,
|
This manual is free documentation: you have unlimited permission to copy,
|
||||||
distribute, and modify it.
|
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.
|
because it does not keep the members aligned.
|
||||||
|
|
||||||
Tarlz can create tar archives with five levels of compression granularity:
|
Tarlz can create tar archives with five levels of compression granularity:
|
||||||
per file (---no-solid), per block (---bsolid, default), per directory
|
per file (@option{--no-solid}), per block (@option{--bsolid}, default), per
|
||||||
(---dsolid), appendable solid (---asolid), and solid (---solid). It can also
|
directory (@option{--dsolid}), appendable solid (@option{--asolid}), and
|
||||||
create uncompressed tar archives.
|
solid (@option{--solid}). It can also create uncompressed tar archives.
|
||||||
|
|
||||||
@noindent
|
@noindent
|
||||||
Of course, compressing each file (or each directory) individually can't
|
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
|
It is a safe POSIX-style backup format. In case of corruption, tarlz
|
||||||
can extract all the undamaged members from the tar.lz archive,
|
can extract all the undamaged members from the tar.lz archive,
|
||||||
skipping over the damaged members, just like the standard
|
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
|
to recover as much data as possible from each damaged member, and
|
||||||
lziprecover can be used to recover some of the damaged members.
|
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}.
|
a way compatible with standard tar tools. @xref{crc32}.
|
||||||
|
|
||||||
Tarlz does not understand other tar formats like @samp{gnu}, @samp{oldgnu},
|
Tarlz does not understand other tar formats like @samp{gnu}, @samp{oldgnu},
|
||||||
@samp{star} or @samp{v7}. The command
|
@samp{star}, or @samp{v7}. The command
|
||||||
@w{@samp{tarlz -tf archive.tar.lz > /dev/null}} can be used to verify that
|
@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.
|
the format of the archive is compatible with tarlz.
|
||||||
|
|
||||||
|
|
||||||
|
@ -137,9 +137,9 @@ tarlz @var{operation} [@var{options}] [@var{files}]
|
||||||
@end example
|
@end example
|
||||||
|
|
||||||
@noindent
|
@noindent
|
||||||
All operations except @samp{--concatenate} and @samp{--compress} operate on
|
All operations except @option{--concatenate} and @option{--compress} operate
|
||||||
whole trees if any @var{file} is a directory. All operations except
|
on whole trees if any @var{file} is a directory. All operations except
|
||||||
@samp{--compress} overwrite output files without warning. If no archive is
|
@option{--compress} overwrite output files without warning. If no archive is
|
||||||
specified, tarlz tries to read it from standard input or write it to
|
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
|
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
|
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.
|
skips it.
|
||||||
|
|
||||||
Tarlz does not use absolute file names nor file names above the current
|
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
|
or appending tarlz archives the files specified, but removes from member
|
||||||
names any leading and trailing slashes and any file name prefixes containing
|
names any leading and trailing slashes and any file name prefixes containing
|
||||||
a @samp{..} component. On extraction, leading and trailing slashes are also
|
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
|
@w{@samp{tarlz -xf foo ./bar baz}} extracts members @samp{bar} and
|
||||||
@samp{./baz} from archive @samp{foo}.
|
@samp{./baz} from archive @samp{foo}.
|
||||||
|
|
||||||
If several compression levels or @samp{--*solid} options are given, the last
|
If several compression levels or @option{--*solid} options are given, the last
|
||||||
setting is used. For example @w{@samp{-9 --solid --uncompressed -1}} is
|
setting is used. For example @w{@option{-9 --solid --uncompressed -1}} is
|
||||||
equivalent to @w{@samp{-1 --solid}}.
|
equivalent to @w{@option{-1 --solid}}.
|
||||||
|
|
||||||
tarlz supports the following operations:
|
tarlz supports the following operations:
|
||||||
|
|
||||||
|
@ -179,7 +179,7 @@ This version number should be included in all bug reports.
|
||||||
@item -A
|
@item -A
|
||||||
@itemx --concatenate
|
@itemx --concatenate
|
||||||
Append one or more archives to the end of an archive. If no archive is
|
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,
|
standard output. All the archives involved must be regular (seekable) files,
|
||||||
and must be either all compressed or all uncompressed. Compressed and
|
and must be either all compressed or all uncompressed. Compressed and
|
||||||
uncompressed archives can't be mixed. Compressed archives must be
|
uncompressed archives can't be mixed. Compressed archives must be
|
||||||
|
@ -202,23 +202,23 @@ Create a new archive from @var{files}.
|
||||||
@item -d
|
@item -d
|
||||||
@itemx --diff
|
@itemx --diff
|
||||||
Compare and report differences between archive and file system. For each tar
|
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
|
exists and is of the same type (regular file, directory, etc). Report on
|
||||||
standard output the differences found in type, mode (permissions), owner and
|
standard output the differences found in type, mode (permissions), owner and
|
||||||
group IDs, modification time, file size, file contents (of regular files),
|
group IDs, modification time, file size, file contents (of regular files),
|
||||||
target (of symlinks) and device number (of block/character special 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
|
As tarlz removes leading slashes from member names, the option @option{-C} may
|
||||||
be used in combination with @samp{--diff} when absolute file names were used
|
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
|
on archive creation: @w{@samp{tarlz -C / -d}}. Alternatively, tarlz may be
|
||||||
run from the root directory to perform the comparison.
|
run from the root directory to perform the comparison.
|
||||||
|
|
||||||
@item --delete
|
@item --delete
|
||||||
Delete files and directories from an archive in place. It currently can
|
Delete files and directories from an archive in place. It currently can
|
||||||
delete only from uncompressed archives and from archives with files
|
delete only from uncompressed archives and from archives with files
|
||||||
compressed individually (@samp{--no-solid} archives). Note that files of
|
compressed individually (@option{--no-solid} archives). Note that files of
|
||||||
about @samp{--data-size} or larger are compressed individually even if
|
about @option{--data-size} or larger are compressed individually even if
|
||||||
@samp{--bsolid} is used, and can therefore be deleted. Tarlz takes care to
|
@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
|
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
|
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
|
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
|
directory without extracting the files under it, use
|
||||||
@w{@samp{tarlz -xf foo --exclude='dir/*' dir}}. Tarlz removes files and
|
@w{@samp{tarlz -xf foo --exclude='dir/*' dir}}. Tarlz removes files and
|
||||||
empty directories unconditionally before extracting over them. Other than
|
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
|
incompatible type of file. For example, extracting a file over a non-empty
|
||||||
directory will usually fail.
|
directory usually fails.
|
||||||
|
|
||||||
@item -z
|
@item -z
|
||||||
@itemx --compress
|
@itemx --compress
|
||||||
Compress existing POSIX tar archives aligning the lzip members to the tar
|
Compress existing POSIX tar archives aligning the lzip members to the tar
|
||||||
members with choice of granularity (---bsolid by default, ---dsolid works
|
members with choice of granularity (@option{--bsolid} by default,
|
||||||
like ---asolid). The input archives are kept unchanged. Existing compressed
|
@option{--dsolid} works like @option{--asolid}). Exit with error status 2 if
|
||||||
archives are not overwritten. A hyphen @samp{-} used as the name of an input
|
any input archive is an empty file. The input archives are kept unchanged.
|
||||||
archive reads from standard input and writes to standard output (unless the
|
Existing compressed archives are not overwritten. A hyphen @samp{-} used as
|
||||||
option @samp{--output} is used). Tarlz can be used as compressor for GNU tar
|
the name of an input archive reads from standard input and writes to
|
||||||
using a command like @w{@samp{tar -c -Hustar foo | tarlz -z -o foo.tar.lz}}.
|
standard output (unless the option @option{--output} is used). Tarlz can be
|
||||||
Note that tarlz only works reliably on archives without global headers, or
|
used as compressor for GNU tar by using a command like
|
||||||
with global headers whose content can be ignored.
|
@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
|
The compression is reversible, including any garbage present after the
|
||||||
end-of-archive blocks. Tarlz stops parsing after the first end-of-archive
|
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
|
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
|
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
|
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
|
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
|
with the extension @samp{.lz} added unless the option @option{--output} is
|
||||||
used. When @samp{--output} is used, only one input archive can be specified.
|
used. When @option{--output} is used, only one input archive can be specified.
|
||||||
@samp{-f} can't be used with @samp{--compress}.
|
@option{-f} can't be used with @option{--compress}.
|
||||||
|
|
||||||
@item --check-lib
|
@item --check-lib
|
||||||
Compare the
|
Compare the
|
||||||
|
@ -314,25 +318,25 @@ tarlz supports the following
|
||||||
@anchor{--data-size}
|
@anchor{--data-size}
|
||||||
@item -B @var{bytes}
|
@item -B @var{bytes}
|
||||||
@itemx --data-size=@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
|
@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}.
|
defaults to @w{1 MiB}. @xref{Minimum archive sizes}.
|
||||||
|
|
||||||
@item -C @var{dir}
|
@item -C @var{dir}
|
||||||
@itemx --directory=@var{dir}
|
@itemx --directory=@var{dir}
|
||||||
Change to directory @var{dir}. When creating or appending, the position of
|
Change to directory @var{dir}. When creating, appending, comparing, or
|
||||||
each @samp{-C} option in the command line is significant; it will change the
|
extracting, the position of each @option{-C} option in the command line is
|
||||||
current working directory for the following @var{files} until a new
|
significant; it changes the current working directory for the following
|
||||||
@samp{-C} option appears in the command line. When extracting or comparing,
|
@var{files} until a new @option{-C} option appears in the command line.
|
||||||
all the @samp{-C} options are executed in sequence before reading the
|
@option{--list} and @option{--delete} ignore any @option{-C} options
|
||||||
archive. Listing ignores any @samp{-C} options specified. @var{dir} is
|
specified. @var{dir} is relative to the then current working directory,
|
||||||
relative to the then current working directory, perhaps changed by a
|
perhaps changed by a previous @option{-C} option.
|
||||||
previous @samp{-C} option.
|
|
||||||
|
|
||||||
Note that a process can only have one current working directory (CWD).
|
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}
|
Therefore multi-threading can't be used to create or decode an archive if a
|
||||||
option appears after a relative file name in the command line.
|
@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}
|
@item -f @var{archive}
|
||||||
@itemx --file=@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
|
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.
|
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
|
@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
|
Note that the number of usable threads is limited during compression to
|
||||||
@w{ceil( uncompressed_size / data_size )} (@pxref{Minimum archive sizes}),
|
@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}
|
@item -o @var{file}
|
||||||
@itemx --output=@var{file}
|
@itemx --output=@var{file}
|
||||||
Write the compressed output to @var{file}. @w{@samp{-o -}} writes the
|
Write the compressed output to @var{file}. @w{@option{-o -}} writes the
|
||||||
compressed output to standard output. Currently @samp{--output} only works
|
compressed output to standard output. Currently @option{--output} only works
|
||||||
with @samp{--compress}.
|
with @option{--compress}.
|
||||||
|
|
||||||
@item -p
|
@item -p
|
||||||
@itemx --preserve-permissions
|
@itemx --preserve-permissions
|
||||||
|
@ -381,8 +385,8 @@ Verbosely list files processed. Further -v's (up to 4) increase the
|
||||||
verbosity level.
|
verbosity level.
|
||||||
|
|
||||||
@item -0 .. -9
|
@item -0 .. -9
|
||||||
Set the compression level for @samp{--create}, @samp{--append}, and
|
Set the compression level for @option{--create}, @option{--append}, and
|
||||||
@samp{--compress}. The default compression level is @samp{-6}. Like lzip,
|
@option{--compress}. The default compression level is @option{-6}. Like lzip,
|
||||||
tarlz also minimizes the dictionary size of the lzip members it creates,
|
tarlz also minimizes the dictionary size of the lzip members it creates,
|
||||||
reducing the amount of memory required for decompression.
|
reducing the amount of memory required for decompression.
|
||||||
|
|
||||||
|
@ -401,10 +405,12 @@ reducing the amount of memory required for decompression.
|
||||||
@end multitable
|
@end multitable
|
||||||
|
|
||||||
@item --uncompressed
|
@item --uncompressed
|
||||||
With @samp{--create}, don't compress the tar archive created. Create an
|
With @option{--create}, don't compress the tar archive created. Create an
|
||||||
uncompressed tar archive instead. With @samp{--append}, don't compress the
|
uncompressed tar archive instead. With @option{--append}, don't compress the
|
||||||
new members appended to the tar archive. Compressed members can't be
|
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
|
@item --asolid
|
||||||
When creating or appending to a compressed archive, use appendable solid
|
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.
|
archives can't be created nor decoded in parallel.
|
||||||
|
|
||||||
@item --anonymous
|
@item --anonymous
|
||||||
Equivalent to @w{@samp{--owner=root --group=root}}.
|
Equivalent to @w{@option{--owner=root --group=root}}.
|
||||||
|
|
||||||
@item --owner=@var{owner}
|
@item --owner=@var{owner}
|
||||||
When creating or appending, use @var{owner} for files added to the archive.
|
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
|
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
|
@var{pattern} contains a @samp{/}, it matches a corresponding @samp{/} in
|
||||||
the file name. For example, @samp{foo/*.o} matches @samp{foo/bar.o}.
|
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
|
@item --ignore-ids
|
||||||
Make @samp{--diff} ignore differences in owner and group IDs. This option is
|
Make @option{--diff} ignore differences in owner and group IDs. This option is
|
||||||
useful when comparing an @samp{--anonymous} archive.
|
useful when comparing an @option{--anonymous} archive.
|
||||||
|
|
||||||
@item --ignore-overflow
|
@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.
|
systems with a 32-bit time_t.
|
||||||
|
|
||||||
@item --keep-damaged
|
@item --keep-damaged
|
||||||
Don't delete partially extracted files. If a decompression error happens
|
Don't delete partially extracted files. If a decompression error happens
|
||||||
while extracting a file, keep the partial data extracted. Use this option to
|
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
|
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
|
@item --missing-crc
|
||||||
Exit with error status 2 if the CRC of the extended records is missing. When
|
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
|
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}
|
(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
|
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
|
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}.
|
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
|
@end itemize
|
||||||
|
|
||||||
Each tar member must be contiguously stored in a lzip member for the
|
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
|
is split over two or more lzip members, the archive must be decoded
|
||||||
sequentially. @xref{Multi-threaded decoding}.
|
sequentially. @xref{Multi-threaded decoding}.
|
||||||
|
|
||||||
|
@ -639,7 +646,7 @@ tar.lz
|
||||||
@end verbatim
|
@end verbatim
|
||||||
|
|
||||||
@ignore
|
@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:@*
|
archive format are allowed:@*
|
||||||
If several extended headers precede an ustar header, only the last
|
If several extended headers precede an ustar header, only the last
|
||||||
extended header takes effect. The other extended headers are ignored.
|
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
|
trouble if the archive is read by an ustar tool, and are ignored by tarlz on
|
||||||
archive extraction. @xref{flawed-compat}.
|
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
|
The pax extended header data consists of one or more records, each of
|
||||||
them constructed as follows:@*
|
them constructed as follows:@*
|
||||||
@w{@samp{"%d %s=%s\n", <length>, <keyword>, <value>}}
|
@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,
|
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
|
previously archived. This record overrides the field @samp{linkname} in the
|
||||||
following ustar header block. The following ustar header block determines
|
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
|
the type of link created. If typeflag of the following header block is 1, a
|
||||||
will be a hard link. If typeflag is 2, it will be a symbolic link and the
|
hard link is created. If typeflag is 2, a symbolic link is created and the
|
||||||
linkpath value will be used as the contents of the symbolic link. The
|
linkpath value is used as the contents of the symbolic link. The linkpath
|
||||||
linkpath record is created only for links with a link name that does not fit
|
record is created only for links with a link name that does not fit in the
|
||||||
in the space provided by the ustar header.
|
space provided by the ustar header.
|
||||||
|
|
||||||
@item mtime
|
@item mtime
|
||||||
The signed decimal representation of the modification time of the following
|
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
|
representing the CRC <value> itself. The <value> is represented as 8
|
||||||
hexadecimal digits in big endian order,
|
hexadecimal digits in big endian order,
|
||||||
@w{@samp{22 GNU.crc32=00000000\n}}. The keyword of the CRC record is
|
@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
|
protected by the CRC to guarantee that corruption is always detected when
|
||||||
using @samp{--missing-crc} (except in case of CRC collision). A CRC was
|
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
|
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
|
variable sized records. A checksum can't detect simple errors like the
|
||||||
swapping of two bytes.
|
swapping of two bytes.
|
||||||
|
@ -878,7 +889,7 @@ character.
|
||||||
Tarlz creates safe archives that allow the reliable detection of invalid or
|
Tarlz creates safe archives that allow the reliable detection of invalid or
|
||||||
corrupt metadata during decoding even when the integrity checking of lzip
|
corrupt metadata during decoding even when the integrity checking of lzip
|
||||||
can't be used because the lzip members are only decompressed partially, as
|
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,
|
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
|
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.
|
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
|
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
|
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
|
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}
|
(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
|
alone, truncating the name to only 100 bytes instead of 256). Therefore the
|
||||||
files corresponding to both the extended header and the overridden ustar
|
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
|
existing files or directories. It may be a security risk to extract a file
|
||||||
with a truncated file name.
|
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
|
misalignment during multi-threaded decoding, it switches to single-threaded
|
||||||
mode and continues decoding the archive.
|
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
|
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
|
it only needs to decompress part of each lzip member. See the following
|
||||||
example listing the Silesia corpus on a dual core machine:
|
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)
|
time tarlz -tf silesia.tar.lz (0.020s)
|
||||||
@end example
|
@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
|
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
|
corresponding to the tar member header. This is another reason why the tar
|
||||||
headers must provide their own integrity checking.
|
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
|
For this to work as expected (and roughly multiply the compression speed by
|
||||||
the number of available processors), the uncompressed archive must be at
|
the number of available processors), the uncompressed archive must be at
|
||||||
least as large as the number of worker threads times the block size
|
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
|
(@pxref{--data-size}). Else some processors do not get any data to compress,
|
||||||
compress, and compression will be proportionally slower. The maximum speed
|
and compression is proportionally slower. The maximum speed increase
|
||||||
increase achievable on a given archive is limited by the ratio
|
achievable on a given archive is limited by the ratio
|
||||||
@w{(uncompressed_size / data_size)}. For example, a tarball the size of gcc
|
@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
|
The following table shows the minimum uncompressed archive size needed for
|
||||||
full use of N processors at a given compression level, using the default
|
full use of N processors at a given compression level, using the default
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/* Tarlz - Archiver with multimember lzip compression
|
/* 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
|
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
|
it under the terms of the GNU General Public License as published by
|
||||||
|
@ -17,7 +17,6 @@
|
||||||
|
|
||||||
#define _FILE_OFFSET_BITS 64
|
#define _FILE_OFFSET_BITS 64
|
||||||
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <fnmatch.h>
|
#include <fnmatch.h>
|
||||||
|
|
||||||
#include "tarlz.h"
|
#include "tarlz.h"
|
||||||
|
|
139
extended.cc
139
extended.cc
|
@ -1,5 +1,5 @@
|
||||||
/* Tarlz - Archiver with multimember lzip compression
|
/* 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
|
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
|
it under the terms of the GNU General Public License as published by
|
||||||
|
@ -20,7 +20,6 @@
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdlib>
|
|
||||||
|
|
||||||
#include "tarlz.h"
|
#include "tarlz.h"
|
||||||
|
|
||||||
|
@ -30,34 +29,31 @@ const CRC32 crc32c( true );
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
unsigned long long record_size( const unsigned keyword_size,
|
unsigned record_size( const unsigned keyword_size, const unsigned value_size )
|
||||||
const unsigned long value_size )
|
|
||||||
{
|
{
|
||||||
/* length + ' ' + keyword + '=' + value + '\n'
|
/* length + ' ' + keyword + '=' + value + '\n'
|
||||||
minimize length; prefer "99<97_bytes>" to "100<97_bytes>" */
|
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 );
|
size += decimal_digits( decimal_digits( size ) + size );
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
unsigned long long parse_decimal( const char * const ptr,
|
long long parse_decimal( const char * const ptr, const char ** const tailp,
|
||||||
const char ** const tailp,
|
const int size, const unsigned long long limit = LLONG_MAX )
|
||||||
const unsigned long long size )
|
|
||||||
{
|
{
|
||||||
unsigned long long result = 0;
|
unsigned long long result = 0;
|
||||||
unsigned long long i = 0;
|
int i = 0;
|
||||||
while( i < size && std::isspace( (unsigned char)ptr[i] ) ) ++i;
|
while( i < size && std::isspace( (unsigned char)ptr[i] ) ) ++i;
|
||||||
if( !std::isdigit( (unsigned char)ptr[i] ) )
|
if( !std::isdigit( (unsigned char)ptr[i] ) ) { *tailp = ptr; return -1; }
|
||||||
{ if( tailp ) *tailp = ptr; return 0; }
|
|
||||||
for( ; i < size && std::isdigit( (unsigned char)ptr[i] ); ++i )
|
for( ; i < size && std::isdigit( (unsigned char)ptr[i] ); ++i )
|
||||||
{
|
{
|
||||||
const unsigned long long prev = result;
|
const unsigned long long prev = result;
|
||||||
result *= 10; result += ptr[i] - '0';
|
result *= 10; result += ptr[i] - '0';
|
||||||
if( result < prev || result > LLONG_MAX ) // overflow
|
if( result < prev || result > limit || result > LLONG_MAX ) // overflow
|
||||||
{ if( tailp ) *tailp = ptr; return 0; }
|
{ *tailp = ptr; return -1; }
|
||||||
}
|
}
|
||||||
if( tailp ) *tailp = ptr + i;
|
*tailp = ptr + i;
|
||||||
return result;
|
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 <= 9 ) return '0' + value;
|
||||||
if( value <= 15 ) return 'A' + value - 10;
|
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 )
|
void print_decimal( char * const buf, int size, unsigned long long num )
|
||||||
{ while( --size >= 0 ) { buf[size] = num % 10 + '0'; num /= 10; } }
|
{ while( --size >= 0 ) { buf[size] = num % 10 + '0'; num /= 10; } }
|
||||||
|
|
||||||
unsigned long long print_size_keyword( char * const buf,
|
int print_size_keyword( char * const buf, const int size, const char * keyword )
|
||||||
const unsigned long long size, const char * keyword )
|
|
||||||
{
|
{
|
||||||
// "size keyword=value\n"
|
// "size keyword=value\n"
|
||||||
unsigned long long pos = decimal_digits( size );
|
int pos = decimal_digits( size );
|
||||||
print_decimal( buf, pos, size ); buf[pos++] = ' ';
|
print_decimal( buf, pos, size ); buf[pos++] = ' ';
|
||||||
while( *keyword ) { buf[pos++] = *keyword; ++keyword; } buf[pos++] = '=';
|
while( *keyword ) { buf[pos++] = *keyword; ++keyword; } buf[pos++] = '=';
|
||||||
return pos;
|
return pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool print_record( char * const buf, const unsigned long long size,
|
bool print_record( char * const buf, const int size, const char * keyword,
|
||||||
const char * keyword, const std::string & value )
|
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() );
|
std::memcpy( buf + pos, value.c_str(), value.size() );
|
||||||
pos += value.size(); buf[pos++] = '\n';
|
pos += value.size(); buf[pos++] = '\n';
|
||||||
return pos == size;
|
return pos == size;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool print_record( char * const buf, const int size,
|
bool print_record( char * const buf, const int size, const char * keyword,
|
||||||
const char * keyword, const unsigned long long value )
|
const unsigned long long value )
|
||||||
{
|
{
|
||||||
int pos = print_size_keyword( buf, size, keyword );
|
int pos = print_size_keyword( buf, size, keyword );
|
||||||
const int vd = decimal_digits( value );
|
const int vd = decimal_digits( value );
|
||||||
|
@ -118,8 +113,8 @@ bool print_record( char * const buf, const int size,
|
||||||
return pos == size;
|
return pos == size;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool print_record( char * const buf, const int size,
|
bool print_record( char * const buf, const int size, const char * keyword,
|
||||||
const char * keyword, const Etime & value )
|
const Etime & value )
|
||||||
{
|
{
|
||||||
int pos = print_size_keyword( buf, size, keyword );
|
int pos = print_size_keyword( buf, size, keyword );
|
||||||
pos += value.print( buf + pos ); buf[pos++] = '\n';
|
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,
|
bool Etime::parse( const char * const ptr, const char ** const tailp,
|
||||||
const long long size )
|
const int size )
|
||||||
{
|
{
|
||||||
char * tail;
|
char * tail;
|
||||||
errno = 0;
|
errno = 0;
|
||||||
long long s = strtoll( ptr, &tail, 10 );
|
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;
|
( *tail != 0 && *tail != '\n' && *tail != '.' ) ) return false;
|
||||||
int ns = 0;
|
int ns = 0;
|
||||||
if( *tail == '.' ) // parse nanoseconds and any extra digits
|
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
|
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;
|
linkpath_recsize_ = linkpath_.size() ? record_size( 8, linkpath_.size() ) : 0;
|
||||||
path_recsize_ = path_.size() ? record_size( 4, path_.size() ) : 0;
|
path_recsize_ = path_.size() ? record_size( 4, path_.size() ) : 0;
|
||||||
file_size_recsize_ =
|
file_size_recsize_ =
|
||||||
|
@ -192,19 +189,21 @@ void Extended::calculate_sizes() const
|
||||||
atime_.out_of_ustar_range() ? record_size( 5, atime_.decimal_size() ) : 0;
|
atime_.out_of_ustar_range() ? record_size( 5, atime_.decimal_size() ) : 0;
|
||||||
mtime_recsize_ =
|
mtime_recsize_ =
|
||||||
mtime_.out_of_ustar_range() ? record_size( 5, mtime_.decimal_size() ) : 0;
|
mtime_.out_of_ustar_range() ? record_size( 5, mtime_.decimal_size() ) : 0;
|
||||||
edsize_ = linkpath_recsize_ + path_recsize_ + file_size_recsize_ +
|
const long long tmp = linkpath_recsize_ + path_recsize_ +
|
||||||
uid_recsize_ + gid_recsize_ + atime_recsize_ + mtime_recsize_ +
|
file_size_recsize_ + uid_recsize_ + gid_recsize_ +
|
||||||
crc_record.size();
|
atime_recsize_ + mtime_recsize_ + crc_record.size();
|
||||||
|
if( tmp > max_edata_size ) { full_size_ = -3; return; }
|
||||||
|
edsize_ = tmp;
|
||||||
padded_edsize_ = round_up( edsize_ );
|
padded_edsize_ = round_up( edsize_ );
|
||||||
|
if( padded_edsize_ > max_edata_size ) { full_size_ = -3; return; }
|
||||||
full_size_ = header_size + padded_edsize_;
|
full_size_ = header_size + padded_edsize_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// print a diagnostic for each unknown keyword once per keyword
|
// print a diagnostic for each unknown keyword once per keyword
|
||||||
void Extended::unknown_keyword( const char * const buf,
|
void Extended::unknown_keyword( const char * const buf, const int size ) const
|
||||||
const unsigned long long 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;
|
while( eq_pos < size && buf[eq_pos] != '=' ) ++eq_pos;
|
||||||
const std::string keyword( buf, eq_pos );
|
const std::string keyword( buf, eq_pos );
|
||||||
for( unsigned i = 0; i < unknown_keywords.size(); ++i )
|
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.
|
/* Return the size of the extended block, or 0 if empty.
|
||||||
long long Extended::format_block( Resizable_buffer & rbuf ) const
|
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 int bufsize = full_size(); // recalculate sizes if needed
|
||||||
const unsigned long long bufsize = full_size(); // recalculate sizes
|
if( bufsize <= 0 ) return bufsize; // error or no extended data
|
||||||
if( edsize_ <= 0 ) return 0; // no extended data
|
|
||||||
if( edsize_ >= 1LL << 33 ) return -1; // too much extended data
|
|
||||||
if( !rbuf.resize( bufsize ) ) return -2; // extended block buffer
|
if( !rbuf.resize( bufsize ) ) return -2; // extended block buffer
|
||||||
uint8_t * const header = rbuf.u8(); // extended header
|
uint8_t * const header = rbuf.u8(); // extended header
|
||||||
char * const buf = rbuf() + header_size; // extended records
|
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_ ) )
|
if( path_recsize_ && !print_record( buf, path_recsize_, "path", path_ ) )
|
||||||
return -1;
|
return -1;
|
||||||
long long pos = path_recsize_;
|
int pos = path_recsize_;
|
||||||
if( linkpath_recsize_ &&
|
if( linkpath_recsize_ &&
|
||||||
!print_record( buf + pos, linkpath_recsize_, "linkpath", linkpath_ ) )
|
!print_record( buf + pos, linkpath_recsize_, "linkpath", linkpath_ ) )
|
||||||
return -1;
|
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 )
|
const bool permissive )
|
||||||
{
|
{
|
||||||
reset(); full_size_ = -1; // invalidate cached sizes
|
reset(); full_size_ = -4; // invalidate cached sizes
|
||||||
for( unsigned long long pos = 0; pos < edsize; ) // parse records
|
for( int pos = 0; pos < edsize; ) // parse records
|
||||||
{
|
{
|
||||||
const char * tail;
|
const char * tail;
|
||||||
const unsigned long long rsize =
|
const int rsize =
|
||||||
parse_decimal( buf + pos, &tail, edsize - pos );
|
parse_decimal( buf + pos, &tail, edsize - pos, edsize - pos );
|
||||||
if( rsize == 0 || rsize > edsize - pos ||
|
if( rsize <= 0 || tail[0] != ' ' || buf[pos+rsize-1] != '\n' ) return false;
|
||||||
tail[0] != ' ' || buf[pos+rsize-1] != '\n' ) return false;
|
|
||||||
++tail; // point to keyword
|
++tail; // point to keyword
|
||||||
// rest = length of (keyword + '=' + value) without the final newline
|
// 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( rest > 5 && std::memcmp( tail, "path=", 5 ) == 0 )
|
||||||
{
|
{
|
||||||
if( path_.size() && !permissive ) return false;
|
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 '/'
|
while( len > 1 && tail[5+len-1] == '/' ) --len; // trailing '/'
|
||||||
path_.assign( tail + 5, len );
|
path_.assign( tail + 5, len );
|
||||||
// this also truncates path_ at the first embedded null character
|
// 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 )
|
else if( rest > 9 && std::memcmp( tail, "linkpath=", 9 ) == 0 )
|
||||||
{
|
{
|
||||||
if( linkpath_.size() && !permissive ) return false;
|
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 '/'
|
while( len > 1 && tail[9+len-1] == '/' ) --len; // trailing '/'
|
||||||
linkpath_.assign( tail + 9, len );
|
linkpath_.assign( tail + 9, len );
|
||||||
}
|
}
|
||||||
else if( rest > 5 && std::memcmp( tail, "size=", 5 ) == 0 )
|
else if( rest > 5 && std::memcmp( tail, "size=", 5 ) == 0 )
|
||||||
{
|
{
|
||||||
if( file_size_ != 0 && !permissive ) return false;
|
if( file_size_ != 0 && !permissive ) return false;
|
||||||
file_size_ = parse_decimal( tail + 5, &tail, rest - 5 );
|
file_size_ = parse_decimal( tail + 5, &tail, rest - 5, max_file_size );
|
||||||
// parse error or size fits in ustar header
|
// overflow, parse error, or size fits in ustar header
|
||||||
if( file_size_ < 1LL << 33 || file_size_ > max_file_size ||
|
if( file_size_ < 1LL << 33 || tail != buf + ( pos + rsize - 1 ) )
|
||||||
tail != buf + ( pos + rsize - 1 ) ) return false;
|
return false;
|
||||||
}
|
}
|
||||||
else if( rest > 4 && std::memcmp( tail, "uid=", 4 ) == 0 )
|
else if( rest > 4 && std::memcmp( tail, "uid=", 4 ) == 0 )
|
||||||
{
|
{
|
||||||
if( uid_ >= 0 && !permissive ) return false;
|
if( uid_ >= 0 && !permissive ) return false;
|
||||||
uid_ = parse_decimal( tail + 4, &tail, rest - 4 );
|
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;
|
if( uid_ < 1 << 21 || tail != buf + ( pos + rsize - 1 ) ) return false;
|
||||||
}
|
}
|
||||||
else if( rest > 4 && std::memcmp( tail, "gid=", 4 ) == 0 )
|
else if( rest > 4 && std::memcmp( tail, "gid=", 4 ) == 0 )
|
||||||
{
|
{
|
||||||
if( gid_ >= 0 && !permissive ) return false;
|
if( gid_ >= 0 && !permissive ) return false;
|
||||||
gid_ = parse_decimal( tail + 4, &tail, rest - 4 );
|
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;
|
if( gid_ < 1 << 21 || tail != buf + ( pos + rsize - 1 ) ) return false;
|
||||||
}
|
}
|
||||||
else if( rest > 6 && std::memcmp( tail, "atime=", 6 ) == 0 )
|
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 )
|
else if( rest > 10 && std::memcmp( tail, "GNU.crc32=", 10 ) == 0 )
|
||||||
{
|
{
|
||||||
if( crc_present_ && !permissive ) return false;
|
if( crc_present_ && !permissive ) return false;
|
||||||
if( rsize != crc_record.size() ) return false;
|
if( rsize != (int)crc_record.size() ) return false;
|
||||||
crc_present_ = true;
|
crc_present_ = true;
|
||||||
const uint32_t stored_crc = parse_record_crc( tail + 10 );
|
const uint32_t stored_crc = parse_record_crc( tail + 10 );
|
||||||
const uint32_t computed_crc =
|
const uint32_t computed_crc =
|
||||||
|
@ -367,7 +378,7 @@ void Extended::fill_from_ustar( const Tar_header header )
|
||||||
if( len > 0 )
|
if( len > 0 )
|
||||||
{
|
{
|
||||||
linkpath_.assign( (const char *)header + linkname_o, len );
|
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_.
|
/* 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 )
|
long long Extended::get_file_size_and_reset( const Tar_header header )
|
||||||
{
|
{
|
||||||
const long long tmp = file_size_;
|
const long long tmp = file_size_;
|
||||||
file_size( 0 ); // reset full_size_
|
file_size( 0 ); // reset full_size_
|
||||||
const Typeflag typeflag = (Typeflag)header[typeflag_o];
|
const Typeflag typeflag = (Typeflag)header[typeflag_o];
|
||||||
if( typeflag == tf_regular || typeflag == tf_hiperf )
|
if( typeflag != tf_regular && typeflag != tf_hiperf ) return 0;
|
||||||
{
|
if( tmp > 0 ) return tmp;
|
||||||
if( tmp == 0 ) return parse_octal( header + size_o, size_l );
|
return parse_octal( header + size_o, size_l );
|
||||||
else return tmp;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/* Tarlz - Archiver with multimember lzip compression
|
/* 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
|
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
|
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,
|
bool Lzip_index::check_header_error( const Lzip_header & header,
|
||||||
const bool first )
|
const bool first )
|
||||||
{
|
{
|
||||||
if( !header.verify_magic() )
|
if( !header.check_magic() )
|
||||||
{ error_ = bad_magic_msg; retval_ = 2; if( first ) bad_magic_ = true;
|
{ error_ = bad_magic_msg; retval_ = 2; if( first ) bad_magic_ = true;
|
||||||
return true; }
|
return true; }
|
||||||
if( !header.verify_version() )
|
if( !header.check_version() )
|
||||||
{ error_ = bad_version( header.version() ); retval_ = 2; return true; }
|
{ error_ = bad_version( header.version() ); retval_ = 2; return true; }
|
||||||
if( !isvalid_ds( header.dictionary_size() ) )
|
if( !isvalid_ds( header.dictionary_size() ) )
|
||||||
{ error_ = bad_dict_msg; retval_ = 2; return true; }
|
{ 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();
|
const unsigned long long member_size = trailer.member_size();
|
||||||
if( member_size == 0 ) // skip trailing zeros
|
if( member_size == 0 ) // skip trailing zeros
|
||||||
{ while( i > Lzip_trailer::size && buffer[i-9] == 0 ) --i; continue; }
|
{ 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;
|
continue;
|
||||||
Lzip_header header;
|
Lzip_header header;
|
||||||
if( !read_header( fd, header, ipos + i - member_size ) ) return false;
|
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 Lzip_header & header2 = *(const Lzip_header *)( buffer + i );
|
||||||
const bool full_h2 = bsize - i >= Lzip_header::size;
|
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.";
|
if( !full_h2 ) error_ = "Last member in input file is truncated.";
|
||||||
else if( !check_header_error( header2, false ) )
|
else if( !check_header_error( header2, false ) )
|
||||||
error_ = "Last member in input file is truncated or corrupt.";
|
error_ = "Last member in input file is truncated or corrupt.";
|
||||||
retval_ = 2; return false;
|
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; }
|
{ error_ = corrupt_mm_msg; retval_ = 2; return false; }
|
||||||
if( !ignore_trailing )
|
if( !ignore_trailing )
|
||||||
{ error_ = trailing_msg; retval_ = 2; return false; }
|
{ 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 )
|
pos - Lzip_trailer::size ) != Lzip_trailer::size )
|
||||||
{ set_errno_error( "Error reading member trailer: " ); break; }
|
{ set_errno_error( "Error reading member trailer: " ); break; }
|
||||||
const unsigned long long member_size = trailer.member_size();
|
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( member_vector.empty() )
|
||||||
{ if( skip_trailing_data( infd, pos, ignore_trailing, loose_trailing ) )
|
{ 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;
|
break;
|
||||||
}
|
}
|
||||||
if( !read_header( infd, header, pos - member_size ) ) 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( member_vector.empty() )
|
||||||
{ if( skip_trailing_data( infd, pos, ignore_trailing, loose_trailing ) )
|
{ if( skip_trailing_data( infd, pos, ignore_trailing, loose_trailing ) )
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/* Tarlz - Archiver with multimember lzip compression
|
/* 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
|
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
|
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
|
/* 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
|
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
|
it under the terms of the GNU General Public License as published by
|
||||||
|
@ -27,10 +27,9 @@
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
#include <cstdarg>
|
#include <cstdarg>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdlib>
|
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h> // for pthread_t
|
||||||
#include <stdint.h> // for lzlib.h
|
#include <stdint.h> // for lzlib.h
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
@ -57,7 +56,7 @@ const char * const program_name = "tarlz";
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
const char * const program_year = "2022";
|
const char * const program_year = "2023";
|
||||||
const char * invocation_name = program_name; // default value
|
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"
|
" -f, --file=<archive> use archive file <archive>\n"
|
||||||
" -h, --dereference follow symlinks; archive the files they point to\n"
|
" -h, --dereference follow symlinks; archive the files they point to\n"
|
||||||
" -n, --threads=<n> set number of (de)compression threads [%ld]\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"
|
" -p, --preserve-permissions don't subtract the umask on extraction\n"
|
||||||
" -q, --quiet suppress all messages\n"
|
" -q, --quiet suppress all messages\n"
|
||||||
" -v, --verbose verbosely list files processed\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 * format_num3( long long num )
|
||||||
{
|
{
|
||||||
const char * const si_prefix = "kMGTPEZY";
|
const char * const si_prefix = "kMGTPEZYRQ";
|
||||||
const char * const binary_prefix = "KMGTPEZY";
|
const char * const binary_prefix = "KMGTPEZYRQ";
|
||||||
enum { buffers = 8, bufsize = 4 * sizeof num };
|
enum { buffers = 8, bufsize = 4 * sizeof num };
|
||||||
static char buffer[buffers][bufsize]; // circle of static buffers for printf
|
static char buffer[buffers][bufsize]; // circle of static buffers for printf
|
||||||
static int current = 0;
|
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 )
|
for( int i = 0; i < 8 && num != 0 && ( num / 1000 ) * 1000 == num; ++i )
|
||||||
{ num /= 1000; prefix = si_prefix[i]; }
|
{ num /= 1000; prefix = si_prefix[i]; }
|
||||||
if( prefix ) *(--p) = prefix;
|
if( prefix ) *(--p) = prefix;
|
||||||
const bool split = num >= 100000 || num <= -100000;
|
const bool split = num >= 10000 || num <= -10000;
|
||||||
|
|
||||||
for( int i = 0; ; )
|
for( int i = 0; ; )
|
||||||
{
|
{
|
||||||
long long onum = num; num /= 10;
|
const long long onum = num; num /= 10;
|
||||||
*(--p) = llabs( onum - 10 * num ) + '0'; if( num == 0 ) break;
|
*(--p) = llabs( onum - ( 10 * num ) ) + '0'; if( num == 0 ) break;
|
||||||
if( split && ++i >= 3 ) { i = 0; *(--p) = '_'; }
|
if( split && ++i >= 3 ) { i = 0; *(--p) = '_'; }
|
||||||
}
|
}
|
||||||
if( negative ) *(--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,
|
long long getnum( const char * const arg, const char * const option_name,
|
||||||
const long long llimit = LLONG_MIN,
|
const long long llimit = LLONG_MIN,
|
||||||
const long long ulimit = LLONG_MAX )
|
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
|
int exponent = 0; // 0 = bad multiplier
|
||||||
switch( tail[0] )
|
switch( tail[0] )
|
||||||
{
|
{
|
||||||
|
case 'Q': exponent = 10; break;
|
||||||
|
case 'R': exponent = 9; break;
|
||||||
case 'Y': exponent = 8; break;
|
case 'Y': exponent = 8; break;
|
||||||
case 'Z': exponent = 7; break;
|
case 'Z': exponent = 7; break;
|
||||||
case 'E': exponent = 6; 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
|
// parse time as 'long long' even if time_t is 32-bit
|
||||||
long long parse_mtime( const char * arg, const char * const pn )
|
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 == '/' )
|
else if( *arg == '.' || *arg == '/' )
|
||||||
{
|
{
|
||||||
struct stat st;
|
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 ); }
|
{ 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 )
|
int open_instream( const std::string & name )
|
||||||
{
|
{
|
||||||
const int infd = open( name.c_str(), O_RDONLY | O_BINARY );
|
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 )
|
void show_error( const char * const msg, const int errcode, const bool help )
|
||||||
{
|
{
|
||||||
if( verbosity < 0 ) return;
|
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 '0': case '1': case '2': case '3': case '4':
|
||||||
case '5': case '6': case '7': case '8': case '9':
|
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 'A': set_mode( cl_opts.program_mode, m_concatenate ); break;
|
||||||
case 'B': cl_opts.data_size =
|
case 'B': cl_opts.data_size =
|
||||||
getnum( arg, pn, min_data_size, max_data_size ); break;
|
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_own: cl_opts.uid = parse_owner( arg, pn ); break;
|
||||||
case opt_per: cl_opts.permissive = true; break;
|
case opt_per: cl_opts.permissive = true; break;
|
||||||
case opt_sol: cl_opts.solidity = solid; 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;
|
case opt_wn: cl_opts.warn_newer = true; break;
|
||||||
default : internal_error( "uncaught option" );
|
default : internal_error( "uncaught option" );
|
||||||
}
|
}
|
||||||
|
@ -679,8 +672,7 @@ int main( const int argc, const char * const argv[] )
|
||||||
program_name, f_pn, z_pn );
|
program_name, f_pn, z_pn );
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if( cl_opts.program_mode == m_compress &&
|
if( cl_opts.program_mode == m_compress && cl_opts.uncompressed() )
|
||||||
( cl_opts.level < 0 || cl_opts.level > 9 ) )
|
|
||||||
{
|
{
|
||||||
if( verbosity >= 0 )
|
if( verbosity >= 0 )
|
||||||
std::fprintf( stderr, "%s: Option '--uncompressed' can't be used with '%s'.\n",
|
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 );
|
setmode( STDOUT_FILENO, O_BINARY );
|
||||||
#endif
|
#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;
|
if( cl_opts.level == 0 ) cl_opts.data_size = 1 << 20;
|
||||||
else cl_opts.data_size = 2 * option_mapping[cl_opts.level].dictionary_size;
|
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_create: return encode( cl_opts );
|
||||||
case m_compress: return compress( cl_opts );
|
case m_compress: return compress( cl_opts );
|
||||||
case m_concatenate: return concatenate( 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_diff:
|
||||||
case m_extract:
|
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
|
/* 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
|
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
|
it under the terms of the GNU General Public License as published by
|
||||||
|
@ -16,14 +16,15 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <climits>
|
#include <climits>
|
||||||
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
#define max_file_size ( LLONG_MAX - header_size )
|
#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];
|
typedef uint8_t Tar_header[header_size];
|
||||||
|
|
||||||
enum Offsets {
|
enum Offsets {
|
||||||
|
@ -46,7 +47,7 @@ enum Typeflag {
|
||||||
const uint8_t ustar_magic[magic_l] =
|
const uint8_t ustar_magic[magic_l] =
|
||||||
{ 0x75, 0x73, 0x74, 0x61, 0x72, 0 }; // "ustar\0"
|
{ 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; }
|
{ return std::memcmp( header + magic_o, ustar_magic, magic_l ) == 0; }
|
||||||
|
|
||||||
inline void init_tar_header( Tar_header header ) // set magic and version
|
inline void init_tar_header( Tar_header header ) // set magic and version
|
||||||
|
@ -154,7 +155,7 @@ public:
|
||||||
unsigned decimal_size() const;
|
unsigned decimal_size() const;
|
||||||
unsigned print( char * const buf ) const;
|
unsigned print( char * const buf ) const;
|
||||||
bool parse( const char * const ptr, const char ** const tailp,
|
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
|
long long uid_, gid_; // may not fit in unsigned int
|
||||||
Etime atime_, mtime_;
|
Etime atime_, mtime_;
|
||||||
|
|
||||||
// cached sizes; if full_size_ < 0 they must be recalculated
|
// cached sizes; if full_size_ <= -4 they must be recalculated
|
||||||
mutable long long edsize_; // extended data size
|
mutable int edsize_; // extended data size
|
||||||
mutable long long padded_edsize_; // edsize rounded up
|
mutable int padded_edsize_; // edsize rounded up
|
||||||
mutable long long full_size_; // header + padded edsize
|
mutable int full_size_; // header + padded edsize
|
||||||
mutable long long linkpath_recsize_;
|
mutable int linkpath_recsize_;
|
||||||
mutable long long path_recsize_;
|
mutable int path_recsize_;
|
||||||
mutable int file_size_recsize_;
|
mutable int file_size_recsize_;
|
||||||
mutable int uid_recsize_;
|
mutable int uid_recsize_;
|
||||||
mutable int gid_recsize_;
|
mutable int gid_recsize_;
|
||||||
|
@ -183,8 +184,7 @@ class Extended // stores metadata from/for extended records
|
||||||
mutable bool crc_present_;
|
mutable bool crc_present_;
|
||||||
|
|
||||||
void calculate_sizes() const;
|
void calculate_sizes() const;
|
||||||
void unknown_keyword( const char * const buf,
|
void unknown_keyword( const char * const buf, const int size ) const;
|
||||||
const unsigned long long size ) const;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static const std::string crc_record;
|
static const std::string crc_record;
|
||||||
|
@ -205,11 +205,6 @@ public:
|
||||||
atime_recsize_ = 0; mtime_recsize_ = 0; crc_present_ = false;
|
atime_recsize_ = 0; mtime_recsize_ = 0; crc_present_ = false;
|
||||||
removed_prefix.clear(); }
|
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 & linkpath() const { return linkpath_; }
|
||||||
const std::string & path() const { return path_; }
|
const std::string & path() const { return path_; }
|
||||||
long long file_size() const { return file_size_; }
|
long long file_size() const { return file_size_; }
|
||||||
|
@ -219,23 +214,26 @@ public:
|
||||||
const Etime & atime() const { return atime_; }
|
const Etime & atime() const { return atime_; }
|
||||||
const Etime & mtime() const { return mtime_; }
|
const Etime & mtime() const { return mtime_; }
|
||||||
|
|
||||||
void linkpath( const char * const lp ) { linkpath_ = lp; full_size_ = -1; }
|
void linkpath( const char * const lp ) { linkpath_ = lp; full_size_ = -4; }
|
||||||
void path( const char * const p ) { path_ = p; full_size_ = -1; }
|
void path( const char * const p ) { path_ = p; full_size_ = -4; }
|
||||||
void file_size( const long long fs ) { full_size_ = -1;
|
void file_size( const long long fs ) { full_size_ = -4;
|
||||||
file_size_ = ( fs >= 0 && fs <= max_file_size ) ? fs : 0; }
|
file_size_ = ( fs >= 0 && fs <= max_file_size ) ? fs : 0; }
|
||||||
bool set_uid( const long long id )
|
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 )
|
bool set_gid( const long long id )
|
||||||
{ if( id >= 0 ) { gid_ = id; full_size_ = -1; } return id >= 0; }
|
{ if( id >= 0 ) { gid_ = id; full_size_ = -4; } return id >= 0; }
|
||||||
void set_atime( const long long s ) { atime_.set( s ); full_size_ = -1; }
|
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_ = -1; }
|
void set_mtime( const long long s ) { mtime_.set( s ); full_size_ = -4; }
|
||||||
|
|
||||||
long long full_size() const
|
/* Return the size of the extended block, or 0 if empty.
|
||||||
{ if( full_size_ < 0 ) calculate_sizes(); return full_size_; }
|
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_; }
|
bool crc_present() const { return crc_present_; }
|
||||||
long long format_block( Resizable_buffer & rbuf ) const;
|
bool parse( const char * const buf, const int edsize,
|
||||||
bool parse( const char * const buf, const unsigned long long edsize,
|
|
||||||
const bool permissive );
|
const bool permissive );
|
||||||
void fill_from_ustar( const Tar_header header );
|
void fill_from_ustar( const Tar_header header );
|
||||||
};
|
};
|
||||||
|
@ -337,17 +335,17 @@ struct Lzip_header
|
||||||
// 4 version
|
// 4 version
|
||||||
// 5 coded dictionary size
|
// 5 coded dictionary size
|
||||||
|
|
||||||
bool verify_magic() const
|
bool check_magic() const
|
||||||
{ return ( std::memcmp( data, lzip_magic, 4 ) == 0 ); }
|
{ 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 )
|
for( int i = 0; i < sz && i < 4; ++i )
|
||||||
if( data[i] != lzip_magic[i] ) return false;
|
if( data[i] != lzip_magic[i] ) return false;
|
||||||
return ( sz > 0 );
|
return ( sz > 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
bool verify_corrupt() const // detect corrupt header
|
bool check_corrupt() const // detect corrupt header
|
||||||
{
|
{
|
||||||
int matches = 0;
|
int matches = 0;
|
||||||
for( int i = 0; i < 4; ++i )
|
for( int i = 0; i < 4; ++i )
|
||||||
|
@ -356,7 +354,7 @@ struct Lzip_header
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t version() const { return data[4]; }
|
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
|
unsigned dictionary_size() const
|
||||||
{
|
{
|
||||||
|
@ -366,8 +364,8 @@ struct Lzip_header
|
||||||
return sz;
|
return sz;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool verify() const
|
bool check() const
|
||||||
{ return verify_magic() && verify_version() &&
|
{ return check_magic() && check_version() &&
|
||||||
isvalid_ds( dictionary_size() ); }
|
isvalid_ds( dictionary_size() ); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -400,7 +398,7 @@ struct Lzip_trailer
|
||||||
return tmp;
|
return tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool verify_consistency() const // check internal consistency
|
bool check_consistency() const // check internal consistency
|
||||||
{
|
{
|
||||||
const unsigned crc = data_crc();
|
const unsigned crc = data_crc();
|
||||||
const unsigned long long dsize = data_size();
|
const unsigned long long dsize = data_size();
|
||||||
|
@ -442,6 +440,7 @@ struct Cl_options // command line options
|
||||||
bool ignore_ids;
|
bool ignore_ids;
|
||||||
bool ignore_overflow;
|
bool ignore_overflow;
|
||||||
bool keep_damaged;
|
bool keep_damaged;
|
||||||
|
bool level_set; // compression level set in command line
|
||||||
bool missing_crc;
|
bool missing_crc;
|
||||||
bool mtime_set;
|
bool mtime_set;
|
||||||
bool permissive;
|
bool permissive;
|
||||||
|
@ -453,9 +452,14 @@ struct Cl_options // command line options
|
||||||
solidity( bsolid ), data_size( 0 ), debug_level( 0 ), level( 6 ),
|
solidity( bsolid ), data_size( 0 ), debug_level( 0 ), level( 6 ),
|
||||||
num_files( 0 ), num_workers( -1 ), out_slots( 64 ), dereference( false ),
|
num_files( 0 ), num_workers( -1 ), out_slots( 64 ), dereference( false ),
|
||||||
filenames_given( false ), ignore_ids( false ), ignore_overflow( false ),
|
filenames_given( false ), ignore_ids( false ), ignore_overflow( false ),
|
||||||
keep_damaged( false ), missing_crc( false ), mtime_set( false ),
|
keep_damaged( false ), level_set( false ), missing_crc( false ),
|
||||||
permissive( false ), preserve_permissions( false ), warn_newer( 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 == "-"; }
|
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";
|
const char * const chdir_msg = "Error changing working directory";
|
||||||
|
|
||||||
// defined in common.cc
|
// 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 );
|
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 readblock( const int fd, uint8_t * const buf, const int size );
|
||||||
int writeblock( const int fd, const 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
|
// defined in common_decode.cc
|
||||||
bool block_is_zero( const uint8_t * const buf, const int size );
|
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 );
|
const int vlevel, Resizable_buffer & rbuf );
|
||||||
bool check_skip_filename( const Cl_options & cl_opts,
|
bool check_skip_filename( const Cl_options & cl_opts,
|
||||||
std::vector< char > & name_pending,
|
std::vector< char > & name_pending,
|
||||||
const char * const filename );
|
const char * const filename, const int chdir_fd = -1 );
|
||||||
mode_t get_umask();
|
|
||||||
bool make_path( const std::string & name );
|
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
|
// 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 );
|
int compress( const Cl_options & cl_opts );
|
||||||
|
|
||||||
// defined in create.cc
|
// 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 );
|
bool write_eoa_records( const int outfd, const bool compressed );
|
||||||
const char * remove_leading_dotslash( const char * const filename,
|
const char * remove_leading_dotslash( const char * const filename,
|
||||||
std::string * const removed_prefixp, const bool dotdot = false );
|
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,
|
bool fill_headers( const char * const filename, Extended & extended,
|
||||||
Tar_header header, long long & file_size, const int flag );
|
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 file_size,
|
||||||
const unsigned long long target_size,
|
const unsigned long long target_size,
|
||||||
unsigned long long & partial_data_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 );
|
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 );
|
bool has_lz_ext( const std::string & name );
|
||||||
int concatenate( const Cl_options & cl_opts );
|
int concatenate( const Cl_options & cl_opts );
|
||||||
int encode( const Cl_options & cl_opts );
|
int encode( const Cl_options & cl_opts );
|
||||||
|
@ -591,10 +589,10 @@ extern const char * const program_name;
|
||||||
struct stat;
|
struct stat;
|
||||||
int hstat( const char * const filename, struct stat * const st,
|
int hstat( const char * const filename, struct stat * const st,
|
||||||
const bool dereference );
|
const bool dereference );
|
||||||
|
bool nonempty_arg( const Arg_parser & parser, const int i );
|
||||||
int open_instream( const std::string & name );
|
int open_instream( const std::string & name );
|
||||||
int open_outstream( const std::string & name, const bool create = true,
|
int open_outstream( const std::string & name, const bool create = true,
|
||||||
Resizable_buffer * const rbufp = 0, const bool force = 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,
|
void show_error( const char * const msg, const int errcode = 0,
|
||||||
const bool help = false );
|
const bool help = false );
|
||||||
bool format_error( Resizable_buffer & rbuf, const int errcode,
|
bool format_error( Resizable_buffer & rbuf, const int errcode,
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
#! /bin/sh
|
#! /bin/sh
|
||||||
# check script for Tarlz - Archiver with multimember lzip compression
|
# 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
|
# This script is free software: you have unlimited permission
|
||||||
# to copy, distribute, and modify it.
|
# to copy, distribute, and modify it.
|
||||||
|
|
||||||
LC_ALL=C
|
LC_ALL=C
|
||||||
export LC_ALL
|
export LC_ALL
|
||||||
objdir=`pwd`
|
objdir="`pwd`"
|
||||||
testdir=`cd "$1" ; pwd`
|
testdir="`cd "$1" ; pwd`"
|
||||||
TARLZ="${objdir}"/tarlz
|
TARLZ="${objdir}"/tarlz
|
||||||
framework_failure() { echo "failure in testing framework" ; exit 1 ; }
|
framework_failure() { echo "failure in testing framework" ; exit 1 ; }
|
||||||
|
|
||||||
|
@ -59,6 +59,8 @@ eoa_lz="${testdir}"/eoa_blocks.tar.lz
|
||||||
fail=0
|
fail=0
|
||||||
lwarnc=0
|
lwarnc=0
|
||||||
test_failed() { fail=1 ; printf " $1" ; [ -z "$2" ] || printf "($2)" ; }
|
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 ] &&
|
cyg_symlink() { [ ${lwarnc} = 0 ] &&
|
||||||
printf "\nwarning: your OS follows symbolic links to directories even when tarlz asks it not to\n$1"
|
printf "\nwarning: your OS follows symbolic links to directories even when tarlz asks it not to\n$1"
|
||||||
lwarnc=1 ; }
|
lwarnc=1 ; }
|
||||||
|
@ -116,6 +118,7 @@ cyg_symlink() { [ ${lwarnc} = 0 ] &&
|
||||||
|
|
||||||
"${TARLZ}" --check-lib # just print warning
|
"${TARLZ}" --check-lib # just print warning
|
||||||
[ $? != 2 ] || test_failed $LINENO # unless bad lzlib.h
|
[ $? != 2 ] || test_failed $LINENO # unless bad lzlib.h
|
||||||
|
|
||||||
printf "testing tarlz-%s..." "$2"
|
printf "testing tarlz-%s..." "$2"
|
||||||
|
|
||||||
"${TARLZ}" -q -tf "${in}"
|
"${TARLZ}" -q -tf "${in}"
|
||||||
|
@ -131,22 +134,20 @@ printf "testing tarlz-%s..." "$2"
|
||||||
"${TARLZ}" -q -cf out.tar.lz
|
"${TARLZ}" -q -cf out.tar.lz
|
||||||
[ $? = 1 ] || test_failed $LINENO
|
[ $? = 1 ] || test_failed $LINENO
|
||||||
[ ! -e out.tar.lz ] || test_failed $LINENO
|
[ ! -e out.tar.lz ] || test_failed $LINENO
|
||||||
"${TARLZ}" -rf out.tar.lz || test_failed $LINENO
|
"${TARLZ}" -q -cf out.tar
|
||||||
[ ! -e out.tar.lz ] || test_failed $LINENO
|
[ $? = 1 ] || test_failed $LINENO
|
||||||
"${TARLZ}" -r || test_failed $LINENO
|
[ ! -e out.tar ] || test_failed $LINENO
|
||||||
"${TARLZ}" --uncompressed -q -rf out.tar "${in}"
|
"${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
|
[ $? = 1 ] || test_failed $LINENO
|
||||||
[ ! -e out.tar ] || 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
|
"${TARLZ}" -q -c "${in}" nx_file > /dev/null
|
||||||
[ $? = 1 ] || test_failed $LINENO
|
[ $? = 1 ] || test_failed $LINENO
|
||||||
"${TARLZ}" -q -c -C nx_dir "${in}"
|
"${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
|
"${TARLZ}" -q -tf empty.tlz
|
||||||
[ $? = 2 ] || test_failed $LINENO
|
[ $? = 2 ] || test_failed $LINENO
|
||||||
rm -f empty.tar.lz empty.tlz || framework_failure
|
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
|
"${TARLZ}" -q -cd # test mixed operations
|
||||||
[ $? = 1 ] || test_failed $LINENO
|
[ $? = 1 ] || test_failed $LINENO
|
||||||
"${TARLZ}" -q -cr
|
"${TARLZ}" -q -cr
|
||||||
|
@ -239,6 +245,9 @@ rm -f foo bar baz || framework_failure
|
||||||
cmp cfoo foo || test_failed $LINENO
|
cmp cfoo foo || test_failed $LINENO
|
||||||
cmp cbar bar || test_failed $LINENO
|
cmp cbar bar || test_failed $LINENO
|
||||||
cmp cbaz baz || 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
|
rm -f foo bar baz || framework_failure
|
||||||
for i in 0 2 6 ; do
|
for i in 0 2 6 ; do
|
||||||
"${TARLZ}" -n$i -xf "${test3_lz}" --missing-crc || test_failed $LINENO $i
|
"${TARLZ}" -n$i -xf "${test3_lz}" --missing-crc || test_failed $LINENO $i
|
||||||
|
@ -273,6 +282,34 @@ for i in 0 2 6 ; do
|
||||||
rm -f foo bar baz || framework_failure
|
rm -f foo bar baz || framework_failure
|
||||||
done
|
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
|
for i in "${test3dir}" "${test3dir_lz}" ; do
|
||||||
"${TARLZ}" -q -tf "$i" --missing-crc || test_failed $LINENO "$i"
|
"${TARLZ}" -q -tf "$i" --missing-crc || test_failed $LINENO "$i"
|
||||||
"${TARLZ}" -q -xf "$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
|
rm -f foo bar baz || framework_failure
|
||||||
done
|
done
|
||||||
"${TARLZ}" -n0 -xf "${testdir}"/test3_eoa3.tar.lz || test_failed $LINENO
|
"${TARLZ}" -n0 -xf "${testdir}"/test3_eoa3.tar.lz || test_failed $LINENO
|
||||||
cmp cfoo foo || test_failed $LINENO $i
|
cmp cfoo foo || test_failed $LINENO
|
||||||
[ ! -e bar ] || test_failed $LINENO $i
|
[ ! -e bar ] || test_failed $LINENO
|
||||||
[ ! -e baz ] || test_failed $LINENO $i
|
[ ! -e baz ] || test_failed $LINENO
|
||||||
rm -f foo bar baz || framework_failure
|
rm -f foo bar baz || framework_failure
|
||||||
|
|
||||||
# test --list and --extract tar in tar.lz
|
# 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}"
|
"${TARLZ}" -Aqf out.tar.lz "${test3_lz}"
|
||||||
[ $? = 2 ] || test_failed $LINENO
|
[ $? = 2 ] || test_failed $LINENO
|
||||||
cat "${in_tar_lz}" > out.tar.lz || framework_failure
|
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}" -Af out.tar.lz "${test3_lz}" || test_failed $LINENO
|
||||||
"${TARLZ}" -xf out.tar.lz || test_failed $LINENO
|
"${TARLZ}" -xf out.tar.lz || test_failed $LINENO
|
||||||
cmp "${in}" test.txt || 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}"
|
"${TARLZ}" -Aqf out.tar "${test3}"
|
||||||
[ $? = 2 ] || test_failed $LINENO
|
[ $? = 2 ] || test_failed $LINENO
|
||||||
cat "${in_tar}" > out.tar || framework_failure
|
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}" -Af out.tar "${test3}" || test_failed $LINENO
|
||||||
"${TARLZ}" -xf out.tar || test_failed $LINENO
|
"${TARLZ}" -xf out.tar || test_failed $LINENO
|
||||||
cmp "${in}" test.txt || test_failed $LINENO
|
cmp "${in}" test.txt || test_failed $LINENO
|
||||||
|
@ -568,11 +613,13 @@ printf "\ntesting --create..."
|
||||||
# test --create
|
# test --create
|
||||||
cat "${in}" > test.txt || framework_failure
|
cat "${in}" > test.txt || framework_failure
|
||||||
"${TARLZ}" --warn-newer -0 -cf out.tar.lz test.txt || test_failed $LINENO
|
"${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
|
rm -f test.txt || framework_failure
|
||||||
"${TARLZ}" -xf out.tar.lz --missing-crc || test_failed $LINENO
|
"${TARLZ}" -xf out.tar.lz --missing-crc || test_failed $LINENO
|
||||||
cmp "${in}" test.txt || test_failed $LINENO
|
cmp "${in}" test.txt || test_failed $LINENO
|
||||||
cat "${in}" > test.txt || framework_failure
|
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
|
rm -f test.txt || framework_failure
|
||||||
"${TARLZ}" -xf out.tar --missing-crc || test_failed $LINENO
|
"${TARLZ}" -xf out.tar --missing-crc || test_failed $LINENO
|
||||||
cmp "${in}" test.txt || 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 cbar dir1/bar || test_failed $LINENO
|
||||||
cmp cbaz dir1/baz || test_failed $LINENO
|
cmp cbaz dir1/baz || test_failed $LINENO
|
||||||
rm -f dir1/foo dir1/bar dir1/baz || framework_failure
|
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
|
test_failed $LINENO
|
||||||
cmp cfoo dir1/foo || test_failed $LINENO
|
cmp cfoo dir1/foo || test_failed $LINENO
|
||||||
cmp cbar dir1/bar || 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 cfoo > foo || framework_failure
|
||||||
cat cbar > bar || framework_failure
|
cat cbar > bar || framework_failure
|
||||||
cat cbaz > baz || 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
|
rm -f foo bar baz || framework_failure
|
||||||
"${TARLZ}" -xf out.tar || test_failed $LINENO
|
"${TARLZ}" -xf out.tar || test_failed $LINENO
|
||||||
cmp cfoo foo || 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' \
|
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
|
'2017-10-01 09:00' '2017-10-01 09' 2017-10-01 ./bar ; do
|
||||||
touch foo || framework_failure
|
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}" -q -df out.tar && test_failed $LINENO "$i"
|
||||||
"${TARLZ}" -xf out.tar || test_failed $LINENO "$i"
|
"${TARLZ}" -xf out.tar || test_failed $LINENO "$i"
|
||||||
|
if [ "${d_works}" = yes ] ; then
|
||||||
"${TARLZ}" -df out.tar --ignore-overflow || test_failed $LINENO "$i"
|
"${TARLZ}" -df out.tar --ignore-overflow || test_failed $LINENO "$i"
|
||||||
|
fi
|
||||||
done
|
done
|
||||||
rm -f out.tar foo bar || framework_failure
|
rm -f out.tar foo bar || framework_failure
|
||||||
|
|
||||||
|
@ -708,9 +759,10 @@ mkdir dir || framework_failure
|
||||||
for i in ${dates} ; do
|
for i in ${dates} ; do
|
||||||
# Skip a time stamp $i if it's out of range for this platform,
|
# 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.
|
# 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
|
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
|
"${TARLZ}" -df out.tar || test_failed $LINENO
|
||||||
rm -rf out.tar dir || framework_failure
|
rm -rf out.tar dir || framework_failure
|
||||||
|
|
||||||
|
@ -718,8 +770,10 @@ printf "\ntesting --diff..."
|
||||||
|
|
||||||
# test --diff
|
# test --diff
|
||||||
"${TARLZ}" -xf "${test3_lz}" || test_failed $LINENO
|
"${TARLZ}" -xf "${test3_lz}" || test_failed $LINENO
|
||||||
"${TARLZ}" --uncompressed -cf out.tar foo || test_failed $LINENO
|
"${TARLZ}" -cf out.tar foo || test_failed $LINENO
|
||||||
"${TARLZ}" --uncompressed -cf aout.tar foo --anonymous || 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
|
if cmp out.tar aout.tar > /dev/null ; then
|
||||||
printf "\nwarning: '--diff' test can't be run as root.\n"
|
printf "\nwarning: '--diff' test can't be run as root.\n"
|
||||||
else
|
else
|
||||||
|
@ -731,7 +785,7 @@ else
|
||||||
"${TARLZ}" -n$i -df "${test3_lz}" --exclude '*' || test_failed $LINENO $i
|
"${TARLZ}" -n$i -df "${test3_lz}" --exclude '*' || test_failed $LINENO $i
|
||||||
"${TARLZ}" -n$i -df "${in_tar_lz}" --exclude '*' || test_failed $LINENO $i
|
"${TARLZ}" -n$i -df "${in_tar_lz}" --exclude '*' || test_failed $LINENO $i
|
||||||
rm -f bar || framework_failure
|
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
|
test_failed $LINENO $i
|
||||||
"${TARLZ}" -n$i -df "${test3_lz}" --exclude bar --ignore-ids ||
|
"${TARLZ}" -n$i -df "${test3_lz}" --exclude bar --ignore-ids ||
|
||||||
test_failed $LINENO $i
|
test_failed $LINENO $i
|
||||||
|
@ -879,12 +933,14 @@ if [ "${ln_works}" = yes ] ; then
|
||||||
cat cbar > dir/bar || framework_failure
|
cat cbar > dir/bar || framework_failure
|
||||||
cat cbaz > dir/baz || framework_failure
|
cat cbaz > dir/baz || framework_failure
|
||||||
ln -s dir dir_link || framework_failure
|
ln -s dir dir_link || framework_failure
|
||||||
"${TARLZ}" -0 -cf out1 dir_link || test_failed $LINENO
|
"${TARLZ}" -0 -c dir_link > out1 || test_failed $LINENO
|
||||||
"${TARLZ}" --un -cf out2 dir_link || test_failed $LINENO
|
is_compressed out1 || test_failed $LINENO
|
||||||
"${TARLZ}" -0 -n0 -cf out3 dir_link || test_failed $LINENO
|
"${TARLZ}" --un -c dir_link > out2 || test_failed $LINENO
|
||||||
"${TARLZ}" -0 -h -cf hout1 dir_link || test_failed $LINENO
|
is_uncompressed out2 || test_failed $LINENO
|
||||||
"${TARLZ}" --un -h -cf hout2 dir_link || test_failed $LINENO
|
"${TARLZ}" -0 -n0 -c dir_link > out3 || test_failed $LINENO
|
||||||
"${TARLZ}" -0 -n0 -h -cf hout3 dir_link || 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
|
rm -rf dir dir_link || framework_failure
|
||||||
for i in 1 2 3 ; do
|
for i in 1 2 3 ; do
|
||||||
"${TARLZ}" -xf out$i --exclude='dir_link/*' dir_link ||
|
"${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
|
"${TARLZ}" -0 -rf aout.tar.lz bar baz --no-solid || test_failed $LINENO
|
||||||
cmp nout.tar.lz aout.tar.lz || test_failed $LINENO
|
cmp nout.tar.lz aout.tar.lz || test_failed $LINENO
|
||||||
rm -f nout.tar.lz aout.tar.lz || framework_failure
|
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
|
touch aout.tar.lz || framework_failure # append to empty file
|
||||||
"${TARLZ}" -0 -rf aout.tar.lz foo bar baz || test_failed $LINENO
|
"${TARLZ}" -0 -rf aout.tar.lz foo bar baz || test_failed $LINENO
|
||||||
cmp out.tar.lz aout.tar.lz || 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
|
cmp out.tar.lz aout.tar.lz || test_failed $LINENO
|
||||||
"${TARLZ}" -0 -r foo bar baz > aout.tar.lz || test_failed $LINENO # to stdout
|
"${TARLZ}" -0 -r foo bar baz > aout.tar.lz || test_failed $LINENO # to stdout
|
||||||
cmp out.tar.lz aout.tar.lz || 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 archive
|
"${TARLZ}" --un -q -rf aout.tar.lz foo bar baz # contradictory ext
|
||||||
[ $? = 2 ] || test_failed $LINENO
|
[ $? = 1 ] || test_failed $LINENO
|
||||||
cmp out.tar.lz aout.tar.lz || 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
|
cat "${eoa_lz}" > aout.tar.lz || framework_failure # append to empty archive
|
||||||
"${TARLZ}" -0 -rf aout.tar.lz foo bar baz || test_failed $LINENO
|
"${TARLZ}" -0 -rf aout.tar.lz foo bar baz || test_failed $LINENO
|
||||||
cmp out.tar.lz aout.tar.lz || 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
|
rm -f out.tar.lz aout.tar.lz || framework_failure
|
||||||
|
|
||||||
# test --append --uncompressed
|
# test --append --uncompressed
|
||||||
"${TARLZ}" --un -cf out.tar foo bar baz || test_failed $LINENO
|
"${TARLZ}" -cf out.tar foo bar baz || test_failed $LINENO
|
||||||
"${TARLZ}" --un -cf aout.tar foo || test_failed $LINENO
|
"${TARLZ}" -cf aout.tar foo || test_failed $LINENO
|
||||||
"${TARLZ}" --un -rf aout.tar foo bar baz --exclude 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
|
cmp out.tar aout.tar || test_failed $LINENO
|
||||||
rm -f aout.tar || framework_failure
|
rm -f aout.tar || framework_failure
|
||||||
touch aout.tar.lz empty || framework_failure # wrong extension empty file
|
touch aout.tar empty || framework_failure # contradictory ext empty file
|
||||||
"${TARLZ}" --un -q -rf aout.tar.lz foo bar baz
|
"${TARLZ}" -0 -q -rf aout.tar foo bar baz
|
||||||
[ $? = 2 ] || test_failed $LINENO
|
[ $? = 1 ] || test_failed $LINENO
|
||||||
cmp aout.tar.lz empty || test_failed $LINENO
|
cmp aout.tar empty || test_failed $LINENO
|
||||||
rm -f aout.tar.lz empty || framework_failure
|
rm -f aout.tar empty || framework_failure
|
||||||
touch aout.tar || framework_failure # append to empty file
|
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
|
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
|
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
|
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
|
[ $? = 1 ] || test_failed $LINENO
|
||||||
cmp out.tar aout.tar || 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
|
cmp out.tar aout.tar || test_failed $LINENO
|
||||||
"${TARLZ}" --un -r foo bar baz > aout.tar || test_failed $LINENO # to stdout
|
"${TARLZ}" --un -r foo bar baz > aout.tar || test_failed $LINENO # to stdout
|
||||||
cmp out.tar aout.tar || test_failed $LINENO
|
cmp out.tar aout.tar || test_failed $LINENO
|
||||||
"${TARLZ}" -0 -q -rf aout.tar foo bar baz # wrong extension archive
|
"${TARLZ}" -0 -q -rf aout.tar foo bar baz # contradictory ext
|
||||||
[ $? = 2 ] || test_failed $LINENO
|
[ $? = 1 ] || test_failed $LINENO
|
||||||
cmp out.tar aout.tar || test_failed $LINENO
|
cmp out.tar aout.tar || test_failed $LINENO
|
||||||
cat "${eoa}" > aout.tar || framework_failure # append to empty archive
|
cat "${eoa}" > aout.tar || framework_failure # append to empty archive
|
||||||
"${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}" -0 -q -rf aout.tar foo bar baz # wrong extension empty archive
|
|
||||||
[ $? = 2 ] || test_failed $LINENO
|
|
||||||
cmp out.tar aout.tar || test_failed $LINENO
|
cmp out.tar aout.tar || test_failed $LINENO
|
||||||
rm -f out.tar aout.tar || framework_failure
|
rm -f out.tar aout.tar || framework_failure
|
||||||
|
|
||||||
|
@ -1015,7 +1062,8 @@ rmdir dir1 || framework_failure
|
||||||
rmdir dir1
|
rmdir dir1
|
||||||
rm -f out.tar.lz || framework_failure
|
rm -f out.tar.lz || framework_failure
|
||||||
mkdir dir1 || 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
|
rmdir dir1 || framework_failure
|
||||||
"${TARLZ}" -xf out.tar || test_failed $LINENO
|
"${TARLZ}" -xf out.tar || test_failed $LINENO
|
||||||
[ -d dir1 ] || test_failed $LINENO
|
[ -d dir1 ] || test_failed $LINENO
|
||||||
|
@ -1110,8 +1158,8 @@ cat cfoo > foo || framework_failure
|
||||||
cat cbar > bar || framework_failure
|
cat cbar > bar || framework_failure
|
||||||
cat cbaz > baz || framework_failure
|
cat cbaz > baz || framework_failure
|
||||||
cat "${in}" > test.txt || framework_failure
|
cat "${in}" > test.txt || framework_failure
|
||||||
"${TARLZ}" --un -cf out.tar test.txt foo bar baz test.txt || test_failed $LINENO
|
"${TARLZ}" -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 out3.tar foo bar baz || test_failed $LINENO
|
||||||
cat out.tar > outz.tar || framework_failure
|
cat out.tar > outz.tar || framework_failure
|
||||||
cat out3.tar > out3z.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
|
rm -f out outz.tar.lz out3z.tar.lz || framework_failure
|
||||||
done
|
done
|
||||||
# concatenate and compress
|
# concatenate and compress
|
||||||
"${TARLZ}" --un -cf foo.tar foo || test_failed $LINENO
|
"${TARLZ}" -cf foo.tar foo || test_failed $LINENO
|
||||||
"${TARLZ}" --un -cf bar.tar bar || test_failed $LINENO
|
"${TARLZ}" -cf bar.tar bar || test_failed $LINENO
|
||||||
"${TARLZ}" --un -cf baz.tar baz || 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 ||
|
"${TARLZ}" -A foo.tar bar.tar baz.tar | "${TARLZ}" -0 -z -o foobarbaz.tar.lz ||
|
||||||
test_failed $LINENO
|
test_failed $LINENO
|
||||||
cmp out3.tar.lz 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
|
# compress and concatenate
|
||||||
"${TARLZ}" -0 -z foo.tar bar.tar baz.tar || test_failed $LINENO
|
"${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 ||
|
"${TARLZ}" -A foo.tar.lz bar.tar.lz baz.tar.lz > foobarbaz.tar.lz ||
|
||||||
|
@ -1260,19 +1311,23 @@ done
|
||||||
if [ "${ln_works}" = yes ] ; then rm -rf dir1 || framework_failure ; fi
|
if [ "${ln_works}" = yes ] ; then rm -rf dir1 || framework_failure ; fi
|
||||||
|
|
||||||
for i in "${testdir}"/test3_nn.tar "${testdir}"/test3_nn.tar.lz ; do
|
for i in "${testdir}"/test3_nn.tar "${testdir}"/test3_nn.tar.lz ; do
|
||||||
"${TARLZ}" -q -n0 -tf "$i" || test_failed $LINENO $i
|
"${TARLZ}" -q -n0 -tf "$i" || test_failed $LINENO "$i"
|
||||||
"${TARLZ}" -q -n4 -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}" -q -n0 -xf "$i" || test_failed $LINENO "$i"
|
||||||
"${TARLZ}" -n0 -df "$i" --ignore-ids || test_failed $LINENO $i
|
if [ "${d_works}" = yes ] ; then
|
||||||
cmp cfoo foo || test_failed $LINENO $i
|
"${TARLZ}" -n0 -df "$i" --ignore-ids || test_failed $LINENO "$i"
|
||||||
[ ! -e bar ] || test_failed $LINENO $i
|
fi
|
||||||
cmp cbaz baz || test_failed $LINENO $i
|
cmp cfoo foo || test_failed $LINENO "$i"
|
||||||
|
[ ! -e bar ] || test_failed $LINENO "$i"
|
||||||
|
cmp cbaz baz || test_failed $LINENO "$i"
|
||||||
rm -f foo bar baz || framework_failure
|
rm -f foo bar baz || framework_failure
|
||||||
"${TARLZ}" -q -n4 -xf "$i" || test_failed $LINENO $i
|
"${TARLZ}" -q -n4 -xf "$i" || test_failed $LINENO "$i"
|
||||||
"${TARLZ}" -n4 -df "$i" --ignore-ids || test_failed $LINENO $i
|
if [ "${d_works}" = yes ] ; then
|
||||||
cmp cfoo foo || test_failed $LINENO $i
|
"${TARLZ}" -n4 -df "$i" --ignore-ids || test_failed $LINENO "$i"
|
||||||
[ ! -e bar ] || test_failed $LINENO $i
|
fi
|
||||||
cmp cbaz baz || test_failed $LINENO $i
|
cmp cfoo foo || test_failed $LINENO "$i"
|
||||||
|
[ ! -e bar ] || test_failed $LINENO "$i"
|
||||||
|
cmp cbaz baz || test_failed $LINENO "$i"
|
||||||
rm -f foo bar baz || framework_failure
|
rm -f foo bar baz || framework_failure
|
||||||
done
|
done
|
||||||
|
|
||||||
|
@ -1288,7 +1343,7 @@ for i in "${inbad1}" "${inbad2}" ; do
|
||||||
"${TARLZ}" -q -n0 -xf "${i}.tar.lz" --keep-damaged
|
"${TARLZ}" -q -n0 -xf "${i}.tar.lz" --keep-damaged
|
||||||
[ $? = 2 ] || test_failed $LINENO "$i"
|
[ $? = 2 ] || test_failed $LINENO "$i"
|
||||||
[ -e test.txt ] || 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
|
rm -f test.txt || framework_failure
|
||||||
done
|
done
|
||||||
#
|
#
|
||||||
|
|
Loading…
Add table
Reference in a new issue