Imported Upstream version 2.3.0
[platform/upstream/gpg2.git] / tools / gpgtar-list.c
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 "../common/i18n.h"
28 #include "gpgtar.h"
29 #include "../common/exectool.h"
30 #include "../common/ccparray.h"
31
32
33 \f
34 static unsigned long long
35 parse_xoctal (const void *data, size_t length, const char *filename)
36 {
37   const unsigned char *p = data;
38   unsigned long long value;
39
40   if (!length)
41     value = 0;
42   else if ( (*p & 0x80))
43     {
44       /* Binary format.  */
45       value = (*p++ & 0x7f);
46       while (--length)
47         {
48           value <<= 8;
49           value |= *p++;
50         }
51     }
52   else
53     {
54       /* Octal format  */
55       value = 0;
56       /* Skip leading spaces and zeroes.  */
57       for (; length && (*p == ' ' || *p == '0'); length--, p++)
58         ;
59       for (; length && *p; length--, p++)
60         {
61           if (*p >= '0' && *p <= '7')
62             {
63               value <<= 3;
64               value += (*p - '0');
65             }
66           else
67             {
68               log_error ("%s: invalid octal number encountered - assuming 0\n",
69                          filename);
70               value = 0;
71               break;
72             }
73         }
74     }
75   return value;
76 }
77
78
79 static tar_header_t
80 parse_header (const void *record, const char *filename, tarinfo_t info)
81 {
82   const struct ustar_raw_header *raw = record;
83   size_t n, namelen, prefixlen;
84   tar_header_t header;
85   int use_prefix;
86   int anyerror = 0;
87
88   info->headerblock = info->nblocks - 1;
89
90   use_prefix = (!memcmp (raw->magic, "ustar", 5)
91                 && (raw->magic[5] == ' ' || !raw->magic[5]));
92
93
94   for (namelen=0; namelen < sizeof raw->name && raw->name[namelen]; namelen++)
95     ;
96   if (namelen == sizeof raw->name)
97     {
98       log_info ("%s: warning: name not terminated by a nul\n", filename);
99       anyerror = 1;
100     }
101   for (n=namelen+1; n < sizeof raw->name; n++)
102     if (raw->name[n])
103       {
104         log_info ("%s: warning: garbage after name\n", filename);
105         anyerror = 1;
106         break;
107       }
108
109   if (use_prefix && raw->prefix[0])
110     {
111       for (prefixlen=0; (prefixlen < sizeof raw->prefix
112                          && raw->prefix[prefixlen]); prefixlen++)
113         ;
114       if (prefixlen == sizeof raw->prefix)
115         log_info ("%s: warning: prefix not terminated by a nul (block %llu)\n",
116                   filename, info->headerblock);
117       for (n=prefixlen+1; n < sizeof raw->prefix; n++)
118         if (raw->prefix[n])
119           {
120             log_info ("%s: warning: garbage after prefix\n", filename);
121             anyerror = 1;
122             break;
123           }
124     }
125   else
126     prefixlen = 0;
127
128   header = xtrycalloc (1, sizeof *header + prefixlen + 1 + namelen);
129   if (!header)
130     {
131       log_error ("%s: error allocating header: %s\n",
132                  filename, gpg_strerror (gpg_error_from_syserror ()));
133       return NULL;
134     }
135   if (prefixlen)
136     {
137       n = prefixlen;
138       memcpy (header->name, raw->prefix, n);
139       if (raw->prefix[n-1] != '/')
140         header->name[n++] = '/';
141     }
142   else
143     n = 0;
144   memcpy (header->name+n, raw->name, namelen);
145   header->name[n+namelen] = 0;
146
147   header->mode  = parse_xoctal (raw->mode, sizeof raw->mode, filename);
148   header->uid   = parse_xoctal (raw->uid, sizeof raw->uid, filename);
149   header->gid   = parse_xoctal (raw->gid, sizeof raw->gid, filename);
150   header->size  = parse_xoctal (raw->size, sizeof raw->size, filename);
151   header->mtime = parse_xoctal (raw->mtime, sizeof raw->mtime, filename);
152   /* checksum = */
153   switch (raw->typeflag[0])
154     {
155     case '0': header->typeflag = TF_REGULAR; break;
156     case '1': header->typeflag = TF_HARDLINK; break;
157     case '2': header->typeflag = TF_SYMLINK; break;
158     case '3': header->typeflag = TF_CHARDEV; break;
159     case '4': header->typeflag = TF_BLOCKDEV; break;
160     case '5': header->typeflag = TF_DIRECTORY; break;
161     case '6': header->typeflag = TF_FIFO; break;
162     case '7': header->typeflag = TF_RESERVED; break;
163     default:  header->typeflag = TF_UNKNOWN; break;
164     }
165
166   /* Compute the number of data records following this header.  */
167   if (header->typeflag == TF_REGULAR || header->typeflag == TF_UNKNOWN)
168     header->nrecords = (header->size + RECORDSIZE-1)/RECORDSIZE;
169   else
170     header->nrecords = 0;
171
172   if (anyerror)
173     {
174       log_info ("%s: header block %llu is corrupt"
175                 " (size=%llu type=%d nrec=%llu)\n",
176                 filename, info->headerblock,
177                 header->size, header->typeflag, header->nrecords);
178       /* log_printhex (record, RECORDSIZE, " "); */
179     }
180
181   return header;
182 }
183
184
185 \f
186 /* Read the next block, assuming it is a tar header.  Returns a header
187    object on success in R_HEADER, or an error.  If the stream is
188    consumed, R_HEADER is set to NULL.  In case of an error an error
189    message has been printed.  */
190 static gpg_error_t
191 read_header (estream_t stream, tarinfo_t info, tar_header_t *r_header)
192 {
193   gpg_error_t err;
194   char record[RECORDSIZE];
195   int i;
196
197   err = read_record (stream, record);
198   if (err)
199     return err;
200   info->nblocks++;
201
202   for (i=0; i < RECORDSIZE && !record[i]; i++)
203     ;
204   if (i == RECORDSIZE)
205     {
206       /* All zero header - check whether it is the first part of an
207          end of archive mark.  */
208       err = read_record (stream, record);
209       if (err)
210         return err;
211       info->nblocks++;
212
213       for (i=0; i < RECORDSIZE && !record[i]; i++)
214         ;
215       if (i != RECORDSIZE)
216         log_info ("%s: warning: skipping empty header\n",
217                   es_fname_get (stream));
218       else
219         {
220           /* End of archive - FIXME: we might want to check for garbage.  */
221           *r_header = NULL;
222           return 0;
223         }
224     }
225
226   *r_header = parse_header (record, es_fname_get (stream), info);
227   return *r_header ? 0 : gpg_error_from_syserror ();
228 }
229
230
231 /* Skip the data records according to HEADER.  Prints an error message
232    on error and return -1. */
233 static int
234 skip_data (estream_t stream, tarinfo_t info, tar_header_t header)
235 {
236   char record[RECORDSIZE];
237   unsigned long long n;
238
239   for (n=0; n < header->nrecords; n++)
240     {
241       if (read_record (stream, record))
242         return -1;
243       info->nblocks++;
244     }
245
246   return 0;
247 }
248
249
250 \f
251 static void
252 print_header (tar_header_t header, estream_t out)
253 {
254   unsigned long mask;
255   char modestr[10+1];
256   int i;
257
258   *modestr = '?';
259   switch (header->typeflag)
260     {
261     case TF_REGULAR:  *modestr = '-'; break;
262     case TF_HARDLINK: *modestr = 'h'; break;
263     case TF_SYMLINK:  *modestr = 'l'; break;
264     case TF_CHARDEV:  *modestr = 'c'; break;
265     case TF_BLOCKDEV: *modestr = 'b'; break;
266     case TF_DIRECTORY:*modestr = 'd'; break;
267     case TF_FIFO:     *modestr = 'f'; break;
268     case TF_RESERVED: *modestr = '='; break;
269     case TF_UNKNOWN:  break;
270     case TF_NOTSUP:   break;
271     }
272   for (mask = 0400, i = 0; i < 9; i++, mask >>= 1)
273     modestr[1+i] = (header->mode & mask)? "rwxrwxrwx"[i]:'-';
274   if ((header->typeflag & 04000))
275     modestr[3] = modestr[3] == 'x'? 's':'S';
276   if ((header->typeflag & 02000))
277     modestr[6] = modestr[6] == 'x'? 's':'S';
278   if ((header->typeflag & 01000))
279     modestr[9] = modestr[9] == 'x'? 't':'T';
280   modestr[10] = 0;
281
282   es_fprintf (out, "%s %lu %lu/%lu %12llu %s %s\n",
283               modestr, header->nlink, header->uid, header->gid, header->size,
284               isotimestamp (header->mtime), header->name);
285 }
286
287
288 \f
289 /* List the tarball FILENAME or, if FILENAME is NULL, the tarball read
290    from stdin.  */
291 gpg_error_t
292 gpgtar_list (const char *filename, int decrypt)
293 {
294   gpg_error_t err;
295   estream_t stream;
296   estream_t cipher_stream = NULL;
297   tar_header_t header = NULL;
298   struct tarinfo_s tarinfo_buffer;
299   tarinfo_t tarinfo = &tarinfo_buffer;
300
301   memset (&tarinfo_buffer, 0, sizeof tarinfo_buffer);
302
303   if (filename)
304     {
305       if (!strcmp (filename, "-"))
306         stream = es_stdin;
307       else
308         stream = es_fopen (filename, "rb");
309       if (!stream)
310         {
311           err = gpg_error_from_syserror ();
312           log_error ("error opening '%s': %s\n", filename, gpg_strerror (err));
313           return err;
314         }
315     }
316   else
317     stream = es_stdin;
318
319   if (stream == es_stdin)
320     es_set_binary (es_stdin);
321
322   if (decrypt)
323     {
324       strlist_t arg;
325       ccparray_t ccp;
326       const char **argv;
327
328       cipher_stream = stream;
329       stream = es_fopenmem (0, "rwb");
330       if (! stream)
331         {
332           err = gpg_error_from_syserror ();
333           goto leave;
334         }
335
336       ccparray_init (&ccp, 0);
337
338       ccparray_put (&ccp, "--decrypt");
339       for (arg = opt.gpg_arguments; arg; arg = arg->next)
340         ccparray_put (&ccp, arg->d);
341
342       ccparray_put (&ccp, NULL);
343       argv = ccparray_get (&ccp, NULL);
344       if (!argv)
345         {
346           err = gpg_error_from_syserror ();
347           goto leave;
348         }
349
350       err = gnupg_exec_tool_stream (opt.gpg_program, argv,
351                                     cipher_stream, NULL, stream, NULL, NULL);
352       xfree (argv);
353       if (err)
354         goto leave;
355
356       err = es_fseek (stream, 0, SEEK_SET);
357       if (err)
358         goto leave;
359     }
360
361   for (;;)
362     {
363       err = read_header (stream, tarinfo, &header);
364       if (err || header == NULL)
365         goto leave;
366
367       print_header (header, es_stdout);
368
369       if (skip_data (stream, tarinfo, header))
370         goto leave;
371       xfree (header);
372       header = NULL;
373     }
374
375
376  leave:
377   xfree (header);
378   if (stream != es_stdin)
379     es_fclose (stream);
380   if (stream != cipher_stream)
381     es_fclose (cipher_stream);
382   return err;
383 }
384
385 gpg_error_t
386 gpgtar_read_header (estream_t stream, tarinfo_t info, tar_header_t *r_header)
387 {
388   return read_header (stream, info, r_header);
389 }
390
391 void
392 gpgtar_print_header (tar_header_t header, estream_t out)
393 {
394   if (header && out)
395     print_header (header, out);
396 }