1
/* gpgtar-extract.c - Extract from a TAR archive
2
* Copyright (C) 2010 Free Software Foundation, Inc.
4
* This file is part of GnuPG.
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.
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.
16
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, see <http://www.gnu.org/licenses/>.
25
#include <sys/types.h>
29
#ifdef HAVE_W32_SYSTEM
30
# include <fcntl.h> /* for setmode() */
31
#endif /*HAVE_W32_SYSTEM*/
34
#include "../common/sysutils.h"
37
#ifndef GPG_ERR_LIMIT_REACHED
38
#define GPG_ERR_LIMIT_REACHED 183
43
extract_regular (estream_t stream, const char *dirname,
47
char record[RECORDSIZE];
48
size_t n, nbytes, nwritten;
50
estream_t outfp = NULL;
52
fname = strconcat (dirname, "/", hdr->name, NULL);
55
err = gpg_error_from_syserror ();
56
log_error ("error creating filename: %s\n", gpg_strerror (err));
62
outfp = es_fopen (fname, "wb");
65
err = gpg_error_from_syserror ();
66
log_error ("error creating `%s': %s\n", fname, gpg_strerror (err));
70
for (n=0; n < hdr->nrecords;)
72
err = read_record (stream, record);
76
nbytes = (n < hdr->nrecords)? RECORDSIZE : (hdr->size % RECORDSIZE);
77
nwritten = es_fwrite (record, 1, nbytes, outfp);
78
if (nwritten != nbytes)
80
err = gpg_error_from_syserror ();
81
log_error ("error writing `%s': %s\n", fname, gpg_strerror (err));
85
/* Fixme: Set permissions etc. */
88
if (!err && opt.verbose)
89
log_info ("extracted `%s'\n", fname);
91
if (err && fname && outfp)
94
log_error ("error removing incomplete file `%s': %s\n",
95
fname, gpg_strerror (gpg_error_from_syserror ()));
103
extract_directory (const char *dirname, tar_header_t hdr)
109
prefixlen = strlen (dirname) + 1;
110
fname = strconcat (dirname, "/", hdr->name, NULL);
113
err = gpg_error_from_syserror ();
114
log_error ("error creating filename: %s\n", gpg_strerror (err));
120
if (fname[strlen (fname)-1] == '/')
121
fname[strlen (fname)-1] = 0;
123
/* Note that we don't need to care about EEXIST because we always
124
extract into a new hierarchy. */
125
if (gnupg_mkdir (fname, "-rwx------"))
127
err = gpg_error_from_syserror ();
128
if (gpg_err_code (err) == GPG_ERR_ENOENT)
130
/* Try to create the directory with parents but keep the
131
original error code in case of a failure. */
135
for (p = fname+prefixlen; (p = strchr (p, '/')); p++)
138
rc = gnupg_mkdir (fname, "-rwx------");
143
if (!rc && !gnupg_mkdir (fname, "-rwx------"))
147
log_error ("error creating directory `%s': %s\n",
148
fname, gpg_strerror (err));
152
if (!err && opt.verbose)
153
log_info ("created `%s/'\n", fname);
160
extract (estream_t stream, const char *dirname, tar_header_t hdr)
165
n = strlen (hdr->name);
166
#ifdef HAVE_DOSISH_SYSTEM
167
if (strchr (hdr->name, '\\'))
169
log_error ("filename `%s' contains a backslash - "
170
"can't extract on this system\n", hdr->name);
171
return gpg_error (GPG_ERR_INV_NAME);
173
#endif /*HAVE_DOSISH_SYSTEM*/
176
|| strstr (hdr->name, "//")
177
|| strstr (hdr->name, "/../")
178
|| !strncmp (hdr->name, "../", 3)
179
|| (n >= 3 && !strcmp (hdr->name+n-3, "/.." )))
181
log_error ("filename `%s' as suspicious parts - not extracting\n",
183
return gpg_error (GPG_ERR_INV_NAME);
186
if (hdr->typeflag == TF_REGULAR || hdr->typeflag == TF_UNKNOWN)
187
err = extract_regular (stream, dirname, hdr);
188
else if (hdr->typeflag == TF_DIRECTORY)
189
err = extract_directory (dirname, hdr);
192
char record[RECORDSIZE];
194
log_info ("unsupported file type %d for `%s' - skipped\n",
195
(int)hdr->typeflag, hdr->name);
196
for (err = 0, n=0; !err && n < hdr->nrecords; n++)
197
err = read_record (stream, record);
203
/* Create a new directory to be used for extracting the tarball.
204
Returns the name of the directory which must be freed by the
205
caller. In case of an error a diagnostic is printed and NULL
208
create_directory (const char *dirprefix)
211
char *prefix_buffer = NULL;
212
char *dirname = NULL;
216
/* Remove common suffixes. */
217
n = strlen (dirprefix);
218
if (n > 4 && (!compare_filenames (dirprefix + n - 4, EXTSEP_S "gpg")
219
|| !compare_filenames (dirprefix + n - 4, EXTSEP_S "pgp")
220
|| !compare_filenames (dirprefix + n - 4, EXTSEP_S "asc")
221
|| !compare_filenames (dirprefix + n - 4, EXTSEP_S "pem")
222
|| !compare_filenames (dirprefix + n - 4, EXTSEP_S "p7m")
223
|| !compare_filenames (dirprefix + n - 4, EXTSEP_S "p7e")))
225
prefix_buffer = xtrystrdup (dirprefix);
228
err = gpg_error_from_syserror ();
231
prefix_buffer[n-4] = 0;
232
dirprefix = prefix_buffer;
237
for (idx=1; idx < 5000; idx++)
240
dirname = xtryasprintf ("%s_%d_", dirprefix, idx);
243
err = gpg_error_from_syserror ();
246
if (!gnupg_mkdir (dirname, "-rwx------"))
247
goto leave; /* Ready. */
248
if (errno != EEXIST && errno != ENOTDIR)
250
err = gpg_error_from_syserror ();
254
err = gpg_error (GPG_ERR_LIMIT_REACHED);
259
log_error ("error creating an extract directory: %s\n",
264
xfree (prefix_buffer);
271
gpgtar_extract (const char *filename)
275
tar_header_t header = NULL;
276
const char *dirprefix = NULL;
277
char *dirname = NULL;
281
if (!strcmp (filename, "-"))
284
stream = es_fopen (filename, "rb");
287
err = gpg_error_from_syserror ();
288
log_error ("error opening `%s': %s\n", filename, gpg_strerror (err));
295
#ifdef HAVE_DOSISH_SYSTEM
296
if (stream == es_stdin)
297
setmode (es_fileno (es_stdin), O_BINARY);
300
if (filename && stream != es_stdin)
302
dirprefix = strrchr (filename, '/');
306
dirprefix = filename;
308
else if (opt.filename)
310
dirprefix = strrchr (opt.filename, '/');
314
dirprefix = opt.filename;
317
if (!dirprefix || !*dirprefix)
318
dirprefix = "GPGARCH";
320
dirname = create_directory (dirprefix);
323
err = gpg_error (GPG_ERR_GENERAL);
328
log_info ("extracting to `%s/'\n", dirname);
332
header = gpgtar_read_header (stream);
336
if (extract (stream, dirname, header))
346
if (stream != es_stdin)