LCOV - code coverage report
Current view: top level - tools - gpgtar-list.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 108 173 62.4 %
Date: 2016-11-29 15:00:56 Functions: 7 8 87.5 %

          Line data    Source code
       1             : /* gpgtar-list.c - List a TAR archive
       2             :  * Copyright (C) 2010 Free Software Foundation, Inc.
       3             :  *
       4             :  * This file is part of GnuPG.
       5             :  *
       6             :  * GnuPG is free software; you can redistribute it and/or modify
       7             :  * it under the terms of the GNU General Public License as published by
       8             :  * the Free Software Foundation; either version 3 of the License, or
       9             :  * (at your option) any later version.
      10             :  *
      11             :  * GnuPG is distributed in the hope that it will be useful,
      12             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14             :  * GNU General Public License for more details.
      15             :  *
      16             :  * You should have received a copy of the GNU General Public License
      17             :  * along with this program; if not, see <https://www.gnu.org/licenses/>.
      18             :  */
      19             : 
      20             : #include <config.h>
      21             : #include <errno.h>
      22             : #include <stdio.h>
      23             : #include <stdlib.h>
      24             : #include <string.h>
      25             : #include <assert.h>
      26             : 
      27             : #include "i18n.h"
      28             : #include "gpgtar.h"
      29             : #include "../common/exectool.h"
      30             : #include "../common/ccparray.h"
      31             : 
      32             : 
      33             : 
      34             : static unsigned long long
      35        5930 : parse_xoctal (const void *data, size_t length, const char *filename)
      36             : {
      37        5930 :   const unsigned char *p = data;
      38             :   unsigned long long value;
      39             : 
      40        5930 :   if (!length)
      41           0 :     value = 0;
      42        5930 :   else if ( (*p & 0x80))
      43             :     {
      44             :       /* Binary format.  */
      45           0 :       value = (*p++ & 0x7f);
      46           0 :       while (--length)
      47             :         {
      48           0 :           value <<= 8;
      49           0 :           value |= *p++;
      50             :         }
      51             :     }
      52             :   else
      53             :     {
      54             :       /* Octal format  */
      55        5930 :       value = 0;
      56             :       /* Skip leading spaces and zeroes.  */
      57        5930 :       for (; length && (*p == ' ' || *p == '0'); length--, p++)
      58             :         ;
      59       72972 :       for (; length && *p; length--, p++)
      60             :         {
      61       30556 :           if (*p >= '0' && *p <= '7')
      62             :             {
      63       30556 :               value <<= 3;
      64       30556 :               value += (*p - '0');
      65             :             }
      66             :           else
      67             :             {
      68           0 :               log_error ("%s: invalid octal number encountered - assuming 0\n",
      69             :                          filename);
      70           0 :               value = 0;
      71           0 :               break;
      72             :             }
      73             :         }
      74             :     }
      75        5930 :   return value;
      76             : }
      77             : 
      78             : 
      79             : static tar_header_t
      80        1186 : parse_header (const void *record, const char *filename)
      81             : {
      82        1186 :   const struct ustar_raw_header *raw = record;
      83             :   size_t n, namelen, prefixlen;
      84             :   tar_header_t header;
      85             :   int use_prefix;
      86             : 
      87        2372 :   use_prefix = (!memcmp (raw->magic, "ustar", 5)
      88        1186 :                 && (raw->magic[5] == ' ' || !raw->magic[5]));
      89             : 
      90             : 
      91        1186 :   for (namelen=0; namelen < sizeof raw->name && raw->name[namelen]; namelen++)
      92             :     ;
      93        1186 :   if (namelen == sizeof raw->name)
      94           0 :     log_info ("%s: warning: name not terminated by a nul byte\n", filename);
      95       74979 :   for (n=namelen+1; n < sizeof raw->name; n++)
      96       73793 :     if (raw->name[n])
      97             :       {
      98           0 :         log_info ("%s: warning: garbage after name\n", filename);
      99           0 :         break;
     100             :       }
     101             : 
     102             : 
     103        1186 :   if (use_prefix && raw->prefix[0])
     104             :     {
     105           0 :       for (prefixlen=0; (prefixlen < sizeof raw->prefix
     106           0 :                          && raw->prefix[prefixlen]); prefixlen++)
     107             :         ;
     108           0 :       if (prefixlen == sizeof raw->prefix)
     109           0 :         log_info ("%s: warning: prefix not terminated by a nul byte\n",
     110             :                   filename);
     111           0 :       for (n=prefixlen+1; n < sizeof raw->prefix; n++)
     112           0 :         if (raw->prefix[n])
     113             :           {
     114           0 :             log_info ("%s: warning: garbage after prefix\n", filename);
     115           0 :             break;
     116             :           }
     117             :     }
     118             :   else
     119        1186 :     prefixlen = 0;
     120             : 
     121        1186 :   header = xtrycalloc (1, sizeof *header + prefixlen + 1 + namelen);
     122        1186 :   if (!header)
     123             :     {
     124           0 :       log_error ("%s: error allocating header: %s\n",
     125             :                  filename, gpg_strerror (gpg_error_from_syserror ()));
     126           0 :       return NULL;
     127             :     }
     128        1186 :   if (prefixlen)
     129             :     {
     130           0 :       n = prefixlen;
     131           0 :       memcpy (header->name, raw->prefix, n);
     132           0 :       if (raw->prefix[n-1] != '/')
     133           0 :         header->name[n++] = '/';
     134             :     }
     135             :   else
     136        1186 :     n = 0;
     137        1186 :   memcpy (header->name+n, raw->name, namelen);
     138        1186 :   header->name[n+namelen] = 0;
     139             : 
     140        1186 :   header->mode  = parse_xoctal (raw->mode, sizeof raw->mode, filename);
     141        1186 :   header->uid   = parse_xoctal (raw->uid, sizeof raw->uid, filename);
     142        1186 :   header->gid   = parse_xoctal (raw->gid, sizeof raw->gid, filename);
     143        1186 :   header->size  = parse_xoctal (raw->size, sizeof raw->size, filename);
     144        1186 :   header->mtime = parse_xoctal (raw->mtime, sizeof raw->mtime, filename);
     145             :   /* checksum = */
     146        1186 :   switch (raw->typeflag[0])
     147             :     {
     148        1121 :     case '0': header->typeflag = TF_REGULAR; break;
     149           0 :     case '1': header->typeflag = TF_HARDLINK; break;
     150           0 :     case '2': header->typeflag = TF_SYMLINK; break;
     151           0 :     case '3': header->typeflag = TF_CHARDEV; break;
     152           0 :     case '4': header->typeflag = TF_BLOCKDEV; break;
     153          65 :     case '5': header->typeflag = TF_DIRECTORY; break;
     154           0 :     case '6': header->typeflag = TF_FIFO; break;
     155           0 :     case '7': header->typeflag = TF_RESERVED; break;
     156           0 :     default:  header->typeflag = TF_UNKNOWN; break;
     157             :     }
     158             : 
     159             : 
     160             :   /* Compute the number of data records following this header.  */
     161        1186 :   if (header->typeflag == TF_REGULAR || header->typeflag == TF_UNKNOWN)
     162        1121 :     header->nrecords = (header->size + RECORDSIZE-1)/RECORDSIZE;
     163             :   else
     164          65 :     header->nrecords = 0;
     165             : 
     166             : 
     167        1186 :   return header;
     168             : }
     169             : 
     170             : 
     171             : 
     172             : /* Read the next block, assming it is a tar header.  Returns a header
     173             :    object on success in R_HEADER, or an error.  If the stream is
     174             :    consumed, R_HEADER is set to NULL.  In case of an error an error
     175             :    message has been printed.  */
     176             : static gpg_error_t
     177        1239 : read_header (estream_t stream, tar_header_t *r_header)
     178             : {
     179             :   gpg_error_t err;
     180             :   char record[RECORDSIZE];
     181             :   int i;
     182             : 
     183        1239 :   err = read_record (stream, record);
     184        1239 :   if (err)
     185           0 :     return err;
     186             : 
     187        1239 :   for (i=0; i < RECORDSIZE && !record[i]; i++)
     188             :     ;
     189        1239 :   if (i == RECORDSIZE)
     190             :     {
     191             :       /* All zero header - check whether it is the first part of an
     192             :          end of archive mark.  */
     193          53 :       err = read_record (stream, record);
     194          53 :       if (err)
     195           0 :         return err;
     196             : 
     197          53 :       for (i=0; i < RECORDSIZE && !record[i]; i++)
     198             :         ;
     199          53 :       if (i != RECORDSIZE)
     200           0 :         log_info ("%s: warning: skipping empty header\n",
     201             :                   es_fname_get (stream));
     202             :       else
     203             :         {
     204             :           /* End of archive - FIXME: we might want to check for garbage.  */
     205          53 :           *r_header = NULL;
     206          53 :           return 0;
     207             :         }
     208             :     }
     209             : 
     210        1186 :   *r_header = parse_header (record, es_fname_get (stream));
     211        1186 :   return *r_header ? 0 : gpg_error_from_syserror ();
     212             : }
     213             : 
     214             : 
     215             : /* Skip the data records according to HEADER.  Prints an error message
     216             :    on error and return -1. */
     217             : static int
     218          56 : skip_data (estream_t stream, tar_header_t header)
     219             : {
     220             :   char record[RECORDSIZE];
     221             :   unsigned long long n;
     222             : 
     223        2032 :   for (n=0; n < header->nrecords; n++)
     224             :     {
     225        1976 :       if (read_record (stream, record))
     226           0 :         return -1;
     227             :     }
     228             : 
     229          56 :   return 0;
     230             : }
     231             : 
     232             : 
     233             : 
     234             : static void
     235          56 : print_header (tar_header_t header, estream_t out)
     236             : {
     237             :   unsigned long mask;
     238             :   char modestr[10+1];
     239             :   int i;
     240             : 
     241          56 :   *modestr = '?';
     242          56 :   switch (header->typeflag)
     243             :     {
     244          56 :     case TF_REGULAR:  *modestr = '-'; break;
     245           0 :     case TF_HARDLINK: *modestr = 'h'; break;
     246           0 :     case TF_SYMLINK:  *modestr = 'l'; break;
     247           0 :     case TF_CHARDEV:  *modestr = 'c'; break;
     248           0 :     case TF_BLOCKDEV: *modestr = 'b'; break;
     249           0 :     case TF_DIRECTORY:*modestr = 'd'; break;
     250           0 :     case TF_FIFO:     *modestr = 'f'; break;
     251           0 :     case TF_RESERVED: *modestr = '='; break;
     252           0 :     case TF_UNKNOWN:  break;
     253           0 :     case TF_NOTSUP:   break;
     254             :     }
     255         560 :   for (mask = 0400, i = 0; i < 9; i++, mask >>= 1)
     256         504 :     modestr[1+i] = (header->mode & mask)? "rwxrwxrwx"[i]:'-';
     257          56 :   if ((header->typeflag & 04000))
     258           0 :     modestr[3] = modestr[3] == 'x'? 's':'S';
     259          56 :   if ((header->typeflag & 02000))
     260           0 :     modestr[6] = modestr[6] == 'x'? 's':'S';
     261          56 :   if ((header->typeflag & 01000))
     262           0 :     modestr[9] = modestr[9] == 'x'? 't':'T';
     263          56 :   modestr[10] = 0;
     264             : 
     265          56 :   es_fprintf (out, "%s %lu %lu/%lu %12llu %s %s\n",
     266             :               modestr, header->nlink, header->uid, header->gid, header->size,
     267          56 :               isotimestamp (header->mtime), header->name);
     268          56 : }
     269             : 
     270             : 
     271             : 
     272             : /* List the tarball FILENAME or, if FILENAME is NULL, the tarball read
     273             :    from stdin.  */
     274             : gpg_error_t
     275           8 : gpgtar_list (const char *filename, int decrypt)
     276             : {
     277             :   gpg_error_t err;
     278             :   estream_t stream;
     279           8 :   estream_t cipher_stream = NULL;
     280           8 :   tar_header_t header = NULL;
     281             : 
     282           8 :   if (filename)
     283             :     {
     284           8 :       if (!strcmp (filename, "-"))
     285           0 :         stream = es_stdin;
     286             :       else
     287           8 :         stream = es_fopen (filename, "rb");
     288           8 :       if (!stream)
     289             :         {
     290           0 :           err = gpg_error_from_syserror ();
     291           0 :           log_error ("error opening '%s': %s\n", filename, gpg_strerror (err));
     292           0 :           return err;
     293             :         }
     294             :     }
     295             :   else
     296           0 :     stream = es_stdin;
     297             : 
     298           8 :   if (stream == es_stdin)
     299           0 :     es_set_binary (es_stdin);
     300             : 
     301           8 :   if (decrypt)
     302             :     {
     303             :       strlist_t arg;
     304             :       ccparray_t ccp;
     305             :       const char **argv;
     306             : 
     307           6 :       cipher_stream = stream;
     308           6 :       stream = es_fopenmem (0, "rwb");
     309           6 :       if (! stream)
     310             :         {
     311           0 :           err = gpg_error_from_syserror ();
     312           0 :           goto leave;
     313             :         }
     314             : 
     315           6 :       ccparray_init (&ccp, 0);
     316             : 
     317           6 :       ccparray_put (&ccp, "--decrypt");
     318          21 :       for (arg = opt.gpg_arguments; arg; arg = arg->next)
     319          15 :         ccparray_put (&ccp, arg->d);
     320             : 
     321           6 :       ccparray_put (&ccp, NULL);
     322           6 :       argv = ccparray_get (&ccp, NULL);
     323           6 :       if (!argv)
     324             :         {
     325           0 :           err = gpg_error_from_syserror ();
     326           0 :           goto leave;
     327             :         }
     328             : 
     329           6 :       err = gnupg_exec_tool_stream (opt.gpg_program, argv,
     330             :                                     cipher_stream, NULL, stream, NULL, NULL);
     331           6 :       xfree (argv);
     332           6 :       if (err)
     333           0 :         goto leave;
     334             : 
     335           6 :       err = es_fseek (stream, 0, SEEK_SET);
     336           6 :       if (err)
     337           0 :         goto leave;
     338             :     }
     339             : 
     340             :   for (;;)
     341             :     {
     342          64 :       err = read_header (stream, &header);
     343          64 :       if (err || header == NULL)
     344             :         goto leave;
     345             : 
     346          56 :       print_header (header, es_stdout);
     347             : 
     348          56 :       if (skip_data (stream, header))
     349           0 :         goto leave;
     350          56 :       xfree (header);
     351          56 :       header = NULL;
     352          56 :     }
     353             : 
     354             : 
     355             :  leave:
     356           8 :   xfree (header);
     357           8 :   if (stream != es_stdin)
     358           8 :     es_fclose (stream);
     359           8 :   if (stream != cipher_stream)
     360           8 :     es_fclose (cipher_stream);
     361           8 :   return err;
     362             : }
     363             : 
     364             : gpg_error_t
     365        1175 : gpgtar_read_header (estream_t stream, tar_header_t *r_header)
     366             : {
     367        1175 :   return read_header (stream, r_header);
     368             : }
     369             : 
     370             : void
     371           0 : gpgtar_print_header (tar_header_t header, estream_t out)
     372             : {
     373           0 :   if (header && out)
     374           0 :     print_header (header, out);
     375           0 : }

Generated by: LCOV version 1.11