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
34 static unsigned long long
parse_xoctal(const void * data,size_t length,const char * filename)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
parse_header(const void * record,const char * filename,tarinfo_t info)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
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
read_header(estream_t stream,tarinfo_t info,tar_header_t * r_header)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
skip_data(estream_t stream,tarinfo_t info,tar_header_t header)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
251 static void
print_header(tar_header_t header,estream_t out)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
289 /* List the tarball FILENAME or, if FILENAME is NULL, the tarball read
290 from stdin. */
291 gpg_error_t
gpgtar_list(const char * filename,int decrypt)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
gpgtar_read_header(estream_t stream,tarinfo_t info,tar_header_t * r_header)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
gpgtar_print_header(tar_header_t header,estream_t out)392 gpgtar_print_header (tar_header_t header, estream_t out)
393 {
394 if (header && out)
395 print_header (header, out);
396 }
397