busybox/archival/bbunzip.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * Common code for gunzip-like applets
   4 *
   5 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
   6 */
   7#include "libbb.h"
   8#include "bb_archive.h"
   9
  10enum {
  11        OPT_STDOUT     = 1 << 0,
  12        OPT_FORCE      = 1 << 1,
  13        /* only some decompressors: */
  14        OPT_VERBOSE    = 1 << 2,
  15        OPT_DECOMPRESS = 1 << 3,
  16        OPT_TEST       = 1 << 4,
  17};
  18
  19static
  20int open_to_or_warn(int to_fd, const char *filename, int flags, int mode)
  21{
  22        int fd = open3_or_warn(filename, flags, mode);
  23        if (fd < 0) {
  24                return 1;
  25        }
  26        xmove_fd(fd, to_fd);
  27        return 0;
  28}
  29
  30char* FAST_FUNC append_ext(char *filename, const char *expected_ext)
  31{
  32        return xasprintf("%s.%s", filename, expected_ext);
  33}
  34
  35int FAST_FUNC bbunpack(char **argv,
  36        IF_DESKTOP(long long) int FAST_FUNC (*unpacker)(transformer_aux_data_t *aux),
  37        char* FAST_FUNC (*make_new_name)(char *filename, const char *expected_ext),
  38        const char *expected_ext
  39)
  40{
  41        struct stat stat_buf;
  42        IF_DESKTOP(long long) int status;
  43        char *filename, *new_name;
  44        smallint exitcode = 0;
  45        transformer_aux_data_t aux;
  46
  47        do {
  48                /* NB: new_name is *maybe* malloc'ed! */
  49                new_name = NULL;
  50                filename = *argv; /* can be NULL - 'streaming' bunzip2 */
  51
  52                if (filename && LONE_DASH(filename))
  53                        filename = NULL;
  54
  55                /* Open src */
  56                if (filename) {
  57                        if (stat(filename, &stat_buf) != 0) {
  58                                bb_simple_perror_msg(filename);
  59 err:
  60                                exitcode = 1;
  61                                goto free_name;
  62                        }
  63                        if (open_to_or_warn(STDIN_FILENO, filename, O_RDONLY, 0))
  64                                goto err;
  65                }
  66
  67                /* Special cases: test, stdout */
  68                if (option_mask32 & (OPT_STDOUT|OPT_TEST)) {
  69                        if (option_mask32 & OPT_TEST)
  70                                if (open_to_or_warn(STDOUT_FILENO, bb_dev_null, O_WRONLY, 0))
  71                                        goto err;
  72                        filename = NULL;
  73                }
  74
  75                /* Open dst if we are going to unpack to file */
  76                if (filename) {
  77                        new_name = make_new_name(filename, expected_ext);
  78                        if (!new_name) {
  79                                bb_error_msg("%s: unknown suffix - ignored", filename);
  80                                goto err;
  81                        }
  82
  83                        /* -f: overwrite existing output files */
  84                        if (option_mask32 & OPT_FORCE) {
  85                                unlink(new_name);
  86                        }
  87
  88                        /* O_EXCL: "real" bunzip2 doesn't overwrite files */
  89                        /* GNU gunzip does not bail out, but goes to next file */
  90                        if (open_to_or_warn(STDOUT_FILENO, new_name, O_WRONLY | O_CREAT | O_EXCL,
  91                                        stat_buf.st_mode))
  92                                goto err;
  93                }
  94
  95                /* Check that the input is sane */
  96                if (isatty(STDIN_FILENO) && (option_mask32 & OPT_FORCE) == 0) {
  97                        bb_error_msg_and_die("compressed data not read from terminal, "
  98                                        "use -f to force it");
  99                }
 100
 101                init_transformer_aux_data(&aux);
 102                aux.check_signature = 1;
 103                status = unpacker(&aux);
 104                if (status < 0)
 105                        exitcode = 1;
 106
 107                if (!(option_mask32 & OPT_STDOUT))
 108                        xclose(STDOUT_FILENO); /* with error check! */
 109
 110                if (filename) {
 111                        char *del = new_name;
 112                        if (status >= 0) {
 113                                /* TODO: restore other things? */
 114                                if (aux.mtime != 0) {
 115                                        struct timeval times[2];
 116
 117                                        times[1].tv_sec = times[0].tv_sec = aux.mtime;
 118                                        times[1].tv_usec = times[0].tv_usec = 0;
 119                                        /* Note: we closed it first.
 120                                         * On some systems calling utimes
 121                                         * then closing resets the mtime
 122                                         * back to current time. */
 123                                        utimes(new_name, times); /* ignoring errors */
 124                                }
 125
 126                                /* Delete _compressed_ file */
 127                                del = filename;
 128                                /* restore extension (unless tgz -> tar case) */
 129                                if (new_name == filename)
 130                                        filename[strlen(filename)] = '.';
 131                        }
 132                        xunlink(del);
 133
 134#if 0 /* Currently buggy - wrong name: "a.gz: 261% - replaced with a.gz" */
 135                        /* Extreme bloat for gunzip compat */
 136                        if (ENABLE_DESKTOP && (option_mask32 & OPT_VERBOSE) && status >= 0) {
 137                                fprintf(stderr, "%s: %u%% - replaced with %s\n",
 138                                        filename, (unsigned)(stat_buf.st_size*100 / (status+1)), new_name);
 139                        }
 140#endif
 141
 142 free_name:
 143                        if (new_name != filename)
 144                                free(new_name);
 145                }
 146        } while (*argv && *++argv);
 147
 148        if (option_mask32 & OPT_STDOUT)
 149                xclose(STDOUT_FILENO); /* with error check! */
 150
 151        return exitcode;
 152}
 153
 154#if ENABLE_UNCOMPRESS || ENABLE_BUNZIP2 || ENABLE_UNLZMA || ENABLE_UNXZ
 155static
 156char* FAST_FUNC make_new_name_generic(char *filename, const char *expected_ext)
 157{
 158        char *extension = strrchr(filename, '.');
 159        if (!extension || strcmp(extension + 1, expected_ext) != 0) {
 160                /* Mimic GNU gunzip - "real" bunzip2 tries to */
 161                /* unpack file anyway, to file.out */
 162                return NULL;
 163        }
 164        *extension = '\0';
 165        return filename;
 166}
 167#endif
 168
 169
 170/*
 171 * Uncompress applet for busybox (c) 2002 Glenn McGrath
 172 *
 173 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
 174 */
 175
 176//usage:#define uncompress_trivial_usage
 177//usage:       "[-cf] [FILE]..."
 178//usage:#define uncompress_full_usage "\n\n"
 179//usage:       "Decompress .Z file[s]\n"
 180//usage:     "\n        -c      Write to stdout"
 181//usage:     "\n        -f      Overwrite"
 182
 183#if ENABLE_UNCOMPRESS
 184static
 185IF_DESKTOP(long long) int FAST_FUNC unpack_uncompress(transformer_aux_data_t *aux)
 186{
 187        return unpack_Z_stream(aux, STDIN_FILENO, STDOUT_FILENO);
 188}
 189int uncompress_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 190int uncompress_main(int argc UNUSED_PARAM, char **argv)
 191{
 192        getopt32(argv, "cf");
 193        argv += optind;
 194
 195        return bbunpack(argv, unpack_uncompress, make_new_name_generic, "Z");
 196}
 197#endif
 198
 199
 200/*
 201 * Gzip implementation for busybox
 202 *
 203 * Based on GNU gzip v1.2.4 Copyright (C) 1992-1993 Jean-loup Gailly.
 204 *
 205 * Originally adjusted for busybox by Sven Rudolph <sr1@inf.tu-dresden.de>
 206 * based on gzip sources
 207 *
 208 * Adjusted further by Erik Andersen <andersen@codepoet.org> to support files as
 209 * well as stdin/stdout, and to generally behave itself wrt command line
 210 * handling.
 211 *
 212 * General cleanup to better adhere to the style guide and make use of standard
 213 * busybox functions by Glenn McGrath
 214 *
 215 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
 216 *
 217 * gzip (GNU zip) -- compress files with zip algorithm and 'compress' interface
 218 * Copyright (C) 1992-1993 Jean-loup Gailly
 219 * The unzip code was written and put in the public domain by Mark Adler.
 220 * Portions of the lzw code are derived from the public domain 'compress'
 221 * written by Spencer Thomas, Joe Orost, James Woods, Jim McKie, Steve Davies,
 222 * Ken Turkowski, Dave Mack and Peter Jannesen.
 223 *
 224 * See the license_msg below and the file COPYING for the software license.
 225 * See the file algorithm.doc for the compression algorithms and file formats.
 226 */
 227
 228//usage:#define gunzip_trivial_usage
 229//usage:       "[-cft] [FILE]..."
 230//usage:#define gunzip_full_usage "\n\n"
 231//usage:       "Decompress FILEs (or stdin)\n"
 232//usage:     "\n        -c      Write to stdout"
 233//usage:     "\n        -f      Force"
 234//usage:     "\n        -t      Test file integrity"
 235//usage:
 236//usage:#define gunzip_example_usage
 237//usage:       "$ ls -la /tmp/BusyBox*\n"
 238//usage:       "-rw-rw-r--    1 andersen andersen   557009 Apr 11 10:55 /tmp/BusyBox-0.43.tar.gz\n"
 239//usage:       "$ gunzip /tmp/BusyBox-0.43.tar.gz\n"
 240//usage:       "$ ls -la /tmp/BusyBox*\n"
 241//usage:       "-rw-rw-r--    1 andersen andersen  1761280 Apr 14 17:47 /tmp/BusyBox-0.43.tar\n"
 242//usage:
 243//usage:#define zcat_trivial_usage
 244//usage:       "FILE"
 245//usage:#define zcat_full_usage "\n\n"
 246//usage:       "Decompress to stdout"
 247
 248#if ENABLE_GUNZIP
 249static
 250char* FAST_FUNC make_new_name_gunzip(char *filename, const char *expected_ext UNUSED_PARAM)
 251{
 252        char *extension = strrchr(filename, '.');
 253
 254        if (!extension)
 255                return NULL;
 256
 257        extension++;
 258        if (strcmp(extension, "tgz" + 1) == 0
 259#if ENABLE_FEATURE_SEAMLESS_Z
 260         || (extension[0] == 'Z' && extension[1] == '\0')
 261#endif
 262        ) {
 263                extension[-1] = '\0';
 264        } else if (strcmp(extension, "tgz") == 0) {
 265                filename = xstrdup(filename);
 266                extension = strrchr(filename, '.');
 267                extension[2] = 'a';
 268                extension[3] = 'r';
 269        } else {
 270                return NULL;
 271        }
 272        return filename;
 273}
 274static
 275IF_DESKTOP(long long) int FAST_FUNC unpack_gunzip(transformer_aux_data_t *aux)
 276{
 277        return unpack_gz_stream(aux, STDIN_FILENO, STDOUT_FILENO);
 278}
 279/*
 280 * Linux kernel build uses gzip -d -n. We accept and ignore it.
 281 * Man page says:
 282 * -n --no-name
 283 * gzip: do not save the original file name and time stamp.
 284 * (The original name is always saved if the name had to be truncated.)
 285 * gunzip: do not restore the original file name/time even if present
 286 * (remove only the gzip suffix from the compressed file name).
 287 * This option is the default when decompressing.
 288 * -N --name
 289 * gzip: always save the original file name and time stamp (this is the default)
 290 * gunzip: restore the original file name and time stamp if present.
 291 */
 292int gunzip_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 293int gunzip_main(int argc UNUSED_PARAM, char **argv)
 294{
 295        getopt32(argv, "cfvdtn");
 296        argv += optind;
 297        /* if called as zcat */
 298        if (applet_name[1] == 'c')
 299                option_mask32 |= OPT_STDOUT;
 300
 301        return bbunpack(argv, unpack_gunzip, make_new_name_gunzip, /*unused:*/ NULL);
 302}
 303#endif
 304
 305
 306/*
 307 * Modified for busybox by Glenn McGrath
 308 * Added support output to stdout by Thomas Lundquist <thomasez@zelow.no>
 309 *
 310 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
 311 */
 312//usage:#define bunzip2_trivial_usage
 313//usage:       "[-cf] [FILE]..."
 314//usage:#define bunzip2_full_usage "\n\n"
 315//usage:       "Decompress FILEs (or stdin)\n"
 316//usage:     "\n        -c      Write to stdout"
 317//usage:     "\n        -f      Force"
 318//usage:#define bzcat_trivial_usage
 319//usage:       "FILE"
 320//usage:#define bzcat_full_usage "\n\n"
 321//usage:       "Decompress to stdout"
 322//applet:IF_BUNZIP2(APPLET(bunzip2, BB_DIR_USR_BIN, BB_SUID_DROP))
 323//applet:IF_BUNZIP2(APPLET_ODDNAME(bzcat, bunzip2, BB_DIR_USR_BIN, BB_SUID_DROP, bzcat))
 324#if ENABLE_BUNZIP2
 325static
 326IF_DESKTOP(long long) int FAST_FUNC unpack_bunzip2(transformer_aux_data_t *aux)
 327{
 328        return unpack_bz2_stream(aux, STDIN_FILENO, STDOUT_FILENO);
 329}
 330int bunzip2_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 331int bunzip2_main(int argc UNUSED_PARAM, char **argv)
 332{
 333        getopt32(argv, "cfvdt");
 334        argv += optind;
 335        if (applet_name[2] == 'c') /* bzcat */
 336                option_mask32 |= OPT_STDOUT;
 337
 338        return bbunpack(argv, unpack_bunzip2, make_new_name_generic, "bz2");
 339}
 340#endif
 341
 342
 343/*
 344 * Small lzma deflate implementation.
 345 * Copyright (C) 2006  Aurelien Jacobs <aurel@gnuage.org>
 346 *
 347 * Based on bunzip.c from busybox
 348 *
 349 * Licensed under GPLv2, see file LICENSE in this source tree.
 350 */
 351
 352//usage:#define unlzma_trivial_usage
 353//usage:       "[-cf] [FILE]..."
 354//usage:#define unlzma_full_usage "\n\n"
 355//usage:       "Decompress FILE (or stdin)\n"
 356//usage:     "\n        -c      Write to stdout"
 357//usage:     "\n        -f      Force"
 358//usage:
 359//usage:#define lzma_trivial_usage
 360//usage:       "-d [-cf] [FILE]..."
 361//usage:#define lzma_full_usage "\n\n"
 362//usage:       "Decompress FILE (or stdin)\n"
 363//usage:     "\n        -d      Decompress"
 364//usage:     "\n        -c      Write to stdout"
 365//usage:     "\n        -f      Force"
 366//usage:
 367//usage:#define lzcat_trivial_usage
 368//usage:       "FILE"
 369//usage:#define lzcat_full_usage "\n\n"
 370//usage:       "Decompress to stdout"
 371//usage:
 372//usage:#define unxz_trivial_usage
 373//usage:       "[-cf] [FILE]..."
 374//usage:#define unxz_full_usage "\n\n"
 375//usage:       "Decompress FILE (or stdin)\n"
 376//usage:     "\n        -c      Write to stdout"
 377//usage:     "\n        -f      Force"
 378//usage:
 379//usage:#define xz_trivial_usage
 380//usage:       "-d [-cf] [FILE]..."
 381//usage:#define xz_full_usage "\n\n"
 382//usage:       "Decompress FILE (or stdin)\n"
 383//usage:     "\n        -d      Decompress"
 384//usage:     "\n        -c      Write to stdout"
 385//usage:     "\n        -f      Force"
 386//usage:
 387//usage:#define xzcat_trivial_usage
 388//usage:       "FILE"
 389//usage:#define xzcat_full_usage "\n\n"
 390//usage:       "Decompress to stdout"
 391
 392#if ENABLE_UNLZMA
 393static
 394IF_DESKTOP(long long) int FAST_FUNC unpack_unlzma(transformer_aux_data_t *aux)
 395{
 396        return unpack_lzma_stream(aux, STDIN_FILENO, STDOUT_FILENO);
 397}
 398int unlzma_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 399int unlzma_main(int argc UNUSED_PARAM, char **argv)
 400{
 401        IF_LZMA(int opts =) getopt32(argv, "cfvdt");
 402# if ENABLE_LZMA
 403        /* lzma without -d or -t? */
 404        if (applet_name[2] == 'm' && !(opts & (OPT_DECOMPRESS|OPT_TEST)))
 405                bb_show_usage();
 406# endif
 407        /* lzcat? */
 408        if (applet_name[2] == 'c')
 409                option_mask32 |= OPT_STDOUT;
 410
 411        argv += optind;
 412        return bbunpack(argv, unpack_unlzma, make_new_name_generic, "lzma");
 413}
 414#endif
 415
 416
 417#if ENABLE_UNXZ
 418static
 419IF_DESKTOP(long long) int FAST_FUNC unpack_unxz(transformer_aux_data_t *aux)
 420{
 421        return unpack_xz_stream(aux, STDIN_FILENO, STDOUT_FILENO);
 422}
 423int unxz_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 424int unxz_main(int argc UNUSED_PARAM, char **argv)
 425{
 426        IF_XZ(int opts =) getopt32(argv, "cfvdt");
 427# if ENABLE_XZ
 428        /* xz without -d or -t? */
 429        if (applet_name[2] == '\0' && !(opts & (OPT_DECOMPRESS|OPT_TEST)))
 430                bb_show_usage();
 431# endif
 432        /* xzcat? */
 433        if (applet_name[2] == 'c')
 434                option_mask32 |= OPT_STDOUT;
 435
 436        argv += optind;
 437        return bbunpack(argv, unpack_unxz, make_new_name_generic, "xz");
 438}
 439#endif
 440