"Fossies" - the Fresh Open Source Software Archive

Member "zutils-1.10/ztest.cc" (5 Jan 2021, 12781 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 "ztest.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 /* Ztest - verify the integrity of compressed files
    2    Copyright (C) 2010-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 <climits>
   22 #include <csignal>
   23 #include <cstdio>
   24 #include <cstdlib>
   25 #include <cstring>
   26 #include <list>
   27 #include <string>
   28 #include <vector>
   29 #include <dirent.h>
   30 #include <fcntl.h>
   31 #include <stdint.h>
   32 #include <unistd.h>
   33 #include <sys/stat.h>
   34 #if defined(__MSVCRT__) || defined(__OS2__)
   35 #include <io.h>
   36 #endif
   37 
   38 #include "arg_parser.h"
   39 #include "rc.h"
   40 #include "zutils.h"
   41 
   42 #ifndef O_BINARY
   43 #define O_BINARY 0
   44 #endif
   45 
   46 
   47 namespace {
   48 
   49 #include "recursive.cc"
   50 
   51 void show_help()
   52   {
   53   std::printf( "ztest verifies the integrity of the compressed files specified.\n"
   54                "Uncompressed files are ignored. If a file is specified as '-', the\n"
   55                "integrity of compressed data read from standard input is verified. Data\n"
   56                "read from standard input must be all in the same compressed format. If\n"
   57                "a file fails to decompress, does not exist, can't be opened, or is a\n"
   58                "terminal, ztest continues verifying the rest of the files. A final\n"
   59                "diagnostic is shown at verbosity level 1 or higher if any file fails the\n"
   60                "test when testing multiple files.\n"
   61                "\nIf no files are specified, recursive searches examine the current\n"
   62                "working directory, and nonrecursive searches read standard input.\n"
   63                "\nThe formats supported are bzip2, gzip, lzip, and xz.\n"
   64                "\nNote that error detection in the xz format is broken. First, some xz\n"
   65                "files lack integrity information. Second, not all xz decompressors can\n"
   66                "verify the integrity of all xz files. Third, section 2.1.1.2 'Stream\n"
   67                "Flags' of the xz format specification allows xz decompressors to produce\n"
   68                "garbage output without issuing any warning. Therefore, xz files can't\n"
   69                "always be verified as reliably as files in the other formats can.\n"
   70                "\nUsage: ztest [options] [files]\n"
   71                "\nExit status is 0 if all compressed files verify OK, 1 if environmental\n"
   72                "problems (file not found, invalid flags, I/O errors, etc), 2 if any\n"
   73                "compressed file is corrupt or invalid.\n"
   74                "\nOptions:\n"
   75                "  -h, --help                   display this help and exit\n"
   76                "  -V, --version                output version information and exit\n"
   77                "  -M, --format=<list>          process only the formats in <list>\n"
   78                "  -N, --no-rcfile              don't read runtime configuration file\n"
   79                "  -O, --force-format=<fmt>     force the format given (bz2, gz, lz, xz)\n"
   80                "  -q, --quiet                  suppress all messages\n"
   81                "  -r, --recursive              operate recursively on directories\n"
   82                "  -R, --dereference-recursive  recursively follow symbolic links\n"
   83                "  -v, --verbose                be verbose (a 2nd -v gives more)\n"
   84                "      --bz2=<command>          set compressor and options for bzip2 format\n"
   85                "      --gz=<command>           set compressor and options for gzip format\n"
   86                "      --lz=<command>           set compressor and options for lzip format\n"
   87                "      --xz=<command>           set compressor and options for xz format\n" );
   88   show_help_addr();
   89   }
   90 
   91 
   92 int open_instream( const std::string & input_filename )
   93   {
   94   const int infd = open( input_filename.c_str(), O_RDONLY | O_BINARY );
   95   if( infd < 0 )
   96     show_file_error( input_filename.c_str(), "Can't open input file", errno );
   97   return infd;
   98   }
   99 
  100 
  101 int ztest_stdin( const int infd, int format_index,
  102                  const std::vector< const char * > & ztest_args )
  103   {
  104   uint8_t magic_data[magic_buf_size];
  105   int magic_size = 0;
  106   if( format_index < 0 )
  107     format_index = test_format( infd, magic_data, &magic_size );
  108   const char * const compressor_name = get_compressor_name( format_index );
  109   if( !compressor_name )
  110     { show_error( "Unknown data format read from stdin." ); return 2; }
  111   int fda[2];               // pipe from feeder
  112   if( pipe( fda ) < 0 )
  113     { show_error( "Can't create pipe", errno ); return 1; }
  114 
  115   const pid_t pid = fork();
  116   if( pid == 0 )            // child1 (compressor feeder)
  117     {
  118     if( close( fda[0] ) != 0 ||
  119         !feed_data( "", infd, fda[1], magic_data, magic_size ) )
  120       _exit( 1 );
  121     if( close( fda[1] ) != 0 )
  122       { show_close_error(); _exit( 1 ); }
  123     _exit( 0 );
  124     }
  125   if( pid < 0 )             // parent
  126     { show_fork_error( "data feeder" ); return 1; }
  127 
  128   const pid_t pid2 = fork();
  129   if( pid2 == 0 )           // child2 (compressor)
  130     {
  131     if( dup2( fda[0], STDIN_FILENO ) >= 0 &&
  132         close( fda[0] ) == 0 && close( fda[1] ) == 0 )
  133       {
  134       const std::vector< std::string > & compressor_args =
  135         get_compressor_args( format_index );
  136       const int size = compressor_args.size();
  137       const int size2 = ztest_args.size();
  138       const char ** const argv = new const char *[size+size2+3];
  139       argv[0] = compressor_name;
  140       for( int i = 0; i < size; ++i )
  141         argv[i+1] = compressor_args[i].c_str();
  142       for( int i = 0; i < size2; ++i )
  143         argv[i+size+1] = ztest_args[i];
  144       argv[size+size2+1] = "-t";
  145       argv[size+size2+2] = 0;
  146       execvp( argv[0], (char **)argv );
  147       }
  148     show_exec_error( compressor_name );
  149     _exit( 1 );
  150     }
  151   if( pid2 < 0 )            // parent
  152     { show_fork_error( compressor_name ); return 1; }
  153 
  154   close( fda[0] ); close( fda[1] );
  155   const bool isgzxz = ( format_index == fmt_gz || format_index == fmt_xz );
  156   int retval = wait_for_child( pid2, compressor_name, 1, isgzxz );
  157   if( retval == 0 && wait_for_child( pid, "data feeder" ) != 0 )
  158     retval = 1;
  159   return retval;
  160   }
  161 
  162 
  163 int ztest_file( const int infd, int format_index,
  164                 const std::string & input_filename,
  165                 const std::vector< const char * > & ztest_args )
  166   {
  167   static int disable_xz = -1;               // tri-state bool
  168   uint8_t magic_data[magic_buf_size];
  169   int magic_size = 0;
  170   if( format_index < 0 )
  171     format_index = test_format( infd, magic_data, &magic_size );
  172   const char * const compressor_name = get_compressor_name( format_index );
  173   if( !compressor_name )
  174     return 0;               // ignore this file
  175   if( format_index == fmt_xz )
  176     {
  177     if( disable_xz < 0 )
  178       {
  179       std::string command( compressor_name ); command += " -V > /dev/null 2>&1";
  180       disable_xz = ( std::system( command.c_str() ) != 0 );
  181       }
  182     if( disable_xz ) return 0;      // ignore this file if no xz installed
  183     }
  184 
  185   const pid_t pid = fork();
  186 
  187   if( pid == 0 )            // child (compressor)
  188     {
  189     const std::vector< std::string > & compressor_args =
  190       get_compressor_args( format_index );
  191     const int size = compressor_args.size();
  192     const int size2 = ztest_args.size();
  193     const char ** const argv = new const char *[size+size2+5];
  194     argv[0] = compressor_name;
  195     for( int i = 0; i < size; ++i )
  196       argv[i+1] = compressor_args[i].c_str();
  197     for( int i = 0; i < size2; ++i )
  198       argv[i+size+1] = ztest_args[i];
  199     argv[size+size2+1] = "-t";
  200     argv[size+size2+2] = "--";
  201     argv[size+size2+3] = input_filename.c_str();
  202     argv[size+size2+4] = 0;
  203     execvp( argv[0], (char **)argv );
  204     show_exec_error( compressor_name );
  205     _exit( 1 );
  206     }
  207   if( pid < 0 )             // parent
  208     { show_fork_error( compressor_name ); return 1; }
  209 
  210   const bool isgzxz = ( format_index == fmt_gz || format_index == fmt_xz );
  211   return wait_for_child( pid, compressor_name, 1, isgzxz );
  212   }
  213 
  214 } // end namespace
  215 
  216 
  217 int main( const int argc, const char * const argv[] )
  218   {
  219   enum { bz2_opt = 256, gz_opt, lz_opt, xz_opt };
  220   int format_index = -1;
  221   int recursive = 0;                // 1 = '-r', 2 = '-R'
  222   std::list< std::string > filenames;
  223   std::vector< const char * > ztest_args;   // args to ztest, maybe empty
  224   program_name = "ztest";
  225   invocation_name = ( argc > 0 ) ? argv[0] : program_name;
  226 
  227   const Arg_parser::Option options[] =
  228     {
  229     { 'h', "help",                  Arg_parser::no  },
  230     { 'M', "format",                Arg_parser::yes },
  231     { 'N', "no-rcfile",             Arg_parser::no  },
  232     { 'O', "force-format",          Arg_parser::yes },
  233     { 'q', "quiet",                 Arg_parser::no  },
  234     { 'r', "recursive",             Arg_parser::no  },
  235     { 'R', "dereference-recursive", Arg_parser::no  },
  236     { 'v', "verbose",               Arg_parser::no  },
  237     { 'V', "version",               Arg_parser::no  },
  238     { bz2_opt,    "bz2",            Arg_parser::yes },
  239     { gz_opt,     "gz",             Arg_parser::yes },
  240     { lz_opt,     "lz",             Arg_parser::yes },
  241     { xz_opt,     "xz",             Arg_parser::yes },
  242     {  0 ,  0,                      Arg_parser::no  } };
  243 
  244   const Arg_parser parser( argc, argv, options );
  245   if( parser.error().size() )               // bad option
  246     { show_error( parser.error().c_str(), 0, true ); return 1; }
  247 
  248   maybe_process_config_file( parser );
  249 
  250   int argind = 0;
  251   for( ; argind < parser.arguments(); ++argind )
  252     {
  253     const int code = parser.code( argind );
  254     if( !code ) break;                  // no more options
  255     const std::string & arg = parser.argument( argind );
  256     switch( code )
  257       {
  258       case 'h': show_help(); return 0;
  259       case 'M': parse_format_list( arg ); break;
  260       case 'N': break;
  261       case 'O': format_index = parse_format_type( arg ); break;
  262       case 'q': verbosity = -1; ztest_args.push_back( "-q" ); break;
  263       case 'r': recursive = 1; break;
  264       case 'R': recursive = 2; break;
  265       case 'v': if( verbosity < 4 ) ++verbosity;
  266                 ztest_args.push_back( "-v" ); break;
  267       case 'V': show_version(); return 0;
  268       case bz2_opt: parse_compressor( arg, fmt_bz2, 1 ); break;
  269       case gz_opt: parse_compressor( arg, fmt_gz, 1 ); break;
  270       case lz_opt: parse_compressor( arg, fmt_lz, 1 ); break;
  271       case xz_opt: parse_compressor( arg, fmt_xz, 1 ); break;
  272       default : internal_error( "uncaught option." );
  273       }
  274     } // end process options
  275 
  276 #if defined(__MSVCRT__) || defined(__OS2__)
  277   setmode( STDIN_FILENO, O_BINARY );
  278   setmode( STDOUT_FILENO, O_BINARY );
  279 #endif
  280 
  281   for( ; argind < parser.arguments(); ++argind )
  282     filenames.push_back( parser.argument( argind ) );
  283 
  284   if( filenames.empty() ) filenames.push_back( recursive ? "." : "-" );
  285 
  286   std::string input_filename;
  287   int files_tested = 0, failed_tests = 0;
  288   int retval = 0;
  289   bool error = false;
  290   bool stdin_used = false;
  291   while( next_filename( filenames, input_filename, error, recursive ) )
  292     {
  293     int infd;
  294     if( input_filename == "." )
  295       {
  296       if( stdin_used ) continue; else stdin_used = true;
  297       infd = STDIN_FILENO; input_filename = "-";
  298       }
  299     else
  300       {
  301       infd = open_instream( input_filename );
  302       if( infd < 0 ) { error = true; continue; }
  303       }
  304 
  305     if( isatty( infd ) )            // for example /dev/tty
  306       {
  307       show_file_error( input_filename == "-" ? "(stdin)" : input_filename.c_str(),
  308                        "I won't read compressed data from a terminal." );
  309       close( infd ); error = true; continue;
  310       }
  311 
  312     int tmp;
  313     if( infd == STDIN_FILENO )
  314       tmp = ztest_stdin( infd, format_index, ztest_args );
  315     else tmp = ztest_file( infd, format_index, input_filename, ztest_args );
  316     if( tmp > retval ) retval = tmp;
  317     ++files_tested; if( tmp ) ++failed_tests;
  318 
  319     if( close( infd ) != 0 )
  320       { show_file_error( input_filename.c_str(), "Error closing input file",
  321                          errno ); error = true; }
  322     }
  323 
  324   if( std::fclose( stdout ) != 0 )  // in case decompressor writes to stdout
  325     {
  326     show_error( "Error closing stdout", errno );
  327     error = true;
  328     }
  329   if( error && retval == 0 ) retval = 1;
  330   if( failed_tests > 0 && verbosity >= 1 && files_tested > 1 )
  331     std::fprintf( stderr, "%s: warning: %d %s failed the test.\n",
  332                   program_name, failed_tests,
  333                   ( failed_tests == 1 ) ? "file" : "files" );
  334   return retval;
  335   }