From f1fed3996a6c5b5f39ddb48cb5c88c09afe13fba Mon Sep 17 00:00:00 2001 From: Sergey Poznyakoff Date: Thu, 4 Nov 2010 16:09:25 +0200 Subject: [PATCH] Run alternative decompression programs if the principal one is not available. Some compression programs are able to handle various compression formats (e.g. `gzip' can decompress files created by `compress', `xz' is able to handle lzma, etc.) Tar tries to use such programs for decompression if the principal decompressor cannot be started. * src/buffer.c (compress_type): Swap ct_none and ct_tar. (archive_compression_type): New static variable. (zip_magic): Remove program and option fields. (zip_program): New structure and static. (compress_program): Remove macro. (find_zip_program): New static function. (first_decompress_program,next_decompress_program): New functions. (open_compressed_archive): Set archive_compression_type instead of use_compress_program_option. * src/common.h (first_decompress_program) (next_decompress_program): New functions. (WARN_DECOMPRESS_PROGRAM): New flag. (WARN_VERBOSE_WARNINGS): Include WARN_DECOMPRESS_PROGRAM. * src/warning.c (warning_args): Add "decompress-program". (warning_types): Add WARN_DECOMPRESS_PROGRAM. * src/system.c (run_decompress_program): New function. (sys_child_open_for_uncompress): Use run_decompress_program instead of calling execlp directly. --- doc/tar.texi | 35 +++++++++++++++++++ src/buffer.c | 95 ++++++++++++++++++++++++++++++++++++++++++++------- src/common.h | 7 +++- src/system.c | 31 +++++++++++++---- src/warning.c | 4 ++- 5 files changed, 152 insertions(+), 20 deletions(-) diff --git a/doc/tar.texi b/doc/tar.texi index 6d9d9cc..2c259ff 100644 --- a/doc/tar.texi +++ b/doc/tar.texi @@ -4166,6 +4166,23 @@ Disable all warning messages. @cindex @samp{Ignoring unknown extended header keyword `%s'}, warning message @item unknown-keyword @samp{Ignoring unknown extended header keyword `%s'} +@kwindex decompress-program +@item decompress-program +Controls verbose messages describing execution failures when trying +alternative decompressor programs (@pxref{alternative decompression +programs}). This warning is disabled by default (unless +@option{--verbose} is used). A common example of what you can get +when using this warning is: + +@smallexample +$ @kbd{tar --warning=decompress-program -x -f archive.Z} +tar (child): cannot run compress: No such file or directory +tar (child): trying gzip +@end smallexample + +This means that @command{tar} first tried to decompress +@file{archive.Z} using @command{compress}, and, when that +failed, switched to @command{gzip}. @end table @subheading Keywords controlling incremental extraction: @@ -8755,6 +8772,24 @@ certain compression formats. If this approach fails, @command{tar} falls back to using archive name suffix to determine its format (@pxref{auto-compress}, for a list of recognized suffixes). +@anchor{alternative decompression programs} +@cindex alternative decompression programs +Some compression programs are able to handle different compression +formats. @GNUTAR{} uses this, if the principal decompressor for the +given format is not available. For example, if @command{compress} is +not installed, @command{tar} will try to use @command{gzip}. As of +version @value{VERSION} the following alternatives are +tried@footnote{To verbosely trace the decompressor selection, use the +@option{--warning=decompress-program} option +(@pxref{warnings,decompress-program}).}: + +@multitable @columnfractions 0.3 0.3 0.3 +@headitem Format @tab Main decompressor @tab Alternatives +@item compress @tab compress @tab gzip +@item lzma @tab lzma @tab xz +@item bzip2 @tab bzip2 @tab lbzip2 +@end multitable + The only case when you have to specify a decompression option while reading the archive is when reading from a pipe or from a tape drive that does not support random access. However, in this case @GNUTAR{} diff --git a/src/buffer.c b/src/buffer.c index ef4bbac..e52b1b1 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -261,8 +261,8 @@ compute_duration () /* Compression detection */ enum compress_type { - ct_tar, /* Plain tar file */ ct_none, /* Unknown compression type */ + ct_tar, /* Plain tar file */ ct_compress, ct_gzip, ct_bzip2, @@ -272,31 +272,102 @@ enum compress_type { ct_xz }; +static enum compress_type archive_compression_type = ct_none; + struct zip_magic { enum compress_type type; size_t length; char const *magic; +}; + +struct zip_program +{ + enum compress_type type; char const *program; char const *option; }; static struct zip_magic const magic[] = { - { ct_tar }, { ct_none, }, - { ct_compress, 2, "\037\235", COMPRESS_PROGRAM, "-Z" }, - { ct_gzip, 2, "\037\213", GZIP_PROGRAM, "-z" }, - { ct_bzip2, 3, "BZh", BZIP2_PROGRAM, "-j" }, - { ct_lzip, 4, "LZIP", LZIP_PROGRAM, "--lzip" }, - { ct_lzma, 6, "\xFFLZMA", LZMA_PROGRAM, "--lzma" }, - { ct_lzop, 4, "\211LZO", LZOP_PROGRAM, "--lzop" }, - { ct_xz, 6, "\xFD" "7zXZ", XZ_PROGRAM, "-J" }, + { ct_tar }, + { ct_compress, 2, "\037\235" }, + { ct_gzip, 2, "\037\213" }, + { ct_bzip2, 3, "BZh" }, + { ct_lzip, 4, "LZIP" }, + { ct_lzma, 6, "\xFFLZMA" }, + { ct_lzop, 4, "\211LZO" }, + { ct_xz, 6, "\xFD" "7zXZ" }, }; #define NMAGIC (sizeof(magic)/sizeof(magic[0])) -#define compress_option(t) magic[t].option -#define compress_program(t) magic[t].program +static struct zip_program zip_program[] = { + { ct_compress, COMPRESS_PROGRAM, "-Z" }, + { ct_compress, GZIP_PROGRAM, "-z" }, + { ct_gzip, GZIP_PROGRAM, "-z" }, + { ct_bzip2, BZIP2_PROGRAM, "-j" }, + { ct_bzip2, "lbzip2", "-j" }, + { ct_lzip, LZIP_PROGRAM, "--lzip" }, + { ct_lzma, LZMA_PROGRAM, "--lzma" }, + { ct_lzma, XZ_PROGRAM, "-J" }, + { ct_lzop, LZOP_PROGRAM, "--lzop" }, + { ct_xz, XZ_PROGRAM, "-J" }, + { ct_none } +}; + +static struct zip_program const * +find_zip_program (enum compress_type type, int *pstate) +{ + int i; + + for (i = *pstate; zip_program[i].type != ct_none; i++) + { + if (zip_program[i].type == type) + { + *pstate = i + 1; + return zip_program + i; + } + } + *pstate = i; + return NULL; +} + +const char * +first_decompress_program (int *pstate) +{ + struct zip_program const *zp; + + if (use_compress_program_option) + return use_compress_program_option; + + if (archive_compression_type == ct_none) + return NULL; + + *pstate = 0; + zp = find_zip_program (archive_compression_type, pstate); + return zp ? zp->program : NULL; +} + +const char * +next_decompress_program (int *pstate) +{ + struct zip_program const *zp; + + if (use_compress_program_option) + return NULL; + zp = find_zip_program (archive_compression_type, pstate); + return zp ? zp->program : NULL; +} + +static const char * +compress_option (enum compress_type type) +{ + struct zip_program const *zp; + int i = 0; + zp = find_zip_program (type, &i); + return zp ? zp->option : NULL; +} /* Check if the file ARCHIVE is a compressed archive. */ static enum compress_type @@ -395,7 +466,7 @@ open_compressed_archive (void) break; default: - use_compress_program_option = compress_program (type); + archive_compression_type = type; break; } } diff --git a/src/common.h b/src/common.h index 4a63824..69097b3 100644 --- a/src/common.h +++ b/src/common.h @@ -440,6 +440,9 @@ void mv_size_left (off_t size); void buffer_write_global_xheader (void); +const char *first_decompress_program (int *pstate); +const char *next_decompress_program (int *pstate); + /* Module create.c. */ enum dump_status @@ -805,10 +808,12 @@ void checkpoint_run (bool do_write); #define WARN_UNKNOWN_CAST 0x00010000 #define WARN_UNKNOWN_KEYWORD 0x00020000 #define WARN_XDEV 0x00040000 +#define WARN_DECOMPRESS_PROGRAM 0x00080000 /* The warnings composing WARN_VERBOSE_WARNINGS are enabled by default in verbose mode */ -#define WARN_VERBOSE_WARNINGS (WARN_RENAME_DIRECTORY|WARN_NEW_DIRECTORY) +#define WARN_VERBOSE_WARNINGS (WARN_RENAME_DIRECTORY|WARN_NEW_DIRECTORY|\ + WARN_DECOMPRESS_PROGRAM) #define WARN_ALL (~WARN_VERBOSE_WARNINGS) void set_warning_option (const char *arg); diff --git a/src/system.c b/src/system.c index a11afd7..ba4ac2d 100644 --- a/src/system.c +++ b/src/system.c @@ -455,6 +455,29 @@ sys_child_open_for_compress (void) wait_for_grandchild (grandchild_pid); } +static void +run_decompress_program (void) +{ + int i; + const char *p, *prog = NULL; + + for (p = first_decompress_program (&i); p; p = next_decompress_program (&i)) + { + if (prog) + { + WARNOPT (WARN_DECOMPRESS_PROGRAM, + (0, errno, _("cannot run %s"), prog)); + WARNOPT (WARN_DECOMPRESS_PROGRAM, + (0, 0, _("trying %s"), p)); + } + prog = p; + execlp (p, p, "-d", NULL); + } + if (!prog) + FATAL_ERROR ((0, 0, _("unable to run decompression program"))); + exec_fatal (prog); +} + /* Set ARCHIVE for uncompressing, then reading an archive. */ pid_t sys_child_open_for_uncompress (void) @@ -501,9 +524,7 @@ sys_child_open_for_uncompress (void) open_fatal (archive_name_array[0]); xdup2 (archive, STDIN_FILENO); priv_set_restore_linkdir (); - execlp (use_compress_program_option, use_compress_program_option, - "-d", (char *) 0); - exec_fatal (use_compress_program_option); + run_decompress_program (); } /* We do need a grandchild tar. */ @@ -520,9 +541,7 @@ sys_child_open_for_uncompress (void) xdup2 (child_pipe[PREAD], STDIN_FILENO); xclose (child_pipe[PWRITE]); priv_set_restore_linkdir (); - execlp (use_compress_program_option, use_compress_program_option, - "-d", (char *) 0); - exec_fatal (use_compress_program_option); + run_decompress_program (); } /* The child tar is still here! */ diff --git a/src/warning.c b/src/warning.c index 4374a88..5d1bcab 100644 --- a/src/warning.c +++ b/src/warning.c @@ -41,6 +41,7 @@ static char const *const warning_args[] = { "unknown-cast", "unknown-keyword", "xdev", + "decompress-program", NULL }; @@ -64,7 +65,8 @@ static int warning_types[] = { WARN_TIMESTAMP, WARN_UNKNOWN_CAST, WARN_UNKNOWN_KEYWORD, - WARN_XDEV + WARN_XDEV, + WARN_DECOMPRESS_PROGRAM }; ARGMATCH_VERIFY (warning_args, warning_types); -- 2.45.2