"Fossies" - the Fresh Open Source Software Archive

Member "zutils-1.10/zutils.cc" (5 Jan 2021, 8896 Bytes) of package /linux/privat/zutils-1.10.tar.lz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "zutils.cc" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes reports: 1.10-rc1_vs_1.10 or 1.9_vs_1.10.

    1 /* Zutils - Utilities dealing with compressed files
    2    Copyright (C) 2009-2021 Antonio Diaz Diaz.
    3 
    4    This program is free software: you can redistribute it and/or modify
    5    it under the terms of the GNU General Public License as published by
    6    the Free Software Foundation, either version 2 of the License, or
    7    (at your option) any later version.
    8 
    9    This program is distributed in the hope that it will be useful,
   10    but WITHOUT ANY WARRANTY; without even the implied warranty of
   11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   12    GNU General Public License for more details.
   13 
   14    You should have received a copy of the GNU General Public License
   15    along with this program.  If not, see <http://www.gnu.org/licenses/>.
   16 */
   17 
   18 #define _FILE_OFFSET_BITS 64
   19 
   20 #include <cerrno>
   21 #include <csignal>
   22 #include <cstdio>
   23 #include <cstdlib>
   24 #include <cstring>
   25 #include <string>
   26 #include <vector>
   27 #include <stdint.h>
   28 #include <unistd.h>
   29 #include <sys/wait.h>
   30 
   31 #include "rc.h"
   32 #include "zutils.h"
   33 
   34 
   35 namespace {
   36 
   37 inline bool isvalid_ds( const uint8_t ds )  // lzip valid dictionary_size
   38   {
   39   enum { min_dictionary_size = 1 << 12,
   40          max_dictionary_size = 1 << 29 };
   41   unsigned dictionary_size = ( 1 << ( ds & 0x1F ) );
   42   if( dictionary_size > min_dictionary_size )
   43     dictionary_size -= ( dictionary_size / 16 ) * ( ( ds >> 5 ) & 7 );
   44   return ( dictionary_size >= min_dictionary_size &&
   45            dictionary_size <= max_dictionary_size );
   46   }
   47 
   48 
   49 /* Returns -1 if child not terminated, 2 in case of error, or exit status of
   50    child process 'pid'.
   51 */
   52 int child_status( const pid_t pid, const char * const name )
   53   {
   54   int status;
   55   while( true )
   56     {
   57     const int tmp = waitpid( pid, &status, WNOHANG );
   58     if( tmp == -1 && errno != EINTR )
   59       {
   60       if( verbosity >= 0 )
   61         std::fprintf( stderr, "%s: Error checking status of '%s': %s\n",
   62                       program_name, name, std::strerror( errno ) );
   63       _exit( 2 );
   64       }
   65     if( tmp == 0 ) return -1;           // child not terminated
   66     if( tmp == pid ) break;         // child terminated
   67     }
   68   if( WIFEXITED( status ) ) return WEXITSTATUS( status );
   69   if( WIFSIGNALED( status ) && WTERMSIG( status ) == SIGPIPE ) return 0;
   70   return 2;
   71   }
   72 
   73 } // end namespace
   74 
   75 
   76 /* Returns the number of bytes really read.
   77    If (returned value < size) and (errno == 0), means EOF was reached.
   78 */
   79 int readblock( const int fd, uint8_t * const buf, const int size )
   80   {
   81   int sz = 0;
   82   errno = 0;
   83   while( sz < size )
   84     {
   85     const int n = read( fd, buf + sz, size - sz );
   86     if( n > 0 ) sz += n;
   87     else if( n == 0 ) break;                // EOF
   88     else if( errno != EINTR ) break;
   89     errno = 0;
   90     }
   91   return sz;
   92   }
   93 
   94 
   95 /* Returns the number of bytes really written.
   96    If (returned value < size), it is always an error.
   97 */
   98 int writeblock( const int fd, const uint8_t * const buf, const int size )
   99   {
  100   int sz = 0;
  101   errno = 0;
  102   while( sz < size )
  103     {
  104     const int n = write( fd, buf + sz, size - sz );
  105     if( n > 0 ) sz += n;
  106     else if( n < 0 && errno != EINTR ) break;
  107     errno = 0;
  108     }
  109   return sz;
  110   }
  111 
  112 
  113 // Empty filename means stdin.
  114 //
  115 bool feed_data( const std::string & filename, const int infd, const int outfd,
  116                 const uint8_t * magic_data, const int magic_size )
  117   {
  118   if( magic_size && writeblock( outfd, magic_data, magic_size ) != magic_size )
  119     { show_error( "Write error", errno ); return false; }
  120   enum { buffer_size = 4096 };
  121   uint8_t buffer[buffer_size];
  122   while( true )
  123     {
  124     const int size = readblock( infd, buffer, buffer_size );
  125     if( size != buffer_size && errno )
  126       { const char * const name = filename.empty() ? "-" : filename.c_str();
  127         show_file_error( name, "Read error", errno ); return false; }
  128     if( size > 0 && writeblock( outfd, buffer, size ) != size )
  129       { show_error( "Write error", errno ); return false; }
  130     if( size < buffer_size ) break;
  131     }
  132   return true;
  133   }
  134 
  135 
  136 bool good_status( const Children & children, const bool finished )
  137   {
  138   bool error = false;
  139   for( int i = 0; i < 2; ++i )
  140     {
  141     const pid_t pid = children.pid[i];
  142     if( pid )
  143       {
  144       const char * const name =
  145         ( i == 0 ) ? "data feeder" : children.compressor_name;
  146       // even if compressor finished, trailing data may remain in data feeder
  147       if( i == 0 || !finished )
  148         {
  149         const int tmp = child_status( pid, name );
  150         if( tmp < 0 )               // child not terminated
  151           { kill( pid, SIGTERM ); wait_for_child( pid, name ); }
  152         else if( tmp != 0 ) error = true;   // child status != 0
  153         }
  154       else
  155         if( wait_for_child( pid, name ) != 0 ) error = true;
  156       }
  157     }
  158   return !error;
  159   }
  160 
  161 
  162 bool set_data_feeder( const std::string & filename, int * const infdp,
  163                       Children & children, int format_index )
  164   {
  165   uint8_t magic_data[magic_buf_size];
  166   int magic_size = 0;
  167   if( format_index < 0 )
  168     format_index = test_format( *infdp, magic_data, &magic_size );
  169   children.compressor_name = get_compressor_name( format_index );
  170 
  171   if( children.compressor_name )    // compressed
  172     {
  173     int fda[2];             // pipe from feeder
  174     int fda2[2];            // pipe from compressor
  175     if( pipe( fda ) < 0 || pipe( fda2 ) < 0 )
  176       { show_error( "Can't create pipe", errno ); return false; }
  177     const int old_infd = *infdp;
  178     *infdp = fda2[0];
  179     const pid_t pid = fork();
  180     if( pid == 0 )          // child 1 (compressor feeder)
  181       {
  182       if( close( fda[0] ) != 0 ||
  183           close( fda2[0] ) != 0 || close( fda2[1] ) != 0 ||
  184           !feed_data( filename, old_infd, fda[1], magic_data, magic_size ) )
  185         _exit( 2 );
  186       if( close( fda[1] ) != 0 )
  187         { show_close_error(); _exit( 2 ); }
  188       _exit( 0 );
  189       }
  190     if( pid < 0 )           // parent
  191       { show_fork_error( "data feeder" ); return false; }
  192 
  193     const pid_t pid2 = fork();
  194     if( pid2 == 0 )         // child 2 (compressor)
  195       {
  196       if( dup2( fda[0], STDIN_FILENO ) >= 0 &&
  197           dup2( fda2[1], STDOUT_FILENO ) >= 0 &&
  198           close( fda[0] ) == 0 && close( fda[1] ) == 0 &&
  199           close( fda2[0] ) == 0 && close( fda2[1] ) == 0 )
  200         {
  201         const std::vector< std::string > & compressor_args =
  202           get_compressor_args( format_index );
  203         const int size = compressor_args.size();
  204         const char ** const argv = new const char *[size+3];
  205         argv[0] = children.compressor_name;
  206         for( int i = 0; i < size; ++i )
  207           argv[i+1] = compressor_args[i].c_str();
  208         argv[size+1] = ( verbosity >= 0 ) ? "-d" : "-dq";
  209         argv[size+2] = 0;
  210         execvp( argv[0], (char **)argv );
  211         }
  212       show_exec_error( children.compressor_name );
  213       _exit( 2 );
  214       }
  215     if( pid2 < 0 )          // parent
  216       { show_fork_error( children.compressor_name ); return false; }
  217 
  218     close( fda[0] ); close( fda[1] ); close( fda2[1] );
  219     children.pid[0] = pid;
  220     children.pid[1] = pid2;
  221     }
  222   else                  // uncompressed
  223     {
  224     int fda[2];             // pipe from feeder
  225     if( pipe( fda ) < 0 )
  226       { show_error( "Can't create pipe", errno ); return false; }
  227     const int old_infd = *infdp;
  228     *infdp = fda[0];
  229     const pid_t pid = fork();
  230     if( pid == 0 )          // child (feeder)
  231       {
  232       if( close( fda[0] ) != 0 ||
  233           !feed_data( filename, old_infd, fda[1], magic_data, magic_size ) )
  234         _exit( 2 );
  235       if( close( fda[1] ) != 0 )
  236         { show_close_error(); _exit( 2 ); }
  237       _exit( 0 );
  238       }
  239     if( pid < 0 )           // parent
  240       { show_fork_error( "data feeder" ); return false; }
  241     close( fda[1] );
  242     children.pid[0] = pid;
  243     children.pid[1] = 0;
  244     }
  245   return true;
  246   }
  247 
  248 
  249 // Returns format index or -1 if uncompressed
  250 //
  251 int test_format( const int infd, uint8_t magic_data[],
  252                  int * const magic_sizep )
  253   {
  254   enum { bzip2_magic_size = 3,
  255           gzip_magic_size = 2,
  256           lzip_magic_size = 5,
  257             xz_magic_size = 5 };
  258   const uint8_t bzip2_magic[bzip2_magic_size] =
  259     { 0x42, 0x5A, 0x68 };               // "BZh"
  260   const uint8_t gzip_magic[gzip_magic_size] =
  261     { 0x1F, 0x8B };
  262   const uint8_t lzip_magic[lzip_magic_size] =
  263     { 0x4C, 0x5A, 0x49, 0x50, 0x01 };           // "LZIP\001"
  264   const uint8_t xz_magic[xz_magic_size] =
  265     { 0xFD, 0x37, 0x7A, 0x58, 0x5A };           // 0xFD, "7zXZ"
  266 
  267   *magic_sizep = readblock( infd, magic_data, magic_buf_size );
  268   if( *magic_sizep == magic_buf_size )
  269     {
  270     if( std::memcmp( magic_data, bzip2_magic, bzip2_magic_size ) == 0 &&
  271         magic_data[3] >= '1' && magic_data[3] <= '9' &&
  272         std::memcmp( magic_data + 4, "1AY&SY", 6 ) == 0 )
  273       return fmt_bz2;
  274     if( std::memcmp( magic_data, gzip_magic, gzip_magic_size ) == 0 )
  275       return fmt_gz;
  276     if( std::memcmp( magic_data, lzip_magic, lzip_magic_size ) == 0 &&
  277         isvalid_ds( magic_data[lzip_magic_size] ) )
  278       return fmt_lz;
  279     if( std::memcmp( magic_data, xz_magic, xz_magic_size ) == 0 )
  280       return fmt_xz;
  281     }
  282   return -1;
  283   }