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
  10/* lzop_main() uses bbunpack(), need this: */
  11//kbuild:lib-$(CONFIG_LZOP) += bbunzip.o
  12
  13/* Note: must be kept in sync with archival/lzop.c */
  14enum {
  15        OPT_STDOUT     = 1 << 0,
  16        OPT_FORCE      = 1 << 1,
  17        /* only some decompressors: */
  18        OPT_VERBOSE    = 1 << 2,
  19        OPT_QUIET      = 1 << 3,
  20        OPT_DECOMPRESS = 1 << 4,
  21        OPT_TEST       = 1 << 5,
  22        SEAMLESS_MAGIC = (1 << 31) * SEAMLESS_COMPRESSION,
  23};
  24
  25static
  26int open_to_or_warn(int to_fd, const char *filename, int flags, int mode)
  27{
  28        int fd = open3_or_warn(filename, flags, mode);
  29        if (fd < 0) {
  30                return 1;
  31        }
  32        xmove_fd(fd, to_fd);
  33        return 0;
  34}
  35
  36char* FAST_FUNC append_ext(char *filename, const char *expected_ext)
  37{
  38        return xasprintf("%s.%s", filename, expected_ext);
  39}
  40
  41int FAST_FUNC bbunpack(char **argv,
  42        IF_DESKTOP(long long) int FAST_FUNC (*unpacker)(transformer_state_t *xstate),
  43        char* FAST_FUNC (*make_new_name)(char *filename, const char *expected_ext),
  44        const char *expected_ext
  45)
  46{
  47        struct stat stat_buf;
  48        IF_DESKTOP(long long) int status = 0;
  49        char *filename, *new_name;
  50        smallint exitcode = 0;
  51        transformer_state_t xstate;
  52
  53        do {
  54                /* NB: new_name is *maybe* malloc'ed! */
  55                new_name = NULL;
  56                filename = *argv; /* can be NULL - 'streaming' bunzip2 */
  57
  58                if (filename && LONE_DASH(filename))
  59                        filename = NULL;
  60
  61                /* Open src */
  62                if (filename) {
  63                        if (!(option_mask32 & SEAMLESS_MAGIC)) {
  64                                if (stat(filename, &stat_buf) != 0) {
  65 err_name:
  66                                        bb_simple_perror_msg(filename);
  67 err:
  68                                        exitcode = 1;
  69                                        goto free_name;
  70                                }
  71                                if (open_to_or_warn(STDIN_FILENO, filename, O_RDONLY, 0))
  72                                        goto err;
  73                        } else {
  74                                /* "clever zcat" with FILE */
  75                                /* fail_if_not_compressed because zcat refuses uncompressed input */
  76                                int fd = open_zipped(filename, /*fail_if_not_compressed:*/ 1);
  77                                if (fd < 0)
  78                                        goto err_name;
  79                                xmove_fd(fd, STDIN_FILENO);
  80                        }
  81                } else
  82                if (option_mask32 & SEAMLESS_MAGIC) {
  83                        /* "clever zcat" on stdin */
  84                        if (setup_unzip_on_fd(STDIN_FILENO, /*fail_if_not_compressed*/ 1))
  85                                goto err;
  86                }
  87
  88                /* Special cases: test, stdout */
  89                if (option_mask32 & (OPT_STDOUT|OPT_TEST)) {
  90                        if (option_mask32 & OPT_TEST)
  91                                if (open_to_or_warn(STDOUT_FILENO, bb_dev_null, O_WRONLY, 0))
  92                                        xfunc_die();
  93                        filename = NULL;
  94                }
  95
  96                /* Open dst if we are going to unpack to file */
  97                if (filename) {
  98                        new_name = make_new_name(filename, expected_ext);
  99                        if (!new_name) {
 100                                bb_error_msg("%s: unknown suffix - ignored", filename);
 101                                goto err;
 102                        }
 103
 104                        /* -f: overwrite existing output files */
 105                        if (option_mask32 & OPT_FORCE) {
 106                                unlink(new_name);
 107                        }
 108
 109                        /* O_EXCL: "real" bunzip2 doesn't overwrite files */
 110                        /* GNU gunzip does not bail out, but goes to next file */
 111                        if (open_to_or_warn(STDOUT_FILENO, new_name, O_WRONLY | O_CREAT | O_EXCL,
 112                                        stat_buf.st_mode))
 113                                goto err;
 114                }
 115
 116                /* Check that the input is sane */
 117                if (!(option_mask32 & OPT_FORCE) && isatty(STDIN_FILENO)) {
 118                        bb_error_msg_and_die("compressed data not read from terminal, "
 119                                        "use -f to force it");
 120                }
 121
 122                if (!(option_mask32 & SEAMLESS_MAGIC)) {
 123                        init_transformer_state(&xstate);
 124                        xstate.check_signature = 1;
 125                        /*xstate.src_fd = STDIN_FILENO; - already is */
 126                        xstate.dst_fd = STDOUT_FILENO;
 127                        status = unpacker(&xstate);
 128                        if (status < 0)
 129                                exitcode = 1;
 130                } else {
 131                        if (bb_copyfd_eof(STDIN_FILENO, STDOUT_FILENO) < 0)
 132                                /* Disk full, tty closed, etc. No point in continuing */
 133                                xfunc_die();
 134                }
 135
 136                if (!(option_mask32 & OPT_STDOUT))
 137                        xclose(STDOUT_FILENO); /* with error check! */
 138
 139                if (filename) {
 140                        char *del = new_name;
 141
 142                        if (status >= 0) {
 143                                unsigned new_name_len;
 144
 145                                /* TODO: restore other things? */
 146                                if (xstate.mtime != 0) {
 147                                        struct timeval times[2];
 148
 149                                        times[1].tv_sec = times[0].tv_sec = xstate.mtime;
 150                                        times[1].tv_usec = times[0].tv_usec = 0;
 151                                        /* Note: we closed it first.
 152                                         * On some systems calling utimes
 153                                         * then closing resets the mtime
 154                                         * back to current time. */
 155                                        utimes(new_name, times); /* ignoring errors */
 156                                }
 157
 158                                if (ENABLE_DESKTOP)
 159                                        new_name_len = strlen(new_name);
 160                                /* Restore source filename (unless tgz -> tar case) */
 161                                if (new_name == filename) {
 162                                        new_name_len = strlen(filename);
 163                                        filename[new_name_len] = '.';
 164                                }
 165                                /* Extreme bloat for gunzip compat */
 166                                /* Some users do want this info... */
 167                                if (ENABLE_DESKTOP && (option_mask32 & OPT_VERBOSE)) {
 168                                        unsigned percent = status
 169                                                ? ((uoff_t)stat_buf.st_size * 100u / (unsigned long long)status)
 170                                                : 0;
 171                                        fprintf(stderr, "%s: %u%% - replaced with %.*s\n",
 172                                                filename,
 173                                                100u - percent,
 174                                                new_name_len, new_name
 175                                        );
 176                                }
 177                                /* Delete _source_ file */
 178                                del = filename;
 179                        }
 180                        xunlink(del);
 181 free_name:
 182                        if (new_name != filename)
 183                                free(new_name);
 184                }
 185        } while (*argv && *++argv);
 186
 187        if (option_mask32 & OPT_STDOUT)
 188                xclose(STDOUT_FILENO); /* with error check! */
 189
 190        return exitcode;
 191}
 192
 193#if ENABLE_UNCOMPRESS || ENABLE_BUNZIP2 || ENABLE_UNLZMA || ENABLE_UNXZ
 194static
 195char* FAST_FUNC make_new_name_generic(char *filename, const char *expected_ext)
 196{
 197        char *extension = strrchr(filename, '.');
 198        if (!extension || strcmp(extension + 1, expected_ext) != 0) {
 199                /* Mimic GNU gunzip - "real" bunzip2 tries to */
 200                /* unpack file anyway, to file.out */
 201                return NULL;
 202        }
 203        *extension = '\0';
 204        return filename;
 205}
 206#endif
 207
 208
 209/*
 210 * Uncompress applet for busybox (c) 2002 Glenn McGrath
 211 *
 212 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
 213 */
 214//usage:#define uncompress_trivial_usage
 215//usage:       "[-cf] [FILE]..."
 216//usage:#define uncompress_full_usage "\n\n"
 217//usage:       "Decompress .Z file[s]\n"
 218//usage:     "\n        -c      Write to stdout"
 219//usage:     "\n        -f      Overwrite"
 220
 221//config:config UNCOMPRESS
 222//config:       bool "uncompress"
 223//config:       default n
 224//config:       help
 225//config:         uncompress is used to decompress archives created by compress.
 226//config:         Not much used anymore, replaced by gzip/gunzip.
 227
 228//applet:IF_UNCOMPRESS(APPLET(uncompress, BB_DIR_BIN, BB_SUID_DROP))
 229//kbuild:lib-$(CONFIG_UNCOMPRESS) += bbunzip.o
 230#if ENABLE_UNCOMPRESS
 231int uncompress_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 232int uncompress_main(int argc UNUSED_PARAM, char **argv)
 233{
 234        getopt32(argv, "cf");
 235        argv += optind;
 236
 237        return bbunpack(argv, unpack_Z_stream, make_new_name_generic, "Z");
 238}
 239#endif
 240
 241
 242/*
 243 * Gzip implementation for busybox
 244 *
 245 * Based on GNU gzip v1.2.4 Copyright (C) 1992-1993 Jean-loup Gailly.
 246 *
 247 * Originally adjusted for busybox by Sven Rudolph <sr1@inf.tu-dresden.de>
 248 * based on gzip sources
 249 *
 250 * Adjusted further by Erik Andersen <andersen@codepoet.org> to support files as
 251 * well as stdin/stdout, and to generally behave itself wrt command line
 252 * handling.
 253 *
 254 * General cleanup to better adhere to the style guide and make use of standard
 255 * busybox functions by Glenn McGrath
 256 *
 257 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
 258 *
 259 * gzip (GNU zip) -- compress files with zip algorithm and 'compress' interface
 260 * Copyright (C) 1992-1993 Jean-loup Gailly
 261 * The unzip code was written and put in the public domain by Mark Adler.
 262 * Portions of the lzw code are derived from the public domain 'compress'
 263 * written by Spencer Thomas, Joe Orost, James Woods, Jim McKie, Steve Davies,
 264 * Ken Turkowski, Dave Mack and Peter Jannesen.
 265 */
 266//usage:#define gunzip_trivial_usage
 267//usage:       "[-cft] [FILE]..."
 268//usage:#define gunzip_full_usage "\n\n"
 269//usage:       "Decompress FILEs (or stdin)\n"
 270//usage:     "\n        -c      Write to stdout"
 271//usage:     "\n        -f      Force"
 272//usage:     "\n        -t      Test file integrity"
 273//usage:
 274//usage:#define gunzip_example_usage
 275//usage:       "$ ls -la /tmp/BusyBox*\n"
 276//usage:       "-rw-rw-r--    1 andersen andersen   557009 Apr 11 10:55 /tmp/BusyBox-0.43.tar.gz\n"
 277//usage:       "$ gunzip /tmp/BusyBox-0.43.tar.gz\n"
 278//usage:       "$ ls -la /tmp/BusyBox*\n"
 279//usage:       "-rw-rw-r--    1 andersen andersen  1761280 Apr 14 17:47 /tmp/BusyBox-0.43.tar\n"
 280//usage:
 281//usage:#define zcat_trivial_usage
 282//usage:       "[FILE]..."
 283//usage:#define zcat_full_usage "\n\n"
 284//usage:       "Decompress to stdout"
 285
 286//config:config GUNZIP
 287//config:       bool "gunzip"
 288//config:       default y
 289//config:       help
 290//config:         gunzip is used to decompress archives created by gzip.
 291//config:         You can use the `-t' option to test the integrity of
 292//config:         an archive, without decompressing it.
 293
 294//applet:IF_GUNZIP(APPLET(gunzip, BB_DIR_BIN, BB_SUID_DROP))
 295//applet:IF_GUNZIP(APPLET_ODDNAME(zcat, gunzip, BB_DIR_BIN, BB_SUID_DROP, zcat))
 296//kbuild:lib-$(CONFIG_GZIP) += bbunzip.o
 297//kbuild:lib-$(CONFIG_GUNZIP) += bbunzip.o
 298#if ENABLE_GUNZIP
 299static
 300char* FAST_FUNC make_new_name_gunzip(char *filename, const char *expected_ext UNUSED_PARAM)
 301{
 302        char *extension = strrchr(filename, '.');
 303
 304        if (!extension)
 305                return NULL;
 306
 307        extension++;
 308        if (strcmp(extension, "tgz" + 1) == 0
 309#if ENABLE_FEATURE_SEAMLESS_Z
 310         || (extension[0] == 'Z' && extension[1] == '\0')
 311#endif
 312        ) {
 313                extension[-1] = '\0';
 314        } else if (strcmp(extension, "tgz") == 0) {
 315                filename = xstrdup(filename);
 316                extension = strrchr(filename, '.');
 317                extension[2] = 'a';
 318                extension[3] = 'r';
 319        } else {
 320                return NULL;
 321        }
 322        return filename;
 323}
 324/*
 325 * Linux kernel build uses gzip -d -n. We accept and ignore it.
 326 * Man page says:
 327 * -n --no-name
 328 * gzip: do not save the original file name and time stamp.
 329 * (The original name is always saved if the name had to be truncated.)
 330 * gunzip: do not restore the original file name/time even if present
 331 * (remove only the gzip suffix from the compressed file name).
 332 * This option is the default when decompressing.
 333 * -N --name
 334 * gzip: always save the original file name and time stamp (this is the default)
 335 * gunzip: restore the original file name and time stamp if present.
 336 */
 337int gunzip_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 338int gunzip_main(int argc UNUSED_PARAM, char **argv)
 339{
 340        getopt32(argv, "cfvqdtn");
 341        argv += optind;
 342
 343        /* If called as zcat...
 344         * Normally, "zcat" is just "gunzip -c".
 345         * But if seamless magic is enabled, then we are much more clever.
 346         */
 347        if (applet_name[1] == 'c')
 348                option_mask32 |= OPT_STDOUT | SEAMLESS_MAGIC;
 349
 350        return bbunpack(argv, unpack_gz_stream, make_new_name_gunzip, /*unused:*/ NULL);
 351}
 352#endif
 353
 354
 355/*
 356 * Modified for busybox by Glenn McGrath
 357 * Added support output to stdout by Thomas Lundquist <thomasez@zelow.no>
 358 *
 359 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
 360 */
 361//usage:#define bunzip2_trivial_usage
 362//usage:       "[-cf] [FILE]..."
 363//usage:#define bunzip2_full_usage "\n\n"
 364//usage:       "Decompress FILEs (or stdin)\n"
 365//usage:     "\n        -c      Write to stdout"
 366//usage:     "\n        -f      Force"
 367//usage:#define bzcat_trivial_usage
 368//usage:       "[FILE]..."
 369//usage:#define bzcat_full_usage "\n\n"
 370//usage:       "Decompress to stdout"
 371
 372//config:config BUNZIP2
 373//config:       bool "bunzip2"
 374//config:       default y
 375//config:       help
 376//config:         bunzip2 is a compression utility using the Burrows-Wheeler block
 377//config:         sorting text compression algorithm, and Huffman coding. Compression
 378//config:         is generally considerably better than that achieved by more
 379//config:         conventional LZ77/LZ78-based compressors, and approaches the
 380//config:         performance of the PPM family of statistical compressors.
 381//config:
 382//config:         Unless you have a specific application which requires bunzip2, you
 383//config:         should probably say N here.
 384
 385//applet:IF_BUNZIP2(APPLET(bunzip2, BB_DIR_USR_BIN, BB_SUID_DROP))
 386//applet:IF_BUNZIP2(APPLET_ODDNAME(bzcat, bunzip2, BB_DIR_USR_BIN, BB_SUID_DROP, bzcat))
 387//kbuild:lib-$(CONFIG_BZIP2) += bbunzip.o
 388//kbuild:lib-$(CONFIG_BUNZIP2) += bbunzip.o
 389#if ENABLE_BUNZIP2
 390int bunzip2_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 391int bunzip2_main(int argc UNUSED_PARAM, char **argv)
 392{
 393        getopt32(argv, "cfvqdt");
 394        argv += optind;
 395        if (applet_name[2] == 'c') /* bzcat */
 396                option_mask32 |= OPT_STDOUT;
 397
 398        return bbunpack(argv, unpack_bz2_stream, make_new_name_generic, "bz2");
 399}
 400#endif
 401
 402
 403/*
 404 * Small lzma deflate implementation.
 405 * Copyright (C) 2006  Aurelien Jacobs <aurel@gnuage.org>
 406 *
 407 * Based on bunzip.c from busybox
 408 *
 409 * Licensed under GPLv2, see file LICENSE in this source tree.
 410 */
 411//usage:#define unlzma_trivial_usage
 412//usage:       "[-cf] [FILE]..."
 413//usage:#define unlzma_full_usage "\n\n"
 414//usage:       "Decompress FILE (or stdin)\n"
 415//usage:     "\n        -c      Write to stdout"
 416//usage:     "\n        -f      Force"
 417//usage:
 418//usage:#define lzma_trivial_usage
 419//usage:       "-d [-cf] [FILE]..."
 420//usage:#define lzma_full_usage "\n\n"
 421//usage:       "Decompress FILE (or stdin)\n"
 422//usage:     "\n        -d      Decompress"
 423//usage:     "\n        -c      Write to stdout"
 424//usage:     "\n        -f      Force"
 425//usage:
 426//usage:#define lzcat_trivial_usage
 427//usage:       "[FILE]..."
 428//usage:#define lzcat_full_usage "\n\n"
 429//usage:       "Decompress to stdout"
 430//usage:
 431//usage:#define unxz_trivial_usage
 432//usage:       "[-cf] [FILE]..."
 433//usage:#define unxz_full_usage "\n\n"
 434//usage:       "Decompress FILE (or stdin)\n"
 435//usage:     "\n        -c      Write to stdout"
 436//usage:     "\n        -f      Force"
 437//usage:
 438//usage:#define xz_trivial_usage
 439//usage:       "-d [-cf] [FILE]..."
 440//usage:#define xz_full_usage "\n\n"
 441//usage:       "Decompress FILE (or stdin)\n"
 442//usage:     "\n        -d      Decompress"
 443//usage:     "\n        -c      Write to stdout"
 444//usage:     "\n        -f      Force"
 445//usage:
 446//usage:#define xzcat_trivial_usage
 447//usage:       "[FILE]..."
 448//usage:#define xzcat_full_usage "\n\n"
 449//usage:       "Decompress to stdout"
 450
 451//config:config UNLZMA
 452//config:       bool "unlzma"
 453//config:       default y
 454//config:       help
 455//config:         unlzma is a compression utility using the Lempel-Ziv-Markov chain
 456//config:         compression algorithm, and range coding. Compression
 457//config:         is generally considerably better than that achieved by the bzip2
 458//config:         compressors.
 459//config:
 460//config:         The BusyBox unlzma applet is limited to decompression only.
 461//config:         On an x86 system, this applet adds about 4K.
 462//config:
 463//config:config FEATURE_LZMA_FAST
 464//config:       bool "Optimize unlzma for speed"
 465//config:       default n
 466//config:       depends on UNLZMA
 467//config:       help
 468//config:         This option reduces decompression time by about 25% at the cost of
 469//config:         a 1K bigger binary.
 470//config:
 471//config:config LZMA
 472//config:       bool "Provide lzma alias which supports only unpacking"
 473//config:       default y
 474//config:       depends on UNLZMA
 475//config:       help
 476//config:         Enable this option if you want commands like "lzma -d" to work.
 477//config:         IOW: you'll get lzma applet, but it will always require -d option.
 478
 479//applet:IF_UNLZMA(APPLET(unlzma, BB_DIR_USR_BIN, BB_SUID_DROP))
 480//applet:IF_UNLZMA(APPLET_ODDNAME(lzcat, unlzma, BB_DIR_USR_BIN, BB_SUID_DROP, lzcat))
 481//applet:IF_LZMA(APPLET_ODDNAME(lzma, unlzma, BB_DIR_USR_BIN, BB_SUID_DROP, lzma))
 482//kbuild:lib-$(CONFIG_UNLZMA) += bbunzip.o
 483#if ENABLE_UNLZMA
 484int unlzma_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 485int unlzma_main(int argc UNUSED_PARAM, char **argv)
 486{
 487        IF_LZMA(int opts =) getopt32(argv, "cfvqdt");
 488# if ENABLE_LZMA
 489        /* lzma without -d or -t? */
 490        if (applet_name[2] == 'm' && !(opts & (OPT_DECOMPRESS|OPT_TEST)))
 491                bb_show_usage();
 492# endif
 493        /* lzcat? */
 494        if (applet_name[2] == 'c')
 495                option_mask32 |= OPT_STDOUT;
 496
 497        argv += optind;
 498        return bbunpack(argv, unpack_lzma_stream, make_new_name_generic, "lzma");
 499}
 500#endif
 501
 502
 503//config:config UNXZ
 504//config:       bool "unxz"
 505//config:       default y
 506//config:       help
 507//config:         unxz is a unlzma successor.
 508//config:
 509//config:config XZ
 510//config:       bool "Provide xz alias which supports only unpacking"
 511//config:       default y
 512//config:       depends on UNXZ
 513//config:       help
 514//config:         Enable this option if you want commands like "xz -d" to work.
 515//config:         IOW: you'll get xz applet, but it will always require -d option.
 516
 517//applet:IF_UNXZ(APPLET(unxz, BB_DIR_USR_BIN, BB_SUID_DROP))
 518//applet:IF_UNXZ(APPLET_ODDNAME(xzcat, unxz, BB_DIR_USR_BIN, BB_SUID_DROP, xzcat))
 519//applet:IF_XZ(APPLET_ODDNAME(xz, unxz, BB_DIR_USR_BIN, BB_SUID_DROP, xz))
 520//kbuild:lib-$(CONFIG_UNXZ) += bbunzip.o
 521#if ENABLE_UNXZ
 522int unxz_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 523int unxz_main(int argc UNUSED_PARAM, char **argv)
 524{
 525        IF_XZ(int opts =) getopt32(argv, "cfvqdt");
 526# if ENABLE_XZ
 527        /* xz without -d or -t? */
 528        if (applet_name[2] == '\0' && !(opts & (OPT_DECOMPRESS|OPT_TEST)))
 529                bb_show_usage();
 530# endif
 531        /* xzcat? */
 532        if (applet_name[2] == 'c')
 533                option_mask32 |= OPT_STDOUT;
 534
 535        argv += optind;
 536        return bbunpack(argv, unpack_xz_stream, make_new_name_generic, "xz");
 537}
 538#endif
 539