diff options
Diffstat (limited to 'main.cc')
-rw-r--r-- | main.cc | 113 |
1 files changed, 50 insertions, 63 deletions
@@ -1,5 +1,5 @@ /* Tarlz - Archiver with multimember lzip compression - Copyright (C) 2013-2018 Antonio Diaz Diaz. + Copyright (C) 2013-2019 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 @@ -32,15 +32,16 @@ #include <string> #include <vector> #include <fcntl.h> +#include <pthread.h> #include <stdint.h> #include <unistd.h> #include <sys/stat.h> #include <grp.h> #include <pwd.h> +#include <lzlib.h> #if defined(__OS2__) #include <io.h> #endif -#include <lzlib.h> #include "arg_parser.h" #include "tarlz.h" @@ -58,21 +59,23 @@ int verbosity = 0; namespace { const char * const program_name = "tarlz"; -const char * const program_year = "2018"; +const char * const program_year = "2019"; const char * invocation_name = 0; enum Mode { m_none, m_append, m_concatenate, m_create, m_extract, m_list }; -void show_help() +void show_help( const long num_online ) { - std::printf( "Tarlz is a small and simple implementation of the tar archiver. By default\n" - "tarlz creates, lists and extracts archives in a simplified posix pax format\n" - "compressed with lzip on a per file basis. Each tar member is compressed in\n" - "its own lzip member, as well as the end-of-file blocks. This method is fully\n" - "backward compatible with standard tar tools like GNU tar, which treat the\n" - "resulting multimember tar.lz archive like any other tar.lz archive. Tarlz\n" - "can append files to the end of such compressed archives.\n" + std::printf( "Tarlz is a combined implementation of the tar archiver and the lzip\n" + "compressor. By default tarlz creates, lists and extracts archives in a\n" + "simplified posix pax format compressed with lzip on a per file basis. Each\n" + "tar member is compressed in its own lzip member, as well as the end-of-file\n" + "blocks. This method adds an indexed lzip layer on top of the tar archive,\n" + "making it possible to decode the archive safely in parallel. The resulting\n" + "multimember tar.lz archive is fully backward compatible with standard tar\n" + "tools like GNU tar, which treat it like any other tar.lz archive. Tarlz can\n" + "append files to the end of such compressed archives.\n" "\nThe tarlz file format is a safe posix-style backup format. In case of\n" "corruption, tarlz can extract all the undamaged members from the tar.lz\n" "archive, skipping over the damaged members, just like the standard\n" @@ -87,6 +90,7 @@ void show_help() " -c, --create create a new archive\n" " -C, --directory=<dir> change to directory <dir>\n" " -f, --file=<archive> use archive file <archive>\n" + " -n, --threads=<n> set number of decompression threads [%ld]\n" " -q, --quiet suppress all messages\n" " -r, --append append files to the end of an archive\n" " -t, --list list the contents of an archive\n" @@ -103,8 +107,13 @@ void show_help() " --keep-damaged don't delete partially extracted files\n" " --missing-crc exit with error status if missing extended CRC\n" // " --permissive allow repeated extended headers and records\n" - " --uncompressed don't compress the archive created\n" - "\nExit status: 0 for a normal exit, 1 for environmental problems (file\n" + " --uncompressed don't compress the archive created\n", + num_online ); + if( verbosity >= 1 ) + { + std::printf( " --debug=<level> (0-1) print debug statistics to stderr\n" ); + } + std::printf( "\nExit status: 0 for a normal exit, 1 for environmental problems (file\n" "not found, invalid flags, I/O errors, etc), 2 to indicate a corrupt or\n" "invalid input file, 3 for an internal consistency error (eg, bug) which\n" "caused tarlz to panic.\n" @@ -189,7 +198,8 @@ void set_owner( const char * const arg ) { const struct passwd * const pw = getpwnam( arg ); if( pw ) cl_owner = pw->pw_uid; - else if( std::isdigit( arg[0] ) ) cl_owner = getnum( arg, 0, INT_MAX ); + else if( std::isdigit( (unsigned char)arg[0] ) ) + cl_owner = getnum( arg, 0, INT_MAX ); else { show_file_error( arg, "Invalid owner" ); std::exit( 1 ); } } @@ -197,7 +207,8 @@ void set_group( const char * const arg ) { const struct group * const gr = getgrnam( arg ); if( gr ) cl_group = gr->gr_gid; - else if( std::isdigit( arg[0] ) ) cl_group = getnum( arg, 0, INT_MAX ); + else if( std::isdigit( (unsigned char)arg[0] ) ) + cl_group = getnum( arg, 0, INT_MAX ); else { show_file_error( arg, "Invalid group" ); std::exit( 1 ); } } @@ -226,43 +237,6 @@ int open_outstream( const std::string & name, const bool create ) } -/* Returns the number of bytes really read. - If (returned value < size) and (errno == 0), means EOF was reached. -*/ -int readblock( const int fd, uint8_t * const buf, const int size ) - { - int sz = 0; - errno = 0; - while( sz < size ) - { - const int n = read( fd, buf + sz, size - sz ); - if( n > 0 ) sz += n; - else if( n == 0 ) break; // EOF - else if( errno != EINTR ) break; - errno = 0; - } - return sz; - } - - -/* Returns the number of bytes really written. - If (returned value < size), it is always an error. -*/ -int writeblock( const int fd, const uint8_t * const buf, const int size ) - { - int sz = 0; - errno = 0; - while( sz < size ) - { - const int n = write( fd, buf + sz, size - sz ); - if( n > 0 ) sz += n; - else if( n < 0 && errno != EINTR ) break; - errno = 0; - } - return sz; - } - - void show_error( const char * const msg, const int errcode, const bool help ) { if( verbosity < 0 ) return; @@ -297,8 +271,10 @@ void internal_error( const char * const msg ) int main( const int argc, const char * const argv[] ) { std::string archive_name; + int debug_level = 0; + int num_workers = -1; // start this many worker threads + int level = 6; // compression level, < 0 means uncompressed Mode program_mode = m_none; - int level = 6; // compression level, < 0 = uncompressed bool keep_damaged = false; bool missing_crc = false; bool permissive = false; @@ -308,8 +284,8 @@ int main( const int argc, const char * const argv[] ) { show_error( "Bad library version. At least lzlib 1.0 is required." ); return 1; } - enum { opt_ano = 256, opt_aso, opt_crc, opt_dso, opt_grp, opt_kd, opt_nso, - opt_own, opt_per, opt_sol, opt_un }; + enum { opt_ano = 256, opt_aso, opt_crc, opt_dbg, opt_dso, opt_grp, opt_kd, + opt_nso, opt_own, opt_per, opt_sol, opt_un }; const Arg_parser::Option options[] = { { '0', 0, Arg_parser::no }, @@ -328,6 +304,7 @@ int main( const int argc, const char * const argv[] ) { 'f', "file", Arg_parser::yes }, { 'h', "help", Arg_parser::no }, { 'H', "format", Arg_parser::yes }, + { 'n', "threads", Arg_parser::yes }, { 'q', "quiet", Arg_parser::no }, { 'r', "append", Arg_parser::no }, { 't', "list", Arg_parser::no }, @@ -336,6 +313,7 @@ int main( const int argc, const char * const argv[] ) { 'x', "extract", Arg_parser::no }, { opt_ano, "anonymous", Arg_parser::no }, { opt_aso, "asolid", Arg_parser::no }, + { opt_dbg, "debug", Arg_parser::yes }, { opt_dso, "dsolid", Arg_parser::no }, { opt_grp, "group", Arg_parser::yes }, { opt_kd, "keep-damaged", Arg_parser::no }, @@ -351,6 +329,11 @@ int main( const int argc, const char * const argv[] ) if( parser.error().size() ) // bad option { show_error( parser.error().c_str(), 0, true ); return 1; } + const long num_online = std::max( 1L, sysconf( _SC_NPROCESSORS_ONLN ) ); + long max_workers = sysconf( _SC_THREAD_THREADS_MAX ); + if( max_workers < 1 || max_workers > INT_MAX / (int)sizeof (pthread_t) ) + max_workers = INT_MAX / sizeof (pthread_t); + int filenames = 0; for( int argind = 0; argind < parser.arguments(); ++argind ) { @@ -367,8 +350,9 @@ int main( const int argc, const char * const argv[] ) case 'c': set_mode( program_mode, m_create ); break; case 'C': break; // skip chdir case 'f': if( sarg != "-" ) archive_name = sarg; break; - case 'h': show_help(); return 0; + case 'h': show_help( num_online ); return 0; case 'H': break; // ignore format + case 'n': num_workers = getnum( arg, 0, max_workers ); break; case 'q': verbosity = -1; break; case 'r': set_mode( program_mode, m_append ); break; case 't': set_mode( program_mode, m_list ); break; @@ -376,15 +360,16 @@ int main( const int argc, const char * const argv[] ) case 'V': show_version(); return 0; case 'x': set_mode( program_mode, m_extract ); break; case opt_ano: set_owner( "root" ); set_group( "root" ); break; - case opt_aso: cl_solid = 2; break; + case opt_aso: solidity = asolid; break; case opt_crc: missing_crc = true; break; - case opt_dso: cl_solid = 1; break; + case opt_dbg: debug_level = getnum( arg, 0, 3 ); break; + case opt_dso: solidity = dsolid; break; case opt_grp: set_group( arg ); break; case opt_kd: keep_damaged = true; break; - case opt_nso: cl_solid = 0; break; + case opt_nso: solidity = no_solid; break; case opt_own: set_owner( arg ); break; case opt_per: permissive = true; break; - case opt_sol: cl_solid = 3; break; + case opt_sol: solidity = solid; break; case opt_un: level = -1; break; default : internal_error( "uncaught option" ); } @@ -395,6 +380,8 @@ int main( const int argc, const char * const argv[] ) setmode( STDOUT_FILENO, O_BINARY ); #endif + if( num_workers < 0 ) num_workers = std::min( num_online, max_workers ); + switch( program_mode ) { case m_none: show_error( "Missing operation.", 0, true ); return 2; @@ -403,8 +390,8 @@ int main( const int argc, const char * const argv[] ) program_mode == m_append ); case m_concatenate: return concatenate( archive_name, parser, filenames ); case m_extract: - case m_list: return decode( archive_name, parser, filenames, - keep_damaged, program_mode == m_list, - missing_crc, permissive ); + case m_list: return decode( archive_name, parser, filenames, num_workers, + debug_level, keep_damaged, program_mode == m_list, + missing_crc, permissive ); } } |