1 /* gpgtar.c - A simple TAR implementation mainly useful for Windows.
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 <https://www.gnu.org/licenses/>.
20 /* GnuPG comes with a shell script gpg-zip which creates archive files
21 in the same format as PGP Zip, which is actually a USTAR format.
22 That is fine and works nicely on all Unices but for Windows we
23 don't have a compatible shell and the supply of tar programs is
24 limited. Given that we need just a few tar option and it is an
25 open question how many Unix concepts are to be mapped to Windows,
26 we might as well write our own little tar customized for use with
27 gpg. So here we go. */
40 #include "../common/openpgpdefs.h"
41 #include "../common/init.h"
42 #include "../common/strlist.h"
47 /* Constants to identify the commands and options. */
48 enum cmd_and_opt_values
76 /* Compatibility with gpg-zip. */
85 /* The list of commands and options. */
86 static ARGPARSE_OPTS opts[] = {
87 ARGPARSE_group (300, N_("@Commands:\n ")),
89 ARGPARSE_c (aCreate, "create", N_("create an archive")),
90 ARGPARSE_c (aExtract, "extract", N_("extract an archive")),
91 ARGPARSE_c (aEncrypt, "encrypt", N_("create an encrypted archive")),
92 ARGPARSE_c (aDecrypt, "decrypt", N_("extract an encrypted archive")),
93 ARGPARSE_c (aSign, "sign", N_("create a signed archive")),
94 ARGPARSE_c (aList, "list-archive", N_("list an archive")),
96 ARGPARSE_group (301, N_("@\nOptions:\n ")),
98 ARGPARSE_s_n (oSymmetric, "symmetric", N_("use symmetric encryption")),
99 ARGPARSE_s_s (oRecipient, "recipient", N_("|USER-ID|encrypt for USER-ID")),
100 ARGPARSE_s_s (oUser, "local-user",
101 N_("|USER-ID|use USER-ID to sign or decrypt")),
102 ARGPARSE_s_s (oOutput, "output", N_("|FILE|write output to FILE")),
103 ARGPARSE_s_n (oVerbose, "verbose", N_("verbose")),
104 ARGPARSE_s_n (oQuiet, "quiet", N_("be somewhat more quiet")),
105 ARGPARSE_s_s (oGpgProgram, "gpg", "@"),
106 ARGPARSE_s_n (oSkipCrypto, "skip-crypto", N_("skip the crypto processing")),
107 ARGPARSE_s_n (oDryRun, "dry-run", N_("do not make any changes")),
108 ARGPARSE_s_s (oSetFilename, "set-filename", "@"),
109 ARGPARSE_s_n (oOpenPGP, "openpgp", "@"),
110 ARGPARSE_s_n (oCMS, "cms", "@"),
112 ARGPARSE_group (302, N_("@\nTar options:\n ")),
114 ARGPARSE_s_s (oDirectory, "directory",
115 N_("|DIRECTORY|extract files into DIRECTORY")),
116 ARGPARSE_s_s (oFilesFrom, "files-from",
117 N_("|FILE|get names to create from FILE")),
118 ARGPARSE_s_n (oNull, "null", N_("-T reads null-terminated names")),
120 ARGPARSE_s_s (oGpgArgs, "gpg-args", "@"),
121 ARGPARSE_s_s (oTarArgs, "tar-args", "@"),
127 /* The list of commands and options for tar that we understand. */
128 static ARGPARSE_OPTS tar_opts[] = {
129 ARGPARSE_s_s (oDirectory, "directory",
130 N_("|DIRECTORY|extract files into DIRECTORY")),
131 ARGPARSE_s_s (oFilesFrom, "files-from",
132 N_("|FILE|get names to create from FILE")),
133 ARGPARSE_s_n (oNull, "null", N_("-T reads null-terminated names")),
140 /* Print usage information and and provide strings for help. */
142 my_strusage( int level )
148 case 11: p = "@GPGTAR@ (@GNUPG@)";
150 case 13: p = VERSION; break;
151 case 17: p = PRINTABLE_OS_NAME; break;
152 case 19: p = _("Please report bugs to <@EMAIL@>.\n"); break;
156 p = _("Usage: gpgtar [options] [files] [directories] (-h for help)");
159 p = _("Syntax: gpgtar [options] [files] [directories]\n"
160 "Encrypt or sign files into an archive\n");
163 default: p = NULL; break;
170 set_cmd (enum cmd_and_opt_values *ret_cmd, enum cmd_and_opt_values new_cmd)
172 enum cmd_and_opt_values cmd = *ret_cmd;
174 if (!cmd || cmd == new_cmd)
176 else if (cmd == aSign && new_cmd == aEncrypt)
178 else if (cmd == aEncrypt && new_cmd == aSign)
182 log_error (_("conflicting commands\n"));
189 /* Shell-like argument splitting.
191 For compatibility with gpg-zip we accept arguments for GnuPG and
192 tar given as a string argument to '--gpg-args' and '--tar-args'.
193 gpg-zip was implemented as a Bourne Shell script, and therefore, we
194 need to split the string the same way the shell would. */
196 shell_parse_stringlist (const char *str, strlist_t *r_list)
198 strlist_t list = NULL;
204 do { if (p - arg + 2 < sizeof arg) *p++ = (c); else return 1; } while (0)
205 #define addargument() \
210 append_to_strlist (&list, arg); \
216 #define singlequote '\''
217 #define doublequote '"'
226 else if (*s == singlequote || *s == doublequote)
233 if (*s == singlequote)
240 assert (s > str || !"cannot be quoted at first char");
241 if (*s == doublequote && *(s - 1) != '\\')
248 assert (! "reached");
252 /* Append the last argument. */
265 /* Like shell_parse_stringlist, but returns an argv vector
266 instead of a strlist. */
268 shell_parse_argv (const char *s, int *r_argc, char ***r_argv)
273 if (shell_parse_stringlist (s, &list))
276 *r_argc = strlist_length (list);
277 *r_argv = xtrycalloc (*r_argc, sizeof **r_argv);
281 for (i = 0; list; i++)
283 gpgrt_annotate_leaked_object (list);
284 (*r_argv)[i] = list->d;
287 gpgrt_annotate_leaked_object (*r_argv);
292 enum cmd_and_opt_values cmd = 0;
294 const char *files_from = NULL;
298 /* Command line parsing. */
300 parse_arguments (ARGPARSE_ARGS *pargs, ARGPARSE_OPTS *popts)
302 int no_more_options = 0;
304 while (!no_more_options && optfile_parse (NULL, NULL, NULL, pargs, popts))
306 switch (pargs->r_opt)
308 case oOutput: opt.outfile = pargs->r.ret_str; break;
309 case oDirectory: opt.directory = pargs->r.ret_str; break;
310 case oSetFilename: opt.filename = pargs->r.ret_str; break;
311 case oQuiet: opt.quiet = 1; break;
312 case oVerbose: opt.verbose++; break;
313 case oNoVerbose: opt.verbose = 0; break;
314 case oFilesFrom: files_from = pargs->r.ret_str; break;
315 case oNull: null_names = 1; break;
321 set_cmd (&cmd, pargs->r_opt);
325 set_cmd (&cmd, aEncrypt);
330 set_cmd (&cmd, aDecrypt);
335 add_to_strlist (&opt.recipients, pargs->r.ret_str);
339 opt.user = pargs->r.ret_str;
343 set_cmd (&cmd, aEncrypt);
348 opt.gpg_program = pargs->r.ret_str;
355 case oOpenPGP: /* Dummy option for now. */ break;
356 case oCMS: /* Dummy option for now. */ break;
361 if (shell_parse_stringlist (pargs->r.ret_str, &list))
362 log_error ("failed to parse gpg arguments '%s'\n",
366 if (opt.gpg_arguments)
367 strlist_last (opt.gpg_arguments)->next = list;
369 opt.gpg_arguments = list;
379 if (shell_parse_argv (pargs->r.ret_str, &tar_argc, &tar_argv))
380 log_error ("failed to parse tar arguments '%s'\n",
384 ARGPARSE_ARGS tar_args;
385 tar_args.argc = &tar_argc;
386 tar_args.argv = &tar_argv;
387 tar_args.flags = ARGPARSE_FLAG_ARG0;
388 parse_arguments (&tar_args, tar_opts);
390 log_error ("unsupported tar arguments '%s'\n",
392 pargs->err = tar_args.err;
401 default: pargs->err = 2; break;
409 main (int argc, char **argv)
415 assert (sizeof (struct ustar_raw_header) == 512);
417 gnupg_reopen_std (GPGTAR_NAME);
418 set_strusage (my_strusage);
419 log_set_prefix (GPGTAR_NAME, GPGRT_LOG_WITH_PREFIX);
421 /* Make sure that our subsystems are ready. */
423 init_common_subsystems (&argc, &argv);
425 /* Parse the command line. */
428 pargs.flags = ARGPARSE_FLAG_KEEP;
429 parse_arguments (&pargs, opts);
431 if ((files_from && !null_names) || (!files_from && null_names))
432 log_error ("--files-from and --null may only be used in conjunction\n");
433 if (files_from && strcmp (files_from, "-"))
434 log_error ("--files-from only supports argument \"-\"\n");
436 if (log_get_errorcount (0))
439 /* Print a warning if an argument looks like an option. */
440 if (!opt.quiet && !(pargs.flags & ARGPARSE_FLAG_STOP_SEEN))
444 for (i=0; i < argc; i++)
445 if (argv[i][0] == '-' && argv[i][1] == '-')
446 log_info (_("NOTE: '%s' is not considered an option\n"), argv[i]);
449 if (! opt.gpg_program)
450 opt.gpg_program = gnupg_module_name (GNUPG_MODULE_NAME_GPG);
453 opt.debug_level = 1024;
460 fname = argc ? *argv : NULL;
462 log_info ("note: ignoring option --set-filename\n");
464 log_info ("note: ignoring option --files-from\n");
465 err = gpgtar_list (fname, !skip_crypto);
466 if (err && log_get_errorcount (0) == 0)
467 log_error ("listing archive failed: %s\n", gpg_strerror (err));
473 if ((!argc && !null_names)
474 || (argc && null_names))
477 log_info ("note: ignoring option --set-filename\n");
478 err = gpgtar_create (null_names? NULL :argv,
480 && (cmd == aEncrypt || cmd == aSignEncrypt),
481 cmd == aSign || cmd == aSignEncrypt);
482 if (err && log_get_errorcount (0) == 0)
483 log_error ("creating archive failed: %s\n", gpg_strerror (err));
490 log_info ("note: ignoring option --output\n");
492 log_info ("note: ignoring option --files-from\n");
493 fname = argc ? *argv : NULL;
494 err = gpgtar_extract (fname, !skip_crypto);
495 if (err && log_get_errorcount (0) == 0)
496 log_error ("extracting archive failed: %s\n", gpg_strerror (err));
500 log_error (_("invalid command (there is no implicit command)\n"));
504 return log_get_errorcount (0)? 1:0;
508 /* Read the next record from STREAM. RECORD is a buffer provided by
509 the caller and must be at leadt of size RECORDSIZE. The function
510 return 0 on success and and error code on failure; a diagnostic
511 printed as well. Note that there is no need for an EOF indicator
512 because a tarball has an explicit EOF record. */
514 read_record (estream_t stream, void *record)
519 nread = es_fread (record, 1, RECORDSIZE, stream);
520 if (nread != RECORDSIZE)
522 err = gpg_error_from_syserror ();
523 if (es_ferror (stream))
524 log_error ("error reading '%s': %s\n",
525 es_fname_get (stream), gpg_strerror (err));
527 log_error ("error reading '%s': premature EOF "
528 "(size of last record: %zu)\n",
529 es_fname_get (stream), nread);
538 /* Write the RECORD of size RECORDSIZE to STREAM. FILENAME is the
539 name of the file used for diagnostics. */
541 write_record (estream_t stream, const void *record)
546 nwritten = es_fwrite (record, 1, RECORDSIZE, stream);
547 if (nwritten != RECORDSIZE)
549 err = gpg_error_from_syserror ();
550 log_error ("error writing '%s': %s\n",
551 es_fname_get (stream), gpg_strerror (err));
560 /* Return true if FP is an unarmored OpenPGP message. Note that this
561 function reads a few bytes from FP but pushes them back. */
564 openpgp_message_p (estream_t fp)
571 if (es_ungetc (ctb, fp))
572 log_fatal ("error ungetting first byte: %s\n",
573 gpg_strerror (gpg_error_from_syserror ()));
577 switch ((ctb & 0x40) ? (ctb & 0x3f) : ((ctb>>2)&0xf))
581 case PKT_ONEPASS_SIG:
585 case PKT_OLD_COMMENT:
589 return 1; /* Yes, this seems to be an OpenPGP message. */