1
0
Fork 0

Adding upstream version 0.25.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-02-17 21:27:24 +01:00
parent 4f5d0de2b2
commit 8853aa3bf2
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
33 changed files with 317 additions and 280 deletions

View file

@ -1,3 +1,13 @@
2024-01-03 Antonio Diaz Diaz <antonio@gnu.org>
* Version 0.25 released.
* New option '--ignore-metadata.
* create.cc, decode.cc, decode_lz.cc:
'#include <sys/types.h>' for major, minor, makedev on BSD systems.
* compress.cc: Reformat file diagnostics as 'PROGRAM: FILE: MESSAGE'.
(compress_archive): Create missing intermediate directories.
* configure, Makefile.in: New variable 'MAKEINFO'.
2023-09-20 Antonio Diaz Diaz <antonio@gnu.org>
* Version 0.24 released.
@ -42,7 +52,7 @@
* main.cc (getnum): Show option name and valid range if error.
(check_lib): Check that LZ_API_VERSION and LZ_version_string match.
(main): Report an error if -o is used with any operation except -z.
* Set variable LIBS from configure.
* configure: Set variable LIBS.
2021-06-14 Antonio Diaz Diaz <antonio@gnu.org>
@ -59,7 +69,7 @@
* Version 0.19 released.
* extended.cc: Print a diagnostic for each unknown keyword found.
* tarlz.h: Add a missing '#include <sys/types.h>'.
* tarlz.h: Add a missing '#include <sys/types.h>' for 'mode_t'.
2020-11-21 Antonio Diaz Diaz <antonio@gnu.org>
@ -219,7 +229,7 @@
* Version 0.1 released.
Copyright (C) 2013-2023 Antonio Diaz Diaz.
Copyright (C) 2013-2024 Antonio Diaz Diaz.
This file is a collection of facts, and thus it is not copyrightable,
but just in case, you have unlimited permission to copy, distribute, and

View file

@ -4,12 +4,11 @@ You will need a C++98 compiler with support for 'long long', and the
compression library lzlib installed. (gcc 3.3.6 or newer is recommended).
I use gcc 6.1.0 and 3.3.6, but the code should compile with any standards
compliant compiler.
Lzlib must be version 1.12 or newer.
Gcc is available at http://gcc.gnu.org.
Lzlib is available at http://www.nongnu.org/lzip/lzlib.html.
Lzlib must be version 1.12 or newer.
The operating system must allow signal handlers read access to objects with
static storage duration so that the cleanup handler for Control-C can delete
the partial output file in '-z, --compress' mode.
@ -75,7 +74,7 @@ After running 'configure', you can run 'make' and 'make install' as
explained above.
Copyright (C) 2013-2023 Antonio Diaz Diaz.
Copyright (C) 2013-2024 Antonio Diaz Diaz.
This file is free documentation: you have unlimited permission to copy,
distribute, and modify it.

View file

@ -29,6 +29,10 @@ main.o : main.cc
%.o : %.cc
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $<
# prevent 'make' from trying to remake source files
$(VPATH)/configure $(VPATH)/Makefile.in $(VPATH)/doc/$(pkgname).texi : ;
%.h %.cc : ;
$(objs) : Makefile
arg_parser.o : arg_parser.h
archive_reader.o : tarlz.h lzip_index.h archive_reader.h
@ -48,7 +52,6 @@ extended.o : tarlz.h
lzip_index.o : tarlz.h lzip_index.h
main.o : tarlz.h arg_parser.h
doc : info man
info : $(VPATH)/doc/$(pkgname).info

21
NEWS
View file

@ -1,17 +1,14 @@
Changes in version 0.24:
Changes in version 0.25:
The option '-C, --directory' is now position-dependent also for diff and
extract. (Reported by Devon Sean McCullough).
The new option '--ignore-metadata', which makes '-d, --diff' ignore
differences in file permissions, owner and group IDs, and modification time,
has been added.
Option '--uncompressed' can now be omitted if it can be deduced from the
archive name. (An uncompressed archive name lacks a '.lz' or '.tlz' extension).
'#include <sys/types.h>' for major, minor, makedev on BSD systems.
The diagnostic shown when a file being archived or an archive being
compressed fails to read, now shows the cause of the failure; end-of-file or
read error.
File diagnostics of '-z' have been reformatted as 'PROGRAM: FILE: MESSAGE'.
'-z, --compress' now exits with error status 2 if any input archive is an
empty file.
The option '-o, --output' now creates missing intermediate directories when
compressing to a file.
A failure in the '--diff' test of the testsuite on OS/2 has been fixed.
(Reported by Elbert Pol).
The variable MAKEINFO has been added to configure and Makefile.in.

11
README
View file

@ -6,7 +6,7 @@ lzlib.
Tarlz creates tar archives using a simplified and safer variant of the POSIX
pax format compressed in lzip format, keeping the alignment between tar
members and lzip members. The resulting multimember tar.lz archive is fully
members and lzip members. The resulting multimember tar.lz archive is
backward compatible with standard tar tools like GNU tar, which treat it
like any other tar.lz archive. Tarlz can append files to the end of such
compressed archives.
@ -61,7 +61,7 @@ large, making undetected corruption and archiver misbehavior more probable.
Headers and metadata must be protected separately from data because the
integrity checking of lzip may not be able to detect the corruption before
the metadata has been used, for example, to create a new file in the wrong
the metadata have been used, for example, to create a new file in the wrong
place.
Because of the above, tarlz protects the extended records with a Cyclic
@ -87,11 +87,10 @@ tar.lz
+===============+=================================================+========+
Copyright (C) 2013-2023 Antonio Diaz Diaz.
Copyright (C) 2013-2024 Antonio Diaz Diaz.
This file is free documentation: you have unlimited permission to copy,
distribute, and modify it.
The file Makefile.in is a data file used by configure to produce the
Makefile. It has the same copyright owner and permissions that configure
itself.
The file Makefile.in is a data file used by configure to produce the Makefile.
It has the same copyright owner and permissions that configure itself.

View file

