summaryrefslogtreecommitdiffstats
path: root/create.cc
diff options
context:
space:
mode:
Diffstat (limited to 'create.cc')
-rw-r--r--create.cc182
1 files changed, 67 insertions, 115 deletions
diff --git a/create.cc b/create.cc
index 53ba5f5..5878dd3 100644
--- a/create.cc
+++ b/create.cc
@@ -1,5 +1,5 @@
/* Tarlz - Archiver with multimember lzip compression
- Copyright (C) 2013-2022 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
@@ -20,14 +20,14 @@
#include <algorithm>
#include <cerrno>
#include <cstdio>
-#include <cstdlib>
-#include <pthread.h>
#include <stdint.h> // for lzlib.h
#include <unistd.h>
#include <sys/stat.h>
#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>
@@ -50,7 +50,6 @@ const char * archive_namep = 0;
unsigned long long partial_data_size = 0; // size of current block
Resizable_buffer grbuf; // extended header + data
int goutfd = -1;
-int error_status = 0;
bool option_C_after_relative_filename( const Arg_parser & parser )
@@ -78,7 +77,7 @@ long long check_compressed_appendable( const int fd, const bool remove_eoa )
if( rd == 0 && errno == 0 ) return 0; // append to empty archive
if( rd < min_member_size || ( rd != bufsize && errno ) ) return -1;
const Lzip_header * const p = (const Lzip_header *)buf; // shut up gcc
- if( !p->verify_magic() || !p->verify_version() ) return -1;
+ if( !p->check_magic() || !p->check_version() ) return -1;
LZ_Decoder * decoder = LZ_decompress_open(); // decompress first header
if( !decoder || LZ_decompress_errno( decoder ) != LZ_ok ||
LZ_decompress_write( decoder, buf, rd ) != rd ||
@@ -86,7 +85,7 @@ long long check_compressed_appendable( const int fd, const bool remove_eoa )
{ LZ_decompress_close( decoder ); return -1; }
LZ_decompress_close( decoder );
const bool maybe_eoa = block_is_zero( buf, header_size );
- if( !verify_ustar_chksum( buf ) && !maybe_eoa ) return -1;
+ if( !check_ustar_chksum( buf ) && !maybe_eoa ) return -1;
const long long end = lseek( fd, 0, SEEK_END );
if( end < min_member_size ) return -1;
@@ -100,7 +99,7 @@ long long check_compressed_appendable( const int fd, const bool remove_eoa )
Lzip_header header; // read last header
if( seek_read( fd, header.data, Lzip_header::size,
end - member_size ) != Lzip_header::size ) return -1;
- if( !header.verify_magic() || !header.verify_version() ||
+ if( !header.check_magic() || !header.check_version() ||
!isvalid_ds( header.dictionary_size() ) ) return -1;
// EOA marker in last member must contain between 512 and 32256 zeros alone
@@ -142,7 +141,7 @@ long long check_uncompressed_appendable( const int fd, const bool remove_eoa )
const int rd = readblock( fd, header, header_size );
if( rd == 0 && errno == 0 ) break; // missing EOA blocks
if( rd != header_size ) return -1;
- if( !verify_ustar_chksum( header ) ) // maybe EOA block
+ if( !check_ustar_chksum( header ) ) // maybe EOA block
{ if( block_is_zero( header, header_size ) ) break; else return -1; }
const Typeflag typeflag = (Typeflag)header[typeflag_o];
if( typeflag == tf_extended || typeflag == tf_global )
@@ -150,7 +149,7 @@ long long check_uncompressed_appendable( const int fd, const bool remove_eoa )
if( prev_extended ) return -1;
const long long edsize = parse_octal( header + size_o, size_l );
const long long bufsize = round_up( edsize );
- if( edsize <= 0 || edsize >= 1LL << 33 || bufsize >= INT_MAX )
+ if( edsize <= 0 || edsize >= 1LL << 33 || bufsize > max_edata_size )
return -1; // overflow or no extended data
if( !rbuf.resize( bufsize ) ) return -2;
if( readblock( fd, rbuf.u8(), bufsize ) != bufsize )
@@ -204,21 +203,6 @@ bool archive_write( const uint8_t * const buf, const int size )
}
-bool write_extended( const Extended & extended )
- {
- const long long ebsize = extended.format_block( grbuf ); // may be 0
- if( ebsize < 0 )
- { show_error( ( ebsize == -2 ) ? mem_msg2 : eferec_msg ); return false; }
- for( long long pos = 0; pos < ebsize; ) // write extended block to archive
- {
- int size = std::min( ebsize - pos, 1LL << 20 );
- if( !archive_write( grbuf.u8() + pos, size ) ) return false;
- pos += size;
- }
- return true;
- }
-
-
// Return true if it stores filename in the ustar header.
bool store_name( const char * const filename, Extended & extended,
Tar_header header, const bool force_extended_name )
@@ -260,12 +244,15 @@ int add_member( const char * const filename, const struct stat *,
const int infd = file_size ? open_instream( filename ) : -1;
if( file_size && infd < 0 ) { set_error_status( 1 ); return 0; }
+ const int ebsize = extended.format_block( grbuf ); // may be 0
+ if( ebsize < 0 ) { show_error( extended.full_size_error() ); return 1; }
if( encoder && gcl_opts->solidity == bsolid &&
- block_is_full( extended.full_size(), file_size, gcl_opts->data_size,
+ block_is_full( ebsize, file_size, gcl_opts->data_size,
partial_data_size ) && !archive_write( 0, 0 ) ) return 1;
+ // write extended block to archive
+ if( ebsize > 0 && !archive_write( grbuf.u8(), ebsize ) ) return 1;
+ if( !archive_write( header, header_size ) ) return 1;
- if( !write_extended( extended ) || !archive_write( header, header_size ) )
- return 1;
if( file_size )
{
const long long bufsize = 32 * header_size;
@@ -278,9 +265,7 @@ int add_member( const char * const filename, const struct stat *,
rest -= rd;
if( rd != size )
{
- if( verbosity >= 0 )
- std::fprintf( stderr, "File '%s' ends unexpectedly at pos %llu\n",
- filename, file_size - rest );
+ show_atpos_error( filename, file_size - rest, false );
close( infd ); return 1;
}
if( rest == 0 ) // last read
@@ -400,31 +385,6 @@ const char * remove_leading_dotslash( const char * const filename,
}
-/* If msgp is null, print the message, else return the message in *msgp.
- If prefix is already in the list, print nothing or return empty *msgp.
- Return true if a message is printed or returned in *msgp. */
-bool print_removed_prefix( const std::string & prefix,
- std::string * const msgp )
- {
- // prevent two threads from modifying the list of prefixes at the same time
- static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
- static std::vector< std::string > prefixes; // list of prefixes
-
- if( verbosity < 0 || prefix.empty() )
- { if( msgp ) msgp->clear(); return false; }
- xlock( &mutex );
- for( unsigned i = 0; i < prefixes.size(); ++i )
- if( prefixes[i] == prefix )
- { xunlock( &mutex ); if( msgp ) msgp->clear(); return false; }
- prefixes.push_back( prefix );
- std::string msg( "Removing leading '" ); msg += prefix;
- msg += "' from member names.";
- if( msgp ) *msgp = msg; else show_error( msg.c_str() );
- xunlock( &mutex ); // put here to prevent mixing calls to show_error
- return true;
- }
-
-
// set file_size != 0 only for regular files
bool fill_headers( const char * const filename, Extended & extended,
Tar_header header, long long & file_size, const int flag )
@@ -534,13 +494,13 @@ bool fill_headers( const char * const filename, Extended & extended,
}
-bool block_is_full( const long long extended_size,
+bool block_is_full( const int extended_size,
const unsigned long long file_size,
const unsigned long long target_size,
unsigned long long & partial_data_size )
{
const unsigned long long member_size = // may overflow 'long long'
- header_size + extended_size + round_up( file_size );
+ extended_size + header_size + round_up( file_size );
if( partial_data_size >= target_size ||
( partial_data_size >= min_data_size &&
partial_data_size + member_size / 2 > target_size ) )
@@ -549,25 +509,6 @@ bool block_is_full( const long long extended_size,
}
-void set_error_status( const int retval )
- {
- // prevent two threads from modifying the error_status at the same time
- static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
-
- xlock( &mutex );
- if( error_status < retval ) error_status = retval;
- xunlock( &mutex );
- }
-
-int final_exit_status( int retval, const bool show_msg )
- {
- if( retval == 0 && error_status )
- { if( show_msg )
- show_error( "Exiting with failure status due to previous errors." );
- retval = error_status; }
- return retval;
- }
-
unsigned ustar_chksum( const Tar_header header )
{
unsigned chksum = chksum_l * 0x20; // treat chksum field as spaces
@@ -577,8 +518,8 @@ unsigned ustar_chksum( const Tar_header header )
}
-bool verify_ustar_chksum( const Tar_header header )
- { return ( verify_ustar_magic( header ) &&
+bool check_ustar_chksum( const Tar_header header )
+ { return ( check_ustar_magic( header ) &&
ustar_chksum( header ) == parse_octal( header + chksum_o, chksum_l ) ); }
@@ -591,10 +532,25 @@ bool has_lz_ext( const std::string & name )
}
+int Cl_options::compressed() const // tri-state bool with error (-2)
+ {
+ const int lz_ext = archive_name.empty() ? -1 : has_lz_ext( archive_name );
+ if( !level_set ) return lz_ext; // no level set in command line
+ const bool cl_compressed = !uncompressed();
+ if( lz_ext < 0 || lz_ext == cl_compressed ) return cl_compressed;
+ show_file_error( archive_name.c_str(), lz_ext ?
+ "Uncompressed archive can't have .lz or .tlz extension." :
+ "Compressed archive requires .lz or .tlz extension." );
+ return -2;
+ }
+
+
int concatenate( const Cl_options & cl_opts )
{
if( cl_opts.num_files <= 0 )
{ if( verbosity >= 1 ) show_error( "Nothing to concatenate." ); return 0; }
+ int compressed = cl_opts.compressed(); // tri-state bool
+ if( compressed == -2 ) return 1;
const bool to_stdout = cl_opts.archive_name.empty();
archive_namep = to_stdout ? "(stdout)" : cl_opts.archive_name.c_str();
const int outfd =
@@ -604,24 +560,17 @@ int concatenate( const Cl_options & cl_opts )
{ close( outfd ); return 1; }
if( !to_stdout && !archive_attrs.init( outfd ) )
{ show_file_error( archive_namep, "Can't stat", errno ); return 1; }
- int compressed; // tri-state bool
- if( to_stdout ) compressed = -1; // unknown
- else
+ if( !to_stdout && compressed >= 0 ) // level or ext are set in cl
{
- compressed = has_lz_ext( cl_opts.archive_name ); // default value
- long long pos = check_compressed_appendable( outfd, true );
- if( pos > 0 ) compressed = true;
- else if( pos < 0 )
- {
- pos = check_uncompressed_appendable( outfd, true );
- if( pos > 0 ) compressed = false;
- else if( pos == -2 ) { show_error( mem_msg ); close( outfd ); return 1; }
- else if( pos < 0 )
- { show_file_error( archive_namep, compressed ?
- "This does not look like an appendable tar.lz archive." :
- "This does not look like an appendable tar archive." );
- close( outfd ); return 2; }
- }
+ const long long pos = compressed ?
+ check_compressed_appendable( outfd, true ) :
+ check_uncompressed_appendable( outfd, true );
+ if( pos == -2 ) { show_error( mem_msg ); close( outfd ); return 1; }
+ if( pos < 0 )
+ { show_file_error( archive_namep, compressed ?
+ "This does not look like an appendable tar.lz archive." :
+ "This does not look like an appendable tar archive." );
+ close( outfd ); return 2; }
}
int retval = 0;
@@ -634,17 +583,18 @@ int concatenate( const Cl_options & cl_opts )
const int infd = open_instream( filename );
if( infd < 0 ) { retval = 1; break; }
struct stat st;
- if( !to_stdout && fstat( infd, &st ) == 0 && archive_attrs.is_the_archive( st ) )
- { show_file_error( filename, "Archive can't contain itself; not concatenated." );
- close( infd ); continue; }
+ if( !to_stdout && fstat( infd, &st ) == 0 &&
+ archive_attrs.is_the_archive( st ) )
+ { show_file_error( filename, "Archive can't contain itself; "
+ "not concatenated." ); close( infd ); continue; }
long long size;
- if( compressed < 0 ) // not initialized yet
+ if( compressed < 0 ) // not initialized yet
{
if( ( size = check_compressed_appendable( infd, false ) ) > 0 )
compressed = true;
else if( ( size = check_uncompressed_appendable( infd, false ) ) > 0 )
compressed = false;
- else if( size != -2 ) { size = -1 ; compressed = has_lz_ext( filename ); }
+ else if( size != -2 ) { size = -1; compressed = has_lz_ext( filename ); }
}
else size = compressed ? check_compressed_appendable( infd, false ) :
check_uncompressed_appendable( infd, false );
@@ -673,15 +623,12 @@ int concatenate( const Cl_options & cl_opts )
int encode( const Cl_options & cl_opts )
{
if( !grbuf.size() ) { show_error( mem_msg ); return 1; }
- const bool compressed = ( cl_opts.level >= 0 && cl_opts.level <= 9 );
+ int compressed = cl_opts.compressed(); // tri-state bool
+ if( compressed == -2 ) return 1;
const bool to_stdout = cl_opts.archive_name.empty();
archive_namep = to_stdout ? "(stdout)" : cl_opts.archive_name.c_str();
gcl_opts = &cl_opts;
- if( !to_stdout && !compressed && has_lz_ext( cl_opts.archive_name ) )
- { show_file_error( archive_namep,
- "Uncompressed mode incompatible with .lz extension." ); return 2; }
-
const bool append = cl_opts.program_mode == m_append;
if( cl_opts.num_files <= 0 )
{
@@ -701,18 +648,23 @@ int encode( const Cl_options & cl_opts )
{ close( goutfd ); return 1; }
if( append && !to_stdout )
{
- if( compressed && check_compressed_appendable( goutfd, true ) < 0 )
- { show_file_error( archive_namep,
- "This does not look like an appendable tar.lz archive." );
- close( goutfd ); return 2; }
- if( !compressed )
+ long long pos;
+ if( compressed < 0 ) // not initialized yet
{
- const long long pos = check_uncompressed_appendable( goutfd, true );
- if( pos == -2 ) { show_error( mem_msg ); close( goutfd ); return 1; }
- if( pos < 0 ) { show_file_error( archive_namep,
- "This does not look like an appendable tar archive." );
- close( goutfd ); return 2; }
+ if( ( pos = check_compressed_appendable( goutfd, true ) ) > 0 )
+ compressed = true;
+ else if( ( pos = check_uncompressed_appendable( goutfd, true ) ) > 0 )
+ compressed = false;
+ else if( pos != -2 ) { pos = -1; compressed = false; } // unknown
}
+ else pos = compressed ? check_compressed_appendable( goutfd, true ) :
+ check_uncompressed_appendable( goutfd, true );
+ if( pos == -2 ) { show_error( mem_msg ); close( goutfd ); return 1; }
+ if( pos < 0 )
+ { show_file_error( archive_namep, compressed ?
+ "This does not look like an appendable tar.lz archive." :
+ "This does not look like an appendable tar archive." );
+ close( goutfd ); return 2; }
}
if( !archive_attrs.init( goutfd ) )