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 "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)(unpack_info_t *info),
  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        unpack_info_t info;
  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                /* memset(&info, 0, sizeof(info)); */
 102                info.mtime = 0; /* so far it has one member only */
 103                status = unpacker(&info);
 104                if (status < 0)
 105                        exitcode = 1;
 106                xclose(STDOUT_FILENO); /* with error check! */
 107
 108                if (filename) {
 109                        char *del = new_name;
 110                        if (status >= 0) {
 111                                /* TODO: restore other things? */
 112                                if (info.mtime) {
 113                                        struct timeval times[2];
 114
 115                                        times[1].tv_sec = times[0].tv_sec = info.mtime;
 116                                        times[1].tv_usec = times[0].tv_usec = 0;
 117                                        /* Note: we closed it first.
 118                                         * On some systems calling utimes
 119                                         * then closing resets the mtime
 120                                         * back to current time. */
 121                                        utimes(new_name, times); /* ignoring errors */
 122                                }
 123
 124                                /* Delete _compressed_ file */
 125                                del = filename;
 126                                /* restore extension (unless tgz -> tar case) */
 127                                if (new_name == filename)
 128                                        filename[strlen(filename)] = '.';
 129                        }
 130                        xunlink(del);
 131
 132#if 0 /* Currently buggy - wrong name: "a.gz: 261% - replaced with a.gz" */
 133                        /* Extreme bloat for gunzip compat */
 134                        if (ENABLE_DESKTOP && (option_mask32 & OPT_VERBOSE) && status >= 0) {
 135                                fprintf(stderr, "%s: %u%% - replaced with %s\n",
 136                                        filename, (unsigned)(stat_buf.st_size*100 / (status+1)), new_name);
 137                        }
 138#endif
 139
 140 free_name:
 141                        if (new_name != filename)
 142                                free(new_name);
 143                }
 144        } while (*argv && *++argv);
 145
 146        return exitcode;
 147}
 148
 149#if ENABLE_UNCOMPRESS || ENABLE_BUNZIP2 || ENABLE_UNLZMA || ENABLE_UNXZ
 150static
 151char* FAST_FUNC make_new_name_generic(char *filename, const char *expected_ext)
 152{
 153        char *extension = strrchr(filename, '.');
 154        if (!extension || strcmp(extension + 1, expected_ext) != 0) {
 155                /* Mimic GNU gunzip - "real" bunzip2 tries to */
 156                /* unpack file anyway, to file.out */
 157                return NULL;
 158        }
 159        *extension = '\0';
 160        return filename;
 161}
 162#endif
 163
 164
 165/*
 166 * Uncompress applet for busybox (c) 2002 Glenn McGrath
 167 *
 168 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
 169 */
 170#if ENABLE_UNCOMPRESS
 171static
 172IF_DESKTOP(long long) int FAST_FUNC unpack_uncompress(unpack_info_t *info UNUSED_PARAM)
 173{
 174        IF_DESKTOP(long long) int status = -1;
 175
 176        if ((xread_char(STDIN_FILENO) != 0x1f) || (xread_char(STDIN_FILENO) != 0x9d)) {
 177                bb_error_msg("invalid magic");
 178        } else {
 179                status = unpack_Z_stream(STDIN_FILENO, STDOUT_FILENO);
 180        }
 181        return status;
 182}
 183int uncompress_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 184int uncompress_main(int argc UNUSED_PARAM, char **argv)
 185{
 186        getopt32(argv, "cf");
 187        argv += optind;
 188
 189        return bbunpack(argv, unpack_uncompress, make_new_name_generic, "Z");
 190}
 191#endif
 192
 193
 194/*
 195 * Gzip implementation for busybox
 196 *
 197 * Based on GNU gzip v1.2.4 Copyright (C) 1992-1993 Jean-loup Gailly.
 198 *
 199 * Originally adjusted for busybox by Sven Rudolph <sr1@inf.tu-dresden.de>
 200 * based on gzip sources
 201 *
 202 * Adjusted further by Erik Andersen <andersen@codepoet.org> to support files as
 203 * well as stdin/stdout, and to generally behave itself wrt command line
 204 * handling.
 205 *
 206 * General cleanup to better adhere to the style guide and make use of standard
 207 * busybox functions by Glenn McGrath
 208 *
 209 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
 210 *
 211 * gzip (GNU zip) -- compress files with zip algorithm and 'compress' interface
 212 * Copyright (C) 1992-1993 Jean-loup Gailly
 213 * The unzip code was written and put in the public domain by Mark Adler.
 214 * Portions of the lzw code are derived from the public domain 'compress'
 215 * written by Spencer Thomas, Joe Orost, James Woods, Jim McKie, Steve Davies,
 216 * Ken Turkowski, Dave Mack and Peter Jannesen.
 217 *
 218 * See the license_msg below and the file COPYING for the software license.
 219 * See the file algorithm.doc for the compression algorithms and file formats.
 220 */
 221#if ENABLE_GUNZIP
 222static
 223char* FAST_FUNC make_new_name_gunzip(char *filename, const char *expected_ext UNUSED_PARAM)
 224{
 225        char *extension = strrchr(filename, '.');
 226
 227        if (!extension)
 228                return NULL;
 229
 230        extension++;
 231        if (strcmp(extension, "tgz" + 1) == 0
 232#if ENABLE_FEATURE_SEAMLESS_Z
 233         || (extension[0] == 'Z' && extension[1] == '\0')
 234#endif
 235        ) {
 236                extension[-1] = '\0';
 237        } else if (strcmp(extension, "tgz") == 0) {
 238                filename = xstrdup(filename);
 239                extension = strrchr(filename, '.');
 240                extension[2] = 'a';
 241                extension[3] = 'r';
 242        } else {
 243                return NULL;
 244        }
 245        return filename;
 246}
 247static
 248IF_DESKTOP(long long) int FAST_FUNC unpack_gunzip(unpack_info_t *info)
 249{
 250        IF_DESKTOP(long long) int status = -1;
 251
 252        /* do the decompression, and cleanup */
 253        if (xread_char(STDIN_FILENO) == 0x1f) {
 254                unsigned char magic2;
 255
 256                magic2 = xread_char(STDIN_FILENO);
 257                if (ENABLE_FEATURE_SEAMLESS_Z && magic2 == 0x9d) {
 258                        status = unpack_Z_stream(STDIN_FILENO, STDOUT_FILENO);
 259                } else if (magic2 == 0x8b) {
 260                        status = unpack_gz_stream_with_info(STDIN_FILENO, STDOUT_FILENO, info);
 261                } else {
 262                        goto bad_magic;
 263                }
 264                if (status < 0) {
 265                        bb_error_msg("error inflating");
 266                }
 267        } else {
 268 bad_magic:
 269                bb_error_msg("invalid magic");
 270                /* status is still == -1 */
 271        }
 272        return status;
 273}
 274/*
 275 * Linux kernel build uses gzip -d -n. We accept and ignore it.
 276 * Man page says:
 277 * -n --no-name
 278 * gzip: do not save the original file name and time stamp.
 279 * (The original name is always saved if the name had to be truncated.)
 280 * gunzip: do not restore the original file name/time even if present
 281 * (remove only the gzip suffix from the compressed file name).
 282 * This option is the default when decompressing.
 283 * -N --name
 284 * gzip: always save the original file name and time stamp (this is the default)
 285 * gunzip: restore the original file name and time stamp if present.
 286 */
 287int gunzip_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 288int gunzip_main(int argc UNUSED_PARAM, char **argv)
 289{
 290        getopt32(argv, "cfvdtn");
 291        argv += optind;
 292        /* if called as zcat */
 293        if (applet_name[1] == 'c')
 294                option_mask32 |= OPT_STDOUT;
 295
 296        return bbunpack(argv, unpack_gunzip, make_new_name_gunzip, /*unused:*/ NULL);
 297}
 298#endif
 299
 300
 301/*
 302 * Modified for busybox by Glenn McGrath
 303 * Added support output to stdout by Thomas Lundquist <thomasez@zelow.no>
 304 *
 305 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
 306 */
 307//usage:#define bunzip2_trivial_usage
 308//usage:       "[-cf] [FILE]..."
 309//usage:#define bunzip2_full_usage "\n\n"
 310//usage:       "Decompress FILEs (or stdin)\n"
 311//usage:     "\nOptions:"
 312//usage:     "\n        -c      Write to stdout"
 313//usage:     "\n        -f      Force"
 314//usage:#define bzcat_trivial_usage
 315//usage:       "FILE"
 316//usage:#define bzcat_full_usage "\n\n"
 317//usage:       "Decompress to stdout"
 318//applet:IF_BUNZIP2(APPLET(bunzip2, _BB_DIR_USR_BIN, _BB_SUID_DROP))
 319//applet:IF_BUNZIP2(APPLET_ODDNAME(bzcat, bunzip2, _BB_DIR_USR_BIN, _BB_SUID_DROP, bzcat))
 320#if ENABLE_BUNZIP2
 321static
 322IF_DESKTOP(long long) int FAST_FUNC unpack_bunzip2(unpack_info_t *info UNUSED_PARAM)
 323{
 324        return unpack_bz2_stream_prime(STDIN_FILENO, STDOUT_FILENO);
 325}
 326int bunzip2_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 327int bunzip2_main(int argc UNUSED_PARAM, char **argv)
 328{
 329        getopt32(argv, "cfvdt");
 330        argv += optind;
 331        if (applet_name[2] == 'c') /* bzcat */
 332                option_mask32 |= OPT_STDOUT;
 333
 334        return bbunpack(argv, unpack_bunzip2, make_new_name_generic, "bz2");
 335}
 336#endif
 337
 338
 339/*
 340 * Small lzma deflate implementation.
 341 * Copyright (C) 2006  Aurelien Jacobs <aurel@gnuage.org>
 342 *
 343 * Based on bunzip.c from busybox
 344 *
 345 * Licensed under GPLv2, see file LICENSE in this source tree.
 346 */
 347#if ENABLE_UNLZMA
 348static
 349IF_DESKTOP(long long) int FAST_FUNC unpack_unlzma(unpack_info_t *info UNUSED_PARAM)
 350{
 351        return unpack_lzma_stream(STDIN_FILENO, STDOUT_FILENO);
 352}
 353int unlzma_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 354int unlzma_main(int argc UNUSED_PARAM, char **argv)
 355{
 356        IF_LZMA(int opts =) getopt32(argv, "cfvdt");
 357# if ENABLE_LZMA
 358        /* lzma without -d or -t? */
 359        if (applet_name[2] == 'm' && !(opts & (OPT_DECOMPRESS|OPT_TEST)))
 360                bb_show_usage();
 361# endif
 362        /* lzcat? */
 363        if (applet_name[2] == 'c')
 364                option_mask32 |= OPT_STDOUT;
 365
 366        argv += optind;
 367        return bbunpack(argv, unpack_unlzma, make_new_name_generic, "lzma");
 368}
 369#endif
 370
 371
 372#if ENABLE_UNXZ
 373static
 374IF_DESKTOP(long long) int FAST_FUNC unpack_unxz(unpack_info_t *info UNUSED_PARAM)
 375{
 376        struct {
 377                uint32_t v1;
 378                uint16_t v2;
 379        } magic;
 380        xread(STDIN_FILENO, &magic, 6);
 381        if (magic.v1 != XZ_MAGIC1a || magic.v2 != XZ_MAGIC2a) {
 382                bb_error_msg("invalid magic");
 383                return -1;
 384        }
 385        return unpack_xz_stream(STDIN_FILENO, STDOUT_FILENO);
 386}
 387int unxz_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 388int unxz_main(int argc UNUSED_PARAM, char **argv)
 389{
 390        IF_XZ(int opts =) getopt32(argv, "cfvdt");
 391# if ENABLE_XZ
 392        /* xz without -d or -t? */
 393        if (applet_name[2] == '\0' && !(opts & (OPT_DECOMPRESS|OPT_TEST)))
 394                bb_show_usage();
 395# endif
 396        /* xzcat? */
 397        if (applet_name[2] == 'c')
 398                option_mask32 |= OPT_STDOUT;
 399
 400        argv += optind;
 401        return bbunpack(argv, unpack_unxz, make_new_name_generic, "xz");
 402}
 403#endif
 404