@ -1,5 +1,5 @@
/* Tarlz - Archiver with multimember lzip compression
Copyright (C) 2013-2023 Antonio Diaz Diaz.
Copyright (C) 2013-2024 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
@ -76,7 +76,7 @@ void xLZ_decompress_write( LZ_Decoder * const decoder,
Archive_descriptor::Archive_descriptor( const std::string & archive_name )
: name( archive_name ), namep( name.empty() ? "(stdin)" : name.c_str() ),
infd( non_tty_infd( archive_name, namep ) ),
lzip_index( infd, true, false ),
lzip_index( infd ),
seekable( lseek( infd, 0, SEEK_SET ) == 0 ),
indexed( seekable && lzip_index.retval() == 0 ) {}

View file

@ -1,5 +1,5 @@
/* Tarlz - Archiver with multimember lzip compression
Copyright (C) 2013-2023 Antonio Diaz Diaz.
Copyright (C) 2013-2024 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

View file

@ -1,5 +1,5 @@
/* Arg_parser - POSIX/GNU command line argument parser. (C++ version)
Copyright (C) 2006-2023 Antonio Diaz Diaz.
/* Arg_parser - POSIX/GNU command-line argument parser. (C++ version)
Copyright (C) 2006-2024 Antonio Diaz Diaz.
This library is free software. Redistribution and use in source and
binary forms, with or without modification, are permitted provided

View file

@ -1,5 +1,5 @@
/* Arg_parser - POSIX/GNU command line argument parser. (C++ version)
Copyright (C) 2006-2023 Antonio Diaz Diaz.
/* Arg_parser - POSIX/GNU command-line argument parser. (C++ version)
Copyright (C) 2006-2024 Antonio Diaz Diaz.
This library is free software. Redistribution and use in source and
binary forms, with or without modification, are permitted provided

View file

@ -1,5 +1,5 @@
/* Tarlz - Archiver with multimember lzip compression
Copyright (C) 2013-2023 Antonio Diaz Diaz.
Copyright (C) 2013-2024 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

View file

@ -1,5 +1,5 @@
/* Tarlz - Archiver with multimember lzip compression
Copyright (C) 2013-2023 Antonio Diaz Diaz.
Copyright (C) 2013-2024 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
@ -227,24 +227,23 @@ bool check_skip_filename( const Cl_options & cl_opts,
}
bool make_path( const std::string & name )
bool make_dirs( const std::string & name )
{
const mode_t mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
unsigned end = name.size(); // first slash before last component
int i = name.size();
while( i > 0 && name[i-1] == '/' ) --i; // remove trailing slashes
while( i > 0 && name[i-1] != '/' ) --i; // remove last component
while( i > 0 && name[i-1] == '/' ) --i; // remove more slashes
const int dirsize = i; // first slash before last component
while( end > 0 && name[end-1] == '/' ) --end; // remove trailing slashes
while( end > 0 && name[end-1] != '/' ) --end; // remove last component
while( end > 0 && name[end-1] == '/' ) --end; // remove more slashes
unsigned index = 0;
while( index < end )
for( i = 0; i < dirsize; ) // if dirsize == 0, dirname is '/' or empty
{
while( index < end && name[index] == '/' ) ++index;
unsigned first = index;
while( index < end && name[index] != '/' ) ++index;
if( first < index )
while( i < dirsize && name[i] == '/' ) ++i;
const int first = i;
while( i < dirsize && name[i] != '/' ) ++i;
if( first < i )
{
const std::string partial( name, 0, index );
const std::string partial( name, 0, i );
const mode_t mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
struct stat st;
if( lstat( partial.c_str(), &st ) == 0 )
{ if( !S_ISDIR( st.st_mode ) ) { errno = ENOTDIR; return false; } }

View file

@ -1,5 +1,5 @@
/* Tarlz - Archiver with multimember lzip compression
Copyright (C) 2013-2023 Antonio Diaz Diaz.
Copyright (C) 2013-2024 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

View file

@ -1,5 +1,5 @@
/* Tarlz - Archiver with multimember lzip compression
Copyright (C) 2013-2023 Antonio Diaz Diaz.
Copyright (C) 2013-2024 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

View file

@ -1,5 +1,5 @@
/* Tarlz - Archiver with multimember lzip compression
Copyright (C) 2013-2023 Antonio Diaz Diaz.
Copyright (C) 2013-2024 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
@ -53,12 +53,11 @@ void cleanup_and_fail( const int retval )
if( delete_output_on_interrupt )
{
delete_output_on_interrupt = false;
if( verbosity >= 0 )
std::fprintf( stderr, "%s: Deleting output file '%s', if it exists.\n",
program_name, output_filename.c_str() );
show_file_error( output_filename.c_str(),
"Deleting output file, if it exists." );
if( outfd >= 0 ) { close( outfd ); outfd = -1; }
if( std::remove( output_filename.c_str() ) != 0 && errno != ENOENT )
show_error( "WARNING: deletion of output file (apparently) failed." );
show_error( "warning: deletion of output file failed", errno );
}
std::exit( retval );
}
@ -103,7 +102,7 @@ void close_and_set_permissions( const struct stat * const in_statsp )
if( in_statsp )
{
const mode_t mode = in_statsp->st_mode;
// fchown will in many cases return with EPERM, which can be safely ignored.
// fchown in many cases returns with EPERM, which can be safely ignored.
if( fchown( outfd, in_statsp->st_uid, in_statsp->st_gid ) == 0 )
{ if( fchmod( outfd, mode ) != 0 ) warning = true; }
else
@ -112,10 +111,8 @@ void close_and_set_permissions( const struct stat * const in_statsp )
warning = true;
}
if( close( outfd ) != 0 )
{
show_error( "Error closing output file", errno );
cleanup_and_fail( 1 );
}
{ show_file_error( output_filename.c_str(), "Error closing output file",
errno ); cleanup_and_fail( 1 ); }
outfd = -1;
delete_output_on_interrupt = false;
if( in_statsp )
@ -126,7 +123,8 @@ void close_and_set_permissions( const struct stat * const in_statsp )
if( utime( output_filename.c_str(), &t ) != 0 ) warning = true;
}
if( warning && verbosity >= 1 )
show_error( "Can't change output file attributes." );
show_file_error( output_filename.c_str(),
"warning: can't change output file attributes", errno );
}
@ -233,6 +231,9 @@ int compress_archive( const Cl_options & cl_opts,
if( to_file && outfd < 0 && ( is_header || is_zero ) )
{
// open outfd after checking infd
if( !make_dirs( output_filename ) )
{ show_file_error( output_filename.c_str(), intdir_msg, errno );
return 1; }
outfd = open_outstream( output_filename, true, 0, false );
// check tty only once and don't try to delete a tty
if( outfd < 0 || !check_tty_out() ) { close( infd ); return 1; }

66
configure vendored
View file

@ -1,12 +1,12 @@
#! /bin/sh
# configure script for Tarlz - Archiver with multimember lzip compression
# Copyright (C) 2013-2023 Antonio Diaz Diaz.
# Copyright (C) 2013-2024 Antonio Diaz Diaz.
#
# This configure script is free software: you have unlimited permission
# to copy, distribute, and modify it.
pkgname=tarlz
pkgversion=0.24
pkgversion=0.25
progname=tarlz
srctrigger=doc/${pkgname}.texi
@ -36,7 +36,7 @@ no_create=
while [ $# != 0 ] ; do
# Get the first arg, and shuffle
option="$1" ; arg2=no
option=$1 ; arg2=no
shift
# Add the argument quoted to args
@ -44,12 +44,12 @@ while [ $# != 0 ] ; do
else args="${args} \"${option}\"" ; fi
# Split out the argument for options that take them
case "${option}" in
*=*) optarg="`echo "${option}" | sed -e 's,^[^=]*=,,;s,/$,,'`" ;;
case ${option} in
*=*) optarg=`echo "${option}" | sed -e 's,^[^=]*=,,;s,/$,,'` ;;
esac
# Process the options
case "${option}" in
case ${option} in
--help | -h)
echo "Usage: $0 [OPTION]... [VAR=VALUE]..."
echo
@ -67,10 +67,10 @@ while [ $# != 0 ] ; do
echo " --infodir=DIR info files directory [${infodir}]"
echo " --mandir=DIR man pages directory [${mandir}]"
echo " CXX=COMPILER C++ compiler to use [${CXX}]"
echo " CPPFLAGS=OPTIONS command line options for the preprocessor [${CPPFLAGS}]"
echo " CXXFLAGS=OPTIONS command line options for the C++ compiler [${CXXFLAGS}]"
echo " CPPFLAGS=OPTIONS command-line options for the preprocessor [${CPPFLAGS}]"
echo " CXXFLAGS=OPTIONS command-line options for the C++ compiler [${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 " MAKEINFO=NAME makeinfo program to use [${MAKEINFO}]"
echo
@ -78,30 +78,30 @@ while [ $# != 0 ] ; do
--version | -V)
echo "Configure script for ${pkgname} version ${pkgversion}"
exit 0 ;;
--srcdir) srcdir="$1" ; arg2=yes ;;
--prefix) prefix="$1" ; arg2=yes ;;
--exec-prefix) exec_prefix="$1" ; arg2=yes ;;
--bindir) bindir="$1" ; arg2=yes ;;
--datarootdir) datarootdir="$1" ; arg2=yes ;;
--infodir) infodir="$1" ; arg2=yes ;;
--mandir) mandir="$1" ; arg2=yes ;;
--srcdir) srcdir=$1 ; arg2=yes ;;
--prefix) prefix=$1 ; arg2=yes ;;
--exec-prefix) exec_prefix=$1 ; arg2=yes ;;
--bindir) bindir=$1 ; arg2=yes ;;
--datarootdir) datarootdir=$1 ; arg2=yes ;;
--infodir) infodir=$1 ; arg2=yes ;;
--mandir) mandir=$1 ; arg2=yes ;;
--srcdir=*) srcdir="${optarg}" ;;
--prefix=*) prefix="${optarg}" ;;
--exec-prefix=*) exec_prefix="${optarg}" ;;
--bindir=*) bindir="${optarg}" ;;
--datarootdir=*) datarootdir="${optarg}" ;;
--infodir=*) infodir="${optarg}" ;;
--mandir=*) mandir="${optarg}" ;;
--no-create) no_create=yes ;;
--srcdir=*) srcdir=${optarg} ;;
--prefix=*) prefix=${optarg} ;;
--exec-prefix=*) exec_prefix=${optarg} ;;
--bindir=*) bindir=${optarg} ;;
--datarootdir=*) datarootdir=${optarg} ;;
--infodir=*) infodir=${optarg} ;;
--mandir=*) mandir=${optarg} ;;
--no-create) no_create=yes ;;
CXX=*) CXX="${optarg}" ;;
CPPFLAGS=*) CPPFLAGS="${optarg}" ;;
CXXFLAGS=*) CXXFLAGS="${optarg}" ;;
CXX=*) CXX=${optarg} ;;
CPPFLAGS=*) CPPFLAGS=${optarg} ;;
CXXFLAGS=*) CXXFLAGS=${optarg} ;;
CXXFLAGS+=*) CXXFLAGS="${CXXFLAGS} ${optarg}" ;;
LDFLAGS=*) LDFLAGS="${optarg}" ;;
LDFLAGS=*) LDFLAGS=${optarg} ;;
LIBS=*) LIBS="${optarg} ${LIBS}" ;;
MAKEINFO=*) MAKEINFO="${optarg}" ;;
MAKEINFO=*) MAKEINFO=${optarg} ;;
--*)
echo "configure: WARNING: unrecognized option: '${option}'" 1>&2 ;;
@ -128,7 +128,7 @@ if [ -z "${srcdir}" ] ; then
if [ ! -r "${srcdir}/${srctrigger}" ] ; then srcdir=.. ; fi
if [ ! -r "${srcdir}/${srctrigger}" ] ; then
## the sed command below emulates the dirname command
srcdir="`echo "$0" | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`"
srcdir=`echo "$0" | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
fi
fi
@ -175,7 +175,7 @@ echo "MAKEINFO = ${MAKEINFO}"
rm -f Makefile
cat > Makefile << EOF
# Makefile for Tarlz - Archiver with multimember lzip compression
# Copyright (C) 2013-2023 Antonio Diaz Diaz.
# Copyright (C) 2013-2024 Antonio Diaz Diaz.
# This file was generated automatically by configure. Don't edit.
#
# This Makefile is free software: you have unlimited permission
@ -201,5 +201,5 @@ EOF
cat "${srcdir}/Makefile.in" >> Makefile
echo "OK. Now you can run make."
echo "If make fails, check that the compression library lzlib is correctly"
echo "installed (see INSTALL)."
echo "If make fails, check that the compression library lzlib is correctly installed"
echo "(see INSTALL)."

View file

@ -1,5 +1,5 @@
/* Tarlz - Archiver with multimember lzip compression
Copyright (C) 2013-2023 Antonio Diaz Diaz.
Copyright (C) 2013-2024 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
@ -26,6 +26,8 @@
#if !defined __FreeBSD__ && !defined __OpenBSD__ && !defined __NetBSD__ && \
!defined __DragonFly__ && !defined __APPLE__ && !defined __OS2__
#include <sys/sysmacros.h> // for major, minor
#else
#include <sys/types.h> // for major, minor
#endif
#include <ftw.h>
#include <grp.h>

View file

@ -1,5 +1,5 @@
/* Tarlz - Archiver with multimember lzip compression
Copyright (C) 2013-2023 Antonio Diaz Diaz.
Copyright (C) 2013-2024 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

View file

@ -1,5 +1,5 @@
/* Tarlz - Archiver with multimember lzip compression
Copyright (C) 2013-2023 Antonio Diaz Diaz.
Copyright (C) 2013-2024 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

View file

@ -1,5 +1,5 @@
/* Tarlz - Archiver with multimember lzip compression
Copyright (C) 2013-2023 Antonio Diaz Diaz.
Copyright (C) 2013-2024 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
@ -29,6 +29,8 @@
#if !defined __FreeBSD__ && !defined __OpenBSD__ && !defined __NetBSD__ && \
!defined __DragonFly__ && !defined __APPLE__ && !defined __OS2__
#include <sys/sysmacros.h> // for major, minor, makedev
#else
#include <sys/types.h> // for major, minor, makedev
#endif
#include <lzlib.h>
@ -38,6 +40,9 @@
#include "archive_reader.h"
#include "decode.h"
#ifndef O_DIRECTORY
#define O_DIRECTORY 0
#endif
namespace {
@ -124,7 +129,7 @@ int extract_member( const Cl_options & cl_opts, Archive_reader & ar,
if( !show_member_name( extended, header, 1, grbuf ) ) return 1;
// remove file (or empty dir) before extraction to prevent following links
std::remove( filename );
if( !make_path( filename ) )
if( !make_dirs( filename ) )
{
show_file_error( filename, intdir_msg, errno );
set_error_status( 1 );
@ -192,7 +197,7 @@ int extract_member( const Cl_options & cl_opts, Archive_reader & ar,
chown( filename, extended.get_uid(), extended.get_gid() ) != 0 ) )
{
if( outfd >= 0 ) mode &= ~( S_ISUID | S_ISGID | S_ISVTX );
// chown will in many cases return with EPERM, which can be safely ignored.
// chown in many cases returns with EPERM, which can be safely ignored.
if( errno != EPERM && errno != EINVAL )
{ show_file_error( filename, chown_msg, errno ); set_error_status( 1 ); }
}
@ -286,7 +291,7 @@ bool compare_file_type( std::string & estr, std::string & ostr,
struct stat st;
bool diff = false, size_differs = false, type_differs = true;
if( hstat( filename, &st, cl_opts.dereference ) != 0 )
format_file_error( estr, filename, "warning: Can't stat", errno );
format_file_error( estr, filename, "warning: can't stat", errno );
else if( ( typeflag == tf_regular || typeflag == tf_hiperf ) &&
!S_ISREG( st.st_mode ) )
format_file_diff( ostr, filename, "Is not a regular file" );
@ -303,14 +308,14 @@ bool compare_file_type( std::string & estr, std::string & ostr,
else
{
type_differs = false;
if( typeflag != tf_symlink )
if( typeflag != tf_symlink && !cl_opts.ignore_metadata )
{
const mode_t mode = parse_octal( header + mode_o, mode_l ); // 12 bits
if( mode != ( st.st_mode & ( S_ISUID | S_ISGID | S_ISVTX |
S_IRWXU | S_IRWXG | S_IRWXO ) ) )
{ format_file_diff( ostr, filename, "Mode differs" ); diff = true; }
}
if( !cl_opts.ignore_ids )
if( !cl_opts.ignore_ids && !cl_opts.ignore_metadata )
{
if( extended.get_uid() != (long long)st.st_uid )
{ format_file_diff( ostr, filename, "Uid differs" ); diff = true; }
@ -319,7 +324,7 @@ bool compare_file_type( std::string & estr, std::string & ostr,
}
if( typeflag != tf_symlink )
{
if( typeflag != tf_directory &&
if( typeflag != tf_directory && !cl_opts.ignore_metadata &&
extended.mtime().sec() != (long long)st.st_mtime )
{
if( (time_t)extended.mtime().sec() == st.st_mtime )

View file

@ -1,5 +1,5 @@
/* Tarlz - Archiver with multimember lzip compression
Copyright (C) 2013-2023 Antonio Diaz Diaz.
Copyright (C) 2013-2024 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
@ -23,7 +23,6 @@ inline bool uid_gid_in_range( const long long uid, const long long gid )
gid == (long long)( (gid_t)gid ); }
const char * const dotdot_msg = "Contains a '..' component, skipping.";
const char * const intdir_msg = "Failed to create intermediate directory";
const char * const cantln_msg = "Can't %slink '%s' to '%s'";
const char * const mkdir_msg = "Can't create directory";
const char * const mknod_msg = "Can't create device node";

View file

@ -1,5 +1,5 @@
/* Tarlz - Archiver with multimember lzip compression
Copyright (C) 2013-2023 Antonio Diaz Diaz.
Copyright (C) 2013-2024 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
@ -29,6 +29,8 @@
#if !defined __FreeBSD__ && !defined __OpenBSD__ && !defined __NetBSD__ && \
!defined __DragonFly__ && !defined __APPLE__ && !defined __OS2__
#include <sys/sysmacros.h> // for major, minor, makedev
#else
#include <sys/types.h> // for major, minor, makedev
#endif
#include <lzlib.h>
@ -258,10 +260,10 @@ Trival skip_member_lz( Archive_reader_i & ar, Packet_courier & courier,
Trival compare_member_lz( const Cl_options & cl_opts,
Archive_reader_i & ar, Packet_courier & courier,
const Extended & extended, const Tar_header header,
Resizable_buffer & rbuf, const long member_id,
const int worker_id )
Archive_reader_i & ar, Packet_courier & courier,
const Extended & extended, const Tar_header header,
Resizable_buffer & rbuf, const long member_id,
const int worker_id )
{
if( verbosity < 1 ) rbuf()[0] = 0;
else if( !format_member_name( extended, header, rbuf, verbosity > 1 ) )
@ -357,7 +359,7 @@ Trival extract_member_lz( const Cl_options & cl_opts,
/* Remove file before extraction to prevent following links.
Don't remove an empty dir because other thread may need it. */
if( typeflag != tf_directory ) std::remove( filename );
if( !make_path( filename ) )
if( !make_dirs( filename ) )
{
if( format_file_error( rbuf, filename, intdir_msg, errno ) &&
!courier.collect_packet( member_id, worker_id, rbuf(), Packet::diag ) )
@ -451,7 +453,7 @@ Trival extract_member_lz( const Cl_options & cl_opts,
chown( filename, extended.get_uid(), extended.get_gid() ) != 0 ) )
{
if( outfd >= 0 ) mode &= ~( S_ISUID | S_ISGID | S_ISVTX );
// chown will in many cases return with EPERM, which can be safely ignored.
// chown in many cases returns with EPERM, which can be safely ignored.
if( errno != EPERM && errno != EINVAL )
{
if( format_file_error( rbuf, filename, chown_msg, errno ) &&

View file

@ -1,5 +1,5 @@
/* Tarlz - Archiver with multimember lzip compression
Copyright (C) 2013-2023 Antonio Diaz Diaz.
Copyright (C) 2013-2024 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

View file

@ -1,5 +1,5 @@
/* Tarlz - Archiver with multimember lzip compression
Copyright (C) 2013-2023 Antonio Diaz Diaz.
Copyright (C) 2013-2024 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

View file

@ -1,5 +1,5 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.2.
.TH TARLZ "1" "September 2023" "tarlz 0.24" "User Commands"
.TH TARLZ "1" "January 2024" "tarlz 0.25" "User Commands"
.SH NAME
tarlz \- creates tar archives with multimember lzip compression
.SH SYNOPSIS
@ -10,12 +10,12 @@ Tarlz is a massively parallel (multi\-threaded) combined implementation of
the tar archiver and the lzip compressor. Tarlz uses the compression library
lzlib.
.PP
Tarlz creates, lists, and extracts archives in a simplified and safer
variant of the POSIX pax format compressed in lzip format, keeping the
alignment between tar members and lzip members. The resulting multimember
tar.lz archive is fully backward compatible with standard tar tools like GNU
tar, which treat it like any other tar.lz archive. Tarlz can append files to
the end of such compressed archives.
Tarlz creates tar archives using a simplified and safer variant of the POSIX
pax format compressed in lzip format, keeping the alignment between tar
members and lzip members. The resulting multimember tar.lz archive is
backward compatible with standard tar tools like GNU tar, which treat it
like any other tar.lz archive. Tarlz can append files to the end of such
compressed archives.
.PP
Keeping the alignment between tar members and lzip members has two
advantages. It adds an indexed lzip layer on top of the tar archive, making
@ -127,6 +127,9 @@ exclude files matching a shell pattern
\fB\-\-ignore\-ids\fR
ignore differences in owner and group IDs
.TP
\fB\-\-ignore\-metadata\fR
compare only file size and file content
.TP
\fB\-\-ignore\-overflow\fR
ignore mtime overflow differences on 32\-bit
.TP
@ -149,7 +152,7 @@ If no archive is specified, tarlz tries to read it from standard input or
write it to standard output.
.PP
Exit status: 0 for a normal exit, 1 for environmental problems
(file not found, files differ, invalid command line options, I/O errors,
(file not found, files differ, invalid command\-line options, I/O errors,
etc), 2 to indicate a corrupt or invalid input file, 3 for an internal
consistency error (e.g., bug) which caused tarlz to panic.
.SH "REPORTING BUGS"
@ -157,8 +160,8 @@ Report bugs to lzip\-bug@nongnu.org
.br
Tarlz home page: http://www.nongnu.org/lzip/tarlz.html
.SH COPYRIGHT
Copyright \(co 2023 Antonio Diaz Diaz.
Using lzlib 1.13
Copyright \(co 2024 Antonio Diaz Diaz.
Using lzlib 1.14\-rc1
License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html>
.br
This is free software: you are free to change and redistribute it.

View file

@ -11,12 +11,12 @@ File: tarlz.info, Node: Top, Next: Introduction, Up: (dir)
Tarlz Manual
************
This manual is for Tarlz (version 0.24, 20 September 2023).
This manual is for Tarlz (version 0.25, 3 January 2024).
* Menu:
* Introduction:: Purpose and features of tarlz
* Invoking tarlz:: Command line interface
* Invoking tarlz:: Command-line interface
* Portable character set:: POSIX portable filename character set
* File format:: Detailed format of the compressed archive
* Amendments to pax format:: The reasons for the differences with pax
@ -28,7 +28,7 @@ This manual is for Tarlz (version 0.24, 20 September 2023).
* Concept index:: Index of concepts
Copyright (C) 2013-2023 Antonio Diaz Diaz.
Copyright (C) 2013-2024 Antonio Diaz Diaz.
This manual is free documentation: you have unlimited permission to copy,
distribute, and modify it.
@ -46,8 +46,8 @@ library lzlib.
Tarlz creates tar archives using a simplified and safer variant of the
POSIX pax format compressed in lzip format, keeping the alignment between
tar members and lzip members. The resulting multimember tar.lz archive is
fully backward compatible with standard tar tools like GNU tar, which treat
it like any other tar.lz archive. Tarlz can append files to the end of such
backward compatible with standard tar tools like GNU tar, which treat it
like any other tar.lz archive. Tarlz can append files to the end of such
compressed archives.
Keeping the alignment between tar members and lzip members has two
@ -433,6 +433,12 @@ to '-1 --solid'.
Make '--diff' ignore differences in owner and group IDs. This option is
useful when comparing an '--anonymous' archive.
'--ignore-metadata'
Make '--diff' ignore any differences in metadata (file permissions,
owner and group IDs, modification time). Compare only file type, file
size, and file content. This option is useful when file permissions
have not been fully restored because uid/gid changed on extraction.
'--ignore-overflow'
Make '--diff' ignore differences in mtime caused by overflow on 32-bit
systems with a 32-bit time_t.
@ -485,7 +491,7 @@ to '-1 --solid'.
Exit status: 0 for a normal exit, 1 for environmental problems (file not
found, files differ, invalid command line options, I/O errors, etc), 2 to
found, files differ, invalid command-line options, I/O errors, etc), 2 to
indicate a corrupt or invalid input file, 3 for an internal consistency
error (e.g., bug) which caused tarlz to panic.
@ -529,7 +535,7 @@ In the diagram below, a box like this:
bytes (for example 512).
A tar.lz file consists of a series of lzip members (compressed data
A tar.lz file consists of one or more lzip members (compressed data
sets). The members simply appear one after another in the file, with no
additional information before, between, or after them.
@ -564,7 +570,7 @@ binary zeros, interpreted as an end-of-archive indicator. These EOA blocks
are either compressed in a separate lzip member or compressed along with the
tar members contained in the last lzip member. For a compressed archive to
be recognized by tarlz as appendable, the last lzip member must contain
between 512 and 32256 zeros alone.
between 512 and 32256 zeros alone (without any non-zero bytes).
The diagram below shows the correspondence between each tar member
(formed by one or two headers plus optional data) in the tar archive and
@ -618,7 +624,7 @@ space, equal-sign, and newline.
'gid'
The unsigned decimal representation of the group ID of the group that
owns the following file. The gid record is created only for files with
a group ID greater than 2_097_151 (octal 7777777). *Note
a group ID greater than 2_097_151 (octal 7_777_777). *Note
ustar-uid-gid::.
'linkpath'
@ -653,13 +659,14 @@ space, equal-sign, and newline.
digits from the ISO/IEC 646:1991 (ASCII) standard. This record
overrides the field 'size' in the following ustar header block. The
size record is created only for files with a size value greater than
8_589_934_591 (octal 77777777777); that is, 8 GiB (2^33 bytes) or
8_589_934_591 (octal 77_777_777_777); that is, 8 GiB (2^33 bytes) or
larger.
'uid'
The unsigned decimal representation of the user ID of the file owner
of the following file. The uid record is created only for files with a
user ID greater than 2_097_151 (octal 7777777). *Note ustar-uid-gid::.
user ID greater than 2_097_151 (octal 7_777_777). *Note
ustar-uid-gid::.
'GNU.crc32'
CRC32-C (Castagnoli) of the extended header data excluding the 8 bytes
@ -737,7 +744,7 @@ S_IROTH 00004 S_IWOTH 00002 S_IXOTH 00001
The fields 'uid' and 'gid' are the user and group IDs of the owner and
group of the file, respectively. If the file uid or gid are greater than
2_097_151 (octal 7777777), an extended record is used to store the uid or
2_097_151 (octal 7_777_777), an extended record is used to store the uid or
gid.
The field 'size' contains the octal representation of the size of the
@ -747,13 +754,13 @@ records following the header is (size / 512) rounded to the next integer.
For all other values of typeflag, tarlz either sets the size field to 0 or
ignores it, and does not store or expect any logical records following the
header. If the file size is larger than 8_589_934_591 bytes
(octal 77777777777), an extended record is used to store the file size.
(octal 77_777_777_777), an extended record is used to store the file size.
The field 'mtime' contains the octal representation of the modification
time of the file at the time it was archived, obtained from the function
'stat'. If the modification time is negative or larger than 8_589_934_591
(octal 77777777777) seconds since the epoch, an extended record is used to
store the modification time. The ustar range of mtime goes from
(octal 77_777_777_777) seconds since the epoch, an extended record is used
to store the modification time. The ustar range of mtime goes from
'1970-01-01 00:00:00 UTC' to '2242-03-16 12:56:31 UTC'.
The field 'chksum' contains the octal representation of the value of the
@ -835,7 +842,7 @@ more probable.
Headers and metadata must be protected separately from data because the
integrity checking of lzip may not be able to detect the corruption before
the metadata has been used, for example, to create a new file in the wrong
the metadata have been used, for example, to create a new file in the wrong
place.
Because of the above, tarlz protects the extended records with a Cyclic
@ -923,7 +930,7 @@ There is no portable way to tell what charset a text string is coded into.
Therefore, tarlz stores all fields representing text strings unmodified,
without conversion to UTF-8 nor any other transformation. This prevents
accidental double UTF-8 conversions. If the need arises this behavior will
be adjusted with a command line option in the future.
be adjusted with a command-line option in the future.

File: tarlz.info, Node: Program design, Next: Multi-threaded decoding, Prev: Amendments to pax format, Up: Top
@ -1252,25 +1259,25 @@ Concept index

Tag Table:
Node: Top216
Node: Introduction1210
Node: Invoking tarlz4041
Ref: --data-size13085
Ref: --bsolid17521
Node: Portable character set23119
Node: File format23762
Ref: key_crc3230703
Ref: ustar-uid-gid33968
Ref: ustar-mtime34770
Node: Amendments to pax format36770
Ref: crc3237479
Ref: flawed-compat38790
Node: Program design42872
Node: Multi-threaded decoding46797
Ref: mt-extraction50078
Node: Minimum archive sizes51384
Node: Examples53511
Node: Problems55878
Node: Concept index56433
Node: Introduction1207
Node: Invoking tarlz4032
Ref: --data-size13076
Ref: --bsolid17512
Node: Portable character set23425
Node: File format24068
Ref: key_crc3231050
Ref: ustar-uid-gid34315
Ref: ustar-mtime35122
Node: Amendments to pax format37125
Ref: crc3237834
Ref: flawed-compat39146
Node: Program design43228
Node: Multi-threaded decoding47153
Ref: mt-extraction50434
Node: Minimum archive sizes51740
Node: Examples53867
Node: Problems56234
Node: Concept index56789

End Tag Table

View file

@ -6,8 +6,8 @@
@finalout
@c %**end of header
@set UPDATED 20 September 2023
@set VERSION 0.24
@set UPDATED 3 January 2024
@set VERSION 0.25
@dircategory Archiving
@direntry
@ -37,7 +37,7 @@ This manual is for Tarlz (version @value{VERSION}, @value{UPDATED}).
@menu
* Introduction:: Purpose and features of tarlz
* Invoking tarlz:: Command line interface
* Invoking tarlz:: Command-line interface
* Portable character set:: POSIX portable filename character set
* File format:: Detailed format of the compressed archive
* Amendments to pax format:: The reasons for the differences with pax
@ -50,7 +50,7 @@ This manual is for Tarlz (version @value{VERSION}, @value{UPDATED}).
@end menu
@sp 1
Copyright @copyright{} 2013-2023 Antonio Diaz Diaz.
Copyright @copyright{} 2013-2024 Antonio Diaz Diaz.
This manual is free documentation: you have unlimited permission to copy,
distribute, and modify it.
@ -68,7 +68,7 @@ compression library @uref{http://www.nongnu.org/lzip/lzlib.html,,lzlib}.
Tarlz creates tar archives using a simplified and safer variant of the POSIX
pax format compressed in lzip format, keeping the alignment between tar
members and lzip members. The resulting multimember tar.lz archive is fully
members and lzip members. The resulting multimember tar.lz archive is
backward compatible with standard tar tools like GNU tar, which treat it
like any other tar.lz archive. Tarlz can append files to the end of such
compressed archives.
@ -477,6 +477,12 @@ Multiple @option{--exclude} options can be specified.
Make @option{--diff} ignore differences in owner and group IDs. This option is
useful when comparing an @option{--anonymous} archive.
@item --ignore-metadata
Make @option{--diff} ignore any differences in metadata (file permissions,
owner and group IDs, modification time). Compare only file type, file size,
and file content. This option is useful when file permissions have not been
fully restored because uid/gid changed on extraction.
@item --ignore-overflow
Make @option{--diff} ignore differences in mtime caused by overflow on 32-bit
systems with a 32-bit time_t.
@ -534,7 +540,7 @@ keyword appearing in the same block of extended records.
@end table
Exit status: 0 for a normal exit, 1 for environmental problems
(file not found, files differ, invalid command line options, I/O errors,
(file not found, files differ, invalid command-line options, I/O errors,
etc), 2 to indicate a corrupt or invalid input file, 3 for an internal
consistency error (e.g., bug) which caused tarlz to panic.
@ -582,7 +588,7 @@ represents a variable number of bytes or a fixed but large number of
bytes (for example 512).
@sp 1
A tar.lz file consists of a series of lzip members (compressed data sets).
A tar.lz file consists of one or more lzip members (compressed data sets).
The members simply appear one after another in the file, with no additional
information before, between, or after them.
@ -622,7 +628,7 @@ binary zeros, interpreted as an end-of-archive indicator. These EOA blocks
are either compressed in a separate lzip member or compressed along with the
tar members contained in the last lzip member. For a compressed archive to
be recognized by tarlz as appendable, the last lzip member must contain
between 512 and 32256 zeros alone.
between 512 and 32256 zeros alone (without any non-zero bytes).
The diagram below shows the correspondence between each tar member (formed
by one or two headers plus optional data) in the tar archive and each
@ -694,7 +700,7 @@ time outside of the ustar range. @xref{ustar-mtime}.
@item gid
The unsigned decimal representation of the group ID of the group that owns
the following file. The gid record is created only for files with a group ID
greater than 2_097_151 (octal 7777777). @xref{ustar-uid-gid}.
greater than 2_097_151 @w{(octal 7_777_777)}. @xref{ustar-uid-gid}.
@item linkpath
The file name of a link being created to another file, of any type,
@ -726,12 +732,12 @@ The size of the file in bytes, expressed as a decimal number using digits
from the ISO/IEC 646:1991 (ASCII) standard. This record overrides the field
@samp{size} in the following ustar header block. The size record is created
only for files with a size value greater than 8_589_934_591
@w{(octal 77777777777)}; that is, @w{8 GiB} (2^33 bytes) or larger.
@w{(octal 77_777_777_777)}; that is, @w{8 GiB} (2^33 bytes) or larger.
@item uid
The unsigned decimal representation of the user ID of the file owner of the
following file. The uid record is created only for files with a user ID
greater than 2_097_151 (octal 7777777). @xref{ustar-uid-gid}.
greater than 2_097_151 @w{(octal 7_777_777)}. @xref{ustar-uid-gid}.
@anchor{key_crc32}
@item GNU.crc32
@ -815,7 +821,8 @@ table shows the symbolic name of each bit and its octal value:
@anchor{ustar-uid-gid}
The fields @samp{uid} and @samp{gid} are the user and group IDs of the owner
and group of the file, respectively. If the file uid or gid are greater than
2_097_151 (octal 7777777), an extended record is used to store the uid or gid.
2_097_151 @w{(octal 7_777_777)}, an extended record is used to store the uid
or gid.
The field @samp{size} contains the octal representation of the size of the
file in bytes. If the field @samp{typeflag} specifies a file of type '0'
@ -824,13 +831,13 @@ records following the header is @w{(size / 512)} rounded to the next
integer. For all other values of typeflag, tarlz either sets the size field
to 0 or ignores it, and does not store or expect any logical records
following the header. If the file size is larger than 8_589_934_591 bytes
@w{(octal 77777777777)}, an extended record is used to store the file size.
@w{(octal 77_777_777_777)}, an extended record is used to store the file size.
@anchor{ustar-mtime}
The field @samp{mtime} contains the octal representation of the modification
time of the file at the time it was archived, obtained from the function
@samp{stat}. If the modification time is negative or larger than
8_589_934_591 @w{(octal 77777777777)} seconds since the epoch, an extended
8_589_934_591 @w{(octal 77_777_777_777)} seconds since the epoch, an extended
record is used to store the modification time. The ustar range of mtime goes
from @w{@samp{1970-01-01 00:00:00 UTC}} to @w{@samp{2242-03-16 12:56:31 UTC}}.
@ -914,7 +921,7 @@ large, making undetected corruption and archiver misbehavior more probable.
Headers and metadata must be protected separately from data because the
integrity checking of lzip may not be able to detect the corruption before
the metadata has been used, for example, to create a new file in the wrong
the metadata have been used, for example, to create a new file in the wrong
place.
Because of the above, tarlz protects the extended records with a Cyclic
@ -999,7 +1006,7 @@ There is no portable way to tell what charset a text string is coded into.
Therefore, tarlz stores all fields representing text strings unmodified,
without conversion to UTF-8 nor any other transformation. This prevents
accidental double UTF-8 conversions. If the need arises this behavior will
be adjusted with a command line option in the future.
be adjusted with a command-line option in the future.
@node Program design

View file

@ -1,5 +1,5 @@
/* Tarlz - Archiver with multimember lzip compression
Copyright (C) 2013-2023 Antonio Diaz Diaz.
Copyright (C) 2013-2024 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

View file

@ -1,5 +1,5 @@
/* Tarlz - Archiver with multimember lzip compression
Copyright (C) 2013-2023 Antonio Diaz Diaz.
Copyright (C) 2013-2024 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
@ -354,7 +354,7 @@ bool Extended::parse( const char * const buf, const int edsize,
if( stored_crc != computed_crc )
{
if( verbosity >= 2 )
std::fprintf( stderr, "CRC32C = %08X\n", (unsigned)computed_crc );
std::fprintf( stderr, "CRC32-C = %08X\n", (unsigned)computed_crc );
return false;
}
}

View file

@ -1,5 +1,5 @@
/* Tarlz - Archiver with multimember lzip compression
Copyright (C) 2013-2023 Antonio Diaz Diaz.
Copyright (C) 2013-2024 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
@ -47,17 +47,16 @@ const char * bad_version( const unsigned version )
} // end namespace
bool Lzip_index::check_header_error( const Lzip_header & header,
const bool first )
bool Lzip_index::check_header( const Lzip_header & header, const bool first )
{
if( !header.check_magic() )
{ error_ = bad_magic_msg; retval_ = 2; if( first ) bad_magic_ = true;
return true; }
return false; }
if( !header.check_version() )
{ error_ = bad_version( header.version() ); retval_ = 2; return true; }
{ error_ = bad_version( header.version() ); retval_ = 2; return false; }
if( !isvalid_ds( header.dictionary_size() ) )
{ error_ = bad_dict_msg; retval_ = 2; return true; }
return false;
{ error_ = bad_dict_msg; retval_ = 2; return false; }
return true;
}
void Lzip_index::set_errno_error( const char * const msg )
@ -78,16 +77,14 @@ void Lzip_index::set_num_error( const char * const msg, unsigned long long num )
bool Lzip_index::read_header( const int fd, Lzip_header & header,
const long long pos )
{
if( seek_read( fd, header.data, Lzip_header::size, pos ) != Lzip_header::size )
if( seek_read( fd, header.data, header.size, pos ) != header.size )
{ set_errno_error( "Error reading member header: " ); return false; }
return true;
}
// If successful, push last member and set pos to member header.
bool Lzip_index::skip_trailing_data( const int fd, unsigned long long & pos,
const bool ignore_trailing,
const bool loose_trailing )
bool Lzip_index::skip_trailing_data( const int fd, unsigned long long & pos )
{
if( pos < min_member_size ) return false;
enum { block_size = 16384,
@ -108,34 +105,31 @@ bool Lzip_index::skip_trailing_data( const int fd, unsigned long long & pos,
if( buffer[i-1] <= max_msb ) // most significant byte of member_size
{
const Lzip_trailer & trailer =
*(const Lzip_trailer *)( buffer + i - Lzip_trailer::size );
*(const Lzip_trailer *)( buffer + i - trailer.size );
const unsigned long long member_size = trailer.member_size();
if( member_size == 0 ) // skip trailing zeros
{ while( i > Lzip_trailer::size && buffer[i-9] == 0 ) --i; continue; }
if( member_size > ipos + i || !trailer.check_consistency() )
continue;
{ while( i > trailer.size && buffer[i-9] == 0 ) --i; continue; }
if( member_size > ipos + i || !trailer.check_consistency() ) continue;
Lzip_header header;
if( !read_header( fd, header, ipos + i - member_size ) ) return false;
if( !header.check() ) continue;
const Lzip_header & header2 = *(const Lzip_header *)( buffer + i );
const bool full_h2 = bsize - i >= Lzip_header::size;
const bool full_h2 = bsize - i >= header.size;
if( header2.check_prefix( bsize - i ) ) // last member
{
if( !full_h2 ) error_ = "Last member in input file is truncated.";
else if( !check_header_error( header2, false ) )
else if( check_header( header2, false ) )
error_ = "Last member in input file is truncated or corrupt.";
retval_ = 2; return false;
}
if( !loose_trailing && full_h2 && header2.check_corrupt() )
if( full_h2 && header2.check_corrupt() )
{ error_ = corrupt_mm_msg; retval_ = 2; return false; }
if( !ignore_trailing )
{ error_ = trailing_msg; retval_ = 2; return false; }
pos = ipos + i - member_size;
pos = ipos + i - member_size; // good member
const unsigned dictionary_size = header.dictionary_size();
member_vector.push_back( Member( 0, trailer.data_size(), pos,
member_size, dictionary_size ) );
if( dictionary_size_ < dictionary_size )
dictionary_size_ = dictionary_size;
member_vector.push_back( Member( 0, trailer.data_size(), pos,
member_size, dictionary_size ) );
return true;
}
if( ipos == 0 )
@ -150,8 +144,7 @@ bool Lzip_index::skip_trailing_data( const int fd, unsigned long long & pos,
}
Lzip_index::Lzip_index( const int infd, const bool ignore_trailing,
const bool loose_trailing )
Lzip_index::Lzip_index( const int infd )
: insize( lseek( infd, 0, SEEK_END ) ), retval_( 0 ), dictionary_size_( 0 ),
bad_magic_( false )
{
@ -164,42 +157,38 @@ Lzip_index::Lzip_index( const int infd, const bool ignore_trailing,
retval_ = 2; return; }
Lzip_header header;
if( !read_header( infd, header, 0 ) ) return;
if( check_header_error( header, true ) ) return;
if( !read_header( infd, header, 0 ) ||
!check_header( header, true ) ) return;
unsigned long long pos = insize; // always points to a header or to EOF
while( pos >= min_member_size )
{
Lzip_trailer trailer;
if( seek_read( infd, trailer.data, Lzip_trailer::size,
pos - Lzip_trailer::size ) != Lzip_trailer::size )
if( seek_read( infd, trailer.data, trailer.size, pos - trailer.size ) !=
trailer.size )
{ set_errno_error( "Error reading member trailer: " ); break; }
const unsigned long long member_size = trailer.member_size();
if( member_size > pos || !trailer.check_consistency() ) // bad trailer
{
if( member_vector.empty() )
{ if( skip_trailing_data( infd, pos, ignore_trailing, loose_trailing ) )
continue; else return; }
set_num_error( "Bad trailer at pos ", pos - Lzip_trailer::size );
break;
{ if( skip_trailing_data( infd, pos ) ) continue; return; }
set_num_error( "Bad trailer at pos ", pos - trailer.size ); break;
}
if( !read_header( infd, header, pos - member_size ) ) break;
if( !header.check() ) // bad header
{
if( member_vector.empty() )
{ if( skip_trailing_data( infd, pos, ignore_trailing, loose_trailing ) )
continue; else return; }
set_num_error( "Bad header at pos ", pos - member_size );
break;
{ if( skip_trailing_data( infd, pos ) ) continue; return; }
set_num_error( "Bad header at pos ", pos - member_size ); break;
}
pos -= member_size;
pos -= member_size; // good member
const unsigned dictionary_size = header.dictionary_size();
member_vector.push_back( Member( 0, trailer.data_size(), pos,
member_size, dictionary_size ) );
if( dictionary_size_ < dictionary_size )
dictionary_size_ = dictionary_size;
member_vector.push_back( Member( 0, trailer.data_size(), pos,
member_size, dictionary_size ) );
}
if( pos != 0 || member_vector.empty() )
if( pos != 0 || member_vector.empty() || retval_ != 0 )
{
member_vector.clear();
if( retval_ == 0 ) { error_ = "Can't create file index."; retval_ = 2; }

View file

@ -1,5 +1,5 @@
/* Tarlz - Archiver with multimember lzip compression
Copyright (C) 2013-2023 Antonio Diaz Diaz.
Copyright (C) 2013-2024 Antonio Diaz Diaz.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -22,7 +22,7 @@
class Block
{
long long pos_, size_; // pos + size <= INT64_MAX
long long pos_, size_; // pos >= 0, size >= 0, pos + size <= INT64_MAX
public:
Block( const long long p, const long long s ) : pos_( p ), size_( s ) {}
@ -43,9 +43,11 @@ class Lzip_index
Block dblock, mblock; // data block, member block
unsigned dictionary_size;
Member( const long long dp, const long long ds,
const long long mp, const long long ms, const unsigned dict_size )
: dblock( dp, ds ), mblock( mp, ms ), dictionary_size( dict_size ) {}
Member( const long long dpos, const long long dsize,
const long long mpos, const long long msize,
const unsigned dict_size )
: dblock( dpos, dsize ), mblock( mpos, msize ),
dictionary_size( dict_size ) {}
};
std::vector< Member > member_vector;
@ -55,16 +57,14 @@ class Lzip_index
unsigned dictionary_size_; // largest dictionary size in the file
bool bad_magic_; // bad magic in first header
bool check_header_error( const Lzip_header & header, const bool first );
bool check_header( const Lzip_header & header, const bool first );
void set_errno_error( const char * const msg );
void set_num_error( const char * const msg, unsigned long long num );
bool read_header( const int fd, Lzip_header & header, const long long pos );
bool skip_trailing_data( const int fd, unsigned long long & pos,
const bool ignore_trailing, const bool loose_trailing );
bool skip_trailing_data( const int fd, unsigned long long & pos );
public:
Lzip_index( const int infd, const bool ignore_trailing,
const bool loose_trailing );
Lzip_index( const int infd );
long members() const { return member_vector.size(); }
const std::string & error() const { return error_; }

57
main.cc
View file

@ -1,5 +1,5 @@
/* Tarlz - Archiver with multimember lzip compression
Copyright (C) 2013-2023 Antonio Diaz Diaz.
Copyright (C) 2013-2024 Antonio Diaz Diaz.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -16,7 +16,7 @@
*/
/*
Exit status: 0 for a normal exit, 1 for environmental problems
(file not found, files differ, invalid command line options, I/O errors,
(file not found, files differ, invalid command-line options, I/O errors,
etc), 2 to indicate a corrupt or invalid input file, 3 for an internal
consistency error (e.g., bug) which caused tarlz to panic.
*/
@ -56,7 +56,7 @@ const char * const program_name = "tarlz";
namespace {
const char * const program_year = "2023";
const char * const program_year = "2024";
const char * invocation_name = program_name; // default value
@ -65,12 +65,12 @@ void show_help( const long num_online )
std::printf( "Tarlz is a massively parallel (multi-threaded) combined implementation of\n"
"the tar archiver and the lzip compressor. Tarlz uses the compression library\n"
"lzlib.\n"
"\nTarlz creates, lists, and extracts archives in a simplified and safer\n"
"variant of the POSIX pax format compressed in lzip format, keeping the\n"
"alignment between tar members and lzip members. The resulting multimember\n"
"tar.lz archive is fully backward compatible with standard tar tools like GNU\n"
"tar, which treat it like any other tar.lz archive. Tarlz can append files to\n"
"the end of such compressed archives.\n"
"\nTarlz creates tar archives using a simplified and safer variant of the POSIX\n"
"pax format compressed in lzip format, keeping the alignment between tar\n"
"members and lzip members. The resulting multimember tar.lz archive is\n"
"backward compatible with standard tar tools like GNU tar, which treat it\n"
"like any other tar.lz archive. Tarlz can append files to the end of such\n"
"compressed archives.\n"
"\nKeeping the alignment between tar members and lzip members has two\n"
"advantages. It adds an indexed lzip layer on top of the tar archive, making\n"
"it possible to decode the archive safely in parallel. It also minimizes the\n"
@ -116,6 +116,7 @@ void show_help( const long num_online )
" --group=<group> use <group> name/ID for files added to archive\n"
" --exclude=<pattern> exclude files matching a shell pattern\n"
" --ignore-ids ignore differences in owner and group IDs\n"
" --ignore-metadata compare only file size and file content\n"
" --ignore-overflow ignore mtime overflow differences on 32-bit\n"
" --keep-damaged don't delete partially extracted files\n"
" --missing-crc exit with error status if missing extended CRC\n"
@ -131,7 +132,7 @@ void show_help( const long num_online )
std::printf( "\nIf no archive is specified, tarlz tries to read it from standard input or\n"
"write it to standard output.\n"
"\nExit status: 0 for a normal exit, 1 for environmental problems\n"
"(file not found, files differ, invalid command line options, I/O errors,\n"
"(file not found, files differ, invalid command-line options, I/O errors,\n"
"etc), 2 to indicate a corrupt or invalid input file, 3 for an internal\n"
"consistency error (e.g., bug) which caused tarlz to panic.\n"
"\nReport bugs to lzip-bug@nongnu.org\n"
@ -211,9 +212,9 @@ int check_lib()
// separate numbers of 5 or more digits in groups of 3 digits using '_'
const char * format_num3( long long num )
{
enum { buffers = 8, bufsize = 4 * sizeof num, n = 10 };
const char * const si_prefix = "kMGTPEZYRQ";
const char * const binary_prefix = "KMGTPEZYRQ";
enum { buffers = 8, bufsize = 4 * sizeof num };
static char buffer[buffers][bufsize]; // circle of static buffers for printf
static int current = 0;
@ -221,14 +222,17 @@ const char * format_num3( long long num )
char * p = buf + bufsize - 1; // fill the buffer backwards
*p = 0; // terminator
const bool negative = num < 0;
char prefix = 0; // try binary first, then si
for( int i = 0; i < 8 && num != 0 && ( num / 1024 ) * 1024 == num; ++i )
{ num /= 1024; prefix = binary_prefix[i]; }
if( prefix ) *(--p) = 'i';
else
for( int i = 0; i < 8 && num != 0 && ( num / 1000 ) * 1000 == num; ++i )
{ num /= 1000; prefix = si_prefix[i]; }
if( prefix ) *(--p) = prefix;
if( num > 1024 || num < -1024 )
{
char prefix = 0; // try binary first, then si
for( int i = 0; i < n && num != 0 && num % 1024 == 0; ++i )
{ num /= 1024; prefix = binary_prefix[i]; }
if( prefix ) *(--p) = 'i';
else
for( int i = 0; i < n && num != 0 && num % 1000 == 0; ++i )
{ num /= 1000; prefix = si_prefix[i]; }
if( prefix ) *(--p) = prefix;
}
const bool split = num >= 10000 || num <= -10000;
for( int i = 0; ; )
@ -251,8 +255,7 @@ void show_option_error( const char * const arg, const char * const msg,
}
// Recognized formats: <num>[QRYZEPTGM][i], <num>k, <num>Ki
//
// Recognized formats: <num>k, <num>Ki, <num>[MGTPEZYRQ][i]
long long getnum( const char * const arg, const char * const option_name,
const long long llimit = LLONG_MIN,
const long long ulimit = LLONG_MAX )
@ -524,8 +527,8 @@ int main( const int argc, const char * const argv[] )
if( argc > 0 ) invocation_name = argv[0];
enum { opt_ano = 256, opt_aso, opt_bso, opt_chk, opt_crc, opt_dbg, opt_del,
opt_dso, opt_exc, opt_grp, opt_hlp, opt_id, opt_kd, opt_mti, opt_nso,
opt_ofl, opt_out, opt_own, opt_per, opt_sol, opt_un, opt_wn };
opt_dso, opt_exc, opt_grp, opt_hlp, opt_iid, opt_imd, opt_kd, opt_mti,
opt_nso, opt_ofl, opt_out, opt_own, opt_per, opt_sol, opt_un, opt_wn };
const Arg_parser::Option options[] =
{
{ '0', 0, Arg_parser::no },
@ -566,7 +569,8 @@ int main( const int argc, const char * const argv[] )
{ opt_exc, "exclude", Arg_parser::yes },
{ opt_grp, "group", Arg_parser::yes },
{ opt_hlp, "help", Arg_parser::no },
{ opt_id, "ignore-ids", Arg_parser::no },
{ opt_iid, "ignore-ids", Arg_parser::no },
{ opt_imd, "ignore-metadata", Arg_parser::no },
{ opt_kd, "keep-damaged", Arg_parser::no },
{ opt_crc, "missing-crc", Arg_parser::no },
{ opt_mti, "mtime", Arg_parser::yes },
@ -642,7 +646,8 @@ int main( const int argc, const char * const argv[] )
case opt_exc: Exclude::add_pattern( sarg ); break;
case opt_grp: cl_opts.gid = parse_group( arg, pn ); break;
case opt_hlp: show_help( num_online ); return 0;
case opt_id: cl_opts.ignore_ids = true; break;
case opt_iid: cl_opts.ignore_ids = true; break;
case opt_imd: cl_opts.ignore_metadata = true; break;
case opt_kd: cl_opts.keep_damaged = true; break;
case opt_mti: cl_opts.mtime = parse_mtime( arg, pn );
cl_opts.mtime_set = true; break;
@ -654,7 +659,7 @@ int main( const int argc, const char * const argv[] )
case opt_sol: cl_opts.solidity = solid; break;
case opt_un: cl_opts.set_level( -1 ); break;
case opt_wn: cl_opts.warn_newer = true; break;
default : internal_error( "uncaught option" );
default: internal_error( "uncaught option." );
}
} // end process options

17
tarlz.h
View file

@ -1,5 +1,5 @@
/* Tarlz - Archiver with multimember lzip compression
Copyright (C) 2013-2023 Antonio Diaz Diaz.
Copyright (C) 2013-2024 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
@ -419,7 +419,7 @@ enum Program_mode { m_none, m_append, m_compress, m_concatenate, m_create,
enum Solidity { no_solid, bsolid, dsolid, asolid, solid };
class Arg_parser;
struct Cl_options // command line options
struct Cl_options // command-line options
{
const Arg_parser & parser;
std::string archive_name;
@ -438,6 +438,7 @@ struct Cl_options // command line options
bool dereference;
bool filenames_given;
bool ignore_ids;
bool ignore_metadata;
bool ignore_overflow;
bool keep_damaged;
bool level_set; // compression level set in command line
@ -451,10 +452,10 @@ struct Cl_options // command line options
: parser( ap ), mtime( 0 ), uid( -1 ), gid( -1 ), program_mode( m_none ),
solidity( bsolid ), data_size( 0 ), debug_level( 0 ), level( 6 ),
num_files( 0 ), num_workers( -1 ), out_slots( 64 ), dereference( false ),
filenames_given( false ), ignore_ids( false ), ignore_overflow( false ),
keep_damaged( false ), level_set( false ), missing_crc( false ),
mtime_set( false ), permissive( false ), preserve_permissions( false ),
warn_newer( false ) {}
filenames_given( false ), ignore_ids( false ), ignore_metadata( false ),
ignore_overflow( false ), keep_damaged( false ), level_set( false ),
missing_crc( false ), mtime_set( false ), permissive( false ),
preserve_permissions( false ), warn_newer( false ) {}
void set_level( const int l ) { level = l; level_set = true; }
@ -469,7 +470,6 @@ inline void set_retval( int & retval, const int new_val )
const char * const bad_magic_msg = "Bad magic number (file not in lzip format).";
const char * const bad_dict_msg = "Invalid dictionary size in member header.";
const char * const corrupt_mm_msg = "Corrupt header in multimember file.";
const char * const trailing_msg = "Trailing data not allowed.";
const char * const bad_hdr_msg = "Corrupt or invalid tar header.";
const char * const gblrec_msg = "Error in global extended records.";
const char * const extrec_msg = "Error in extended records.";
@ -490,6 +490,7 @@ const char * const nfound_msg = "Not found in archive.";
const char * const seek_msg = "Seek error";
const char * const werr_msg = "Write error";
const char * const chdir_msg = "Error changing working directory";
const char * const intdir_msg = "Failed to create intermediate directory";
// defined in common.cc
unsigned long long parse_octal( const uint8_t * const ptr, const int size );
@ -505,7 +506,7 @@ bool show_member_name( const Extended & extended, const Tar_header header,
bool check_skip_filename( const Cl_options & cl_opts,
std::vector< char > & name_pending,
const char * const filename, const int chdir_fd = -1 );
bool make_path( const std::string & name );
bool make_dirs( const std::string & name );
// defined in common_mutex.cc
void exit_fail_mt( const int retval = 1 ); // terminate the program

View file

@ -1,14 +1,14 @@
#! /bin/sh
# check script for Tarlz - Archiver with multimember lzip compression
# Copyright (C) 2013-2023 Antonio Diaz Diaz.
# Copyright (C) 2013-2024 Antonio Diaz Diaz.
#
# This script is free software: you have unlimited permission
# to copy, distribute, and modify it.
LC_ALL=C
export LC_ALL
objdir="`pwd`"
testdir="`cd "$1" ; pwd`"
objdir=`pwd`
testdir=`cd "$1" ; pwd`
TARLZ="${objdir}"/tarlz
framework_failure() { echo "failure in testing framework" ; exit 1 ; }
@ -154,13 +154,13 @@ printf "testing tarlz-%s..." "$2"
[ $? = 1 ] || test_failed $LINENO
"${TARLZ}" -q -x -C nx_dir "${test3_lz}"
[ $? = 1 ] || test_failed $LINENO
touch empty.tar.lz empty.tlz # list an empty lz file
touch empty.tar.lz empty.tlz || framework_failure # list an empty lz file
"${TARLZ}" -q -tf empty.tar.lz
[ $? = 2 ] || test_failed $LINENO
"${TARLZ}" -q -tf empty.tlz
[ $? = 2 ] || test_failed $LINENO
rm -f empty.tar.lz empty.tlz || framework_failure
touch empty.tar # compress an empty archive
touch empty.tar || framework_failure # compress an empty archive
"${TARLZ}" -q -z empty.tar
[ $? = 2 ] || test_failed $LINENO
[ ! -e empty.tar.lz ] || test_failed $LINENO
@ -245,6 +245,7 @@ rm -f foo bar baz || framework_failure
cmp cfoo foo || test_failed $LINENO
cmp cbar bar || test_failed $LINENO
cmp cbaz baz || test_failed $LINENO
# time and mode comparison always fails on OS/2
if "${TARLZ}" -df "${test3}" --ignore-ids ; then d_works=yes
else printf "warning: some '--diff' tests will be skipped.\n"
fi
@ -290,10 +291,12 @@ for i in "${test3}" "${test3_lz}" ; do
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"
if [ "${d_works}" = yes ] ; then
"${TARLZ}" -df "$i" -C dir1 foo -C ../dir2 --ignore-ids bar \
-C ../dir3 baz || test_failed $LINENO "$i"
"${TARLZ}" -df "$i" -C dir3 baz -C ../dir2 bar -C ../dir1 foo \
--ignore-ids || test_failed $LINENO "$i"
fi
rm -rf dir1 dir2 dir3 || framework_failure
done
for i in "${test3dir}" "${test3dir_lz}" ; do
@ -303,10 +306,12 @@ for i in "${test3dir}" "${test3dir_lz}" ; do
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"
if [ "${d_works}" = yes ] ; then
"${TARLZ}" -q -df "$i" --ignore-ids -C dir1 dir/foo -C ../dir2 dir/bar \
-C ../dir3 dir/baz || test_failed $LINENO "$i"
"${TARLZ}" -q -df "${test3}" -C dir1/dir foo -C ../../dir2/dir bar \
--ignore-ids -C ../../dir3/dir baz || test_failed $LINENO "$i"
fi
rm -rf dir1 dir2 dir3 || framework_failure
done
@ -768,7 +773,6 @@ rm -rf out.tar dir || framework_failure
printf "\ntesting --diff..."
# test --diff
"${TARLZ}" -xf "${test3_lz}" || test_failed $LINENO
"${TARLZ}" -cf out.tar foo || test_failed $LINENO
"${TARLZ}" -cf aout.tar foo --anonymous || test_failed $LINENO
@ -787,6 +791,8 @@ else
rm -f bar || framework_failure
"${TARLZ}" -n$i -df "${test3_lz}" --ignore-ids foo baz ||
test_failed $LINENO $i
"${TARLZ}" -n$i -df "${test3_lz}" --ignore-metadata foo baz ||
test_failed $LINENO $i
"${TARLZ}" -n$i -df "${test3_lz}" --exclude bar --ignore-ids ||
test_failed $LINENO $i
rm -f foo baz || framework_failure
@ -1199,6 +1205,9 @@ cmp out.tar.lz out || test_failed $LINENO
"${TARLZ}" -0 -B8KiB -z --bsolid outz.tar || test_failed $LINENO
cmp out.tar.lz outz.tar.lz || test_failed $LINENO
rm -f out outz.tar.lz || framework_failure
"${TARLZ}" -0 -B8KiB -z -o a/b/c/out --bsolid out.tar || test_failed $LINENO
cmp out.tar.lz a/b/c/out || test_failed $LINENO
rm -rf a || framework_failure
#
"${TARLZ}" -0 -n0 --asolid -cf out.tar.lz test.txt foo bar baz test.txt || test_failed $LINENO
"${TARLZ}" -0 -n0 --asolid -cf out3.tar.lz foo bar baz || test_failed $LINENO