busybox/archival/unzip.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * Mini unzip implementation for busybox
   4 *
   5 * Copyright (C) 2004 by Ed Clark
   6 *
   7 * Loosely based on original busybox unzip applet by Laurence Anderson.
   8 * All options and features should work in this version.
   9 *
  10 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  11 */
  12/* For reference see
  13 * http://www.pkware.com/company/standards/appnote/
  14 * http://www.info-zip.org/pub/infozip/doc/appnote-iz-latest.zip
  15 *
  16 * TODO
  17 * Zip64 + other methods
  18 */
  19
  20//config:config UNZIP
  21//config:       bool "unzip"
  22//config:       default y
  23//config:       help
  24//config:         unzip will list or extract files from a ZIP archive,
  25//config:         commonly found on DOS/WIN systems. The default behavior
  26//config:         (with no options) is to extract the archive into the
  27//config:         current directory. Use the `-d' option to extract to a
  28//config:         directory of your choice.
  29
  30//applet:IF_UNZIP(APPLET(unzip, BB_DIR_USR_BIN, BB_SUID_DROP))
  31//kbuild:lib-$(CONFIG_UNZIP) += unzip.o
  32
  33//usage:#define unzip_trivial_usage
  34//usage:       "[-lnopq] FILE[.zip] [FILE]... [-x FILE...] [-d DIR]"
  35//usage:#define unzip_full_usage "\n\n"
  36//usage:       "Extract FILEs from ZIP archive\n"
  37//usage:     "\n        -l      List contents (with -q for short form)"
  38//usage:     "\n        -n      Never overwrite files (default: ask)"
  39//usage:     "\n        -o      Overwrite"
  40//usage:     "\n        -p      Print to stdout"
  41//usage:     "\n        -q      Quiet"
  42//usage:     "\n        -x FILE Exclude FILEs"
  43//usage:     "\n        -d DIR  Extract into DIR"
  44
  45#include "libbb.h"
  46#include "bb_archive.h"
  47
  48enum {
  49#if BB_BIG_ENDIAN
  50        ZIP_FILEHEADER_MAGIC = 0x504b0304,
  51        ZIP_CDF_MAGIC        = 0x504b0102, /* central directory's file header */
  52        ZIP_CDE_MAGIC        = 0x504b0506, /* "end of central directory" record */
  53        ZIP_DD_MAGIC         = 0x504b0708,
  54#else
  55        ZIP_FILEHEADER_MAGIC = 0x04034b50,
  56        ZIP_CDF_MAGIC        = 0x02014b50,
  57        ZIP_CDE_MAGIC        = 0x06054b50,
  58        ZIP_DD_MAGIC         = 0x08074b50,
  59#endif
  60};
  61
  62#define ZIP_HEADER_LEN 26
  63
  64typedef union {
  65        uint8_t raw[ZIP_HEADER_LEN];
  66        struct {
  67                uint16_t version;               /* 0-1 */
  68                uint16_t zip_flags;             /* 2-3 */
  69                uint16_t method;                /* 4-5 */
  70                uint16_t modtime;               /* 6-7 */
  71                uint16_t moddate;               /* 8-9 */
  72                uint32_t crc32 PACKED;          /* 10-13 */
  73                uint32_t cmpsize PACKED;        /* 14-17 */
  74                uint32_t ucmpsize PACKED;       /* 18-21 */
  75                uint16_t filename_len;          /* 22-23 */
  76                uint16_t extra_len;             /* 24-25 */
  77        } formatted PACKED;
  78} zip_header_t; /* PACKED - gcc 4.2.1 doesn't like it (spews warning) */
  79
  80/* Check the offset of the last element, not the length.  This leniency
  81 * allows for poor packing, whereby the overall struct may be too long,
  82 * even though the elements are all in the right place.
  83 */
  84struct BUG_zip_header_must_be_26_bytes {
  85        char BUG_zip_header_must_be_26_bytes[
  86                offsetof(zip_header_t, formatted.extra_len) + 2
  87                        == ZIP_HEADER_LEN ? 1 : -1];
  88};
  89
  90#define FIX_ENDIANNESS_ZIP(zip_header) do { \
  91        (zip_header).formatted.version      = SWAP_LE16((zip_header).formatted.version     ); \
  92        (zip_header).formatted.method       = SWAP_LE16((zip_header).formatted.method      ); \
  93        (zip_header).formatted.modtime      = SWAP_LE16((zip_header).formatted.modtime     ); \
  94        (zip_header).formatted.moddate      = SWAP_LE16((zip_header).formatted.moddate     ); \
  95        (zip_header).formatted.crc32        = SWAP_LE32((zip_header).formatted.crc32       ); \
  96        (zip_header).formatted.cmpsize      = SWAP_LE32((zip_header).formatted.cmpsize     ); \
  97        (zip_header).formatted.ucmpsize     = SWAP_LE32((zip_header).formatted.ucmpsize    ); \
  98        (zip_header).formatted.filename_len = SWAP_LE16((zip_header).formatted.filename_len); \
  99        (zip_header).formatted.extra_len    = SWAP_LE16((zip_header).formatted.extra_len   ); \
 100} while (0)
 101
 102#define CDF_HEADER_LEN 42
 103
 104typedef union {
 105        uint8_t raw[CDF_HEADER_LEN];
 106        struct {
 107                /* uint32_t signature; 50 4b 01 02 */
 108                uint16_t version_made_by;       /* 0-1 */
 109                uint16_t version_needed;        /* 2-3 */
 110                uint16_t cdf_flags;             /* 4-5 */
 111                uint16_t method;                /* 6-7 */
 112                uint16_t mtime;                 /* 8-9 */
 113                uint16_t mdate;                 /* 10-11 */
 114                uint32_t crc32;                 /* 12-15 */
 115                uint32_t cmpsize;               /* 16-19 */
 116                uint32_t ucmpsize;              /* 20-23 */
 117                uint16_t file_name_length;      /* 24-25 */
 118                uint16_t extra_field_length;    /* 26-27 */
 119                uint16_t file_comment_length;   /* 28-29 */
 120                uint16_t disk_number_start;     /* 30-31 */
 121                uint16_t internal_file_attributes; /* 32-33 */
 122                uint32_t external_file_attributes PACKED; /* 34-37 */
 123                uint32_t relative_offset_of_local_header PACKED; /* 38-41 */
 124        } formatted PACKED;
 125} cdf_header_t;
 126
 127struct BUG_cdf_header_must_be_42_bytes {
 128        char BUG_cdf_header_must_be_42_bytes[
 129                offsetof(cdf_header_t, formatted.relative_offset_of_local_header) + 4
 130                        == CDF_HEADER_LEN ? 1 : -1];
 131};
 132
 133#define FIX_ENDIANNESS_CDF(cdf_header) do { \
 134        (cdf_header).formatted.crc32        = SWAP_LE32((cdf_header).formatted.crc32       ); \
 135        (cdf_header).formatted.cmpsize      = SWAP_LE32((cdf_header).formatted.cmpsize     ); \
 136        (cdf_header).formatted.ucmpsize     = SWAP_LE32((cdf_header).formatted.ucmpsize    ); \
 137        (cdf_header).formatted.file_name_length = SWAP_LE16((cdf_header).formatted.file_name_length); \
 138        (cdf_header).formatted.extra_field_length = SWAP_LE16((cdf_header).formatted.extra_field_length); \
 139        (cdf_header).formatted.file_comment_length = SWAP_LE16((cdf_header).formatted.file_comment_length); \
 140        IF_DESKTOP( \
 141        (cdf_header).formatted.version_made_by = SWAP_LE16((cdf_header).formatted.version_made_by); \
 142        (cdf_header).formatted.external_file_attributes = SWAP_LE32((cdf_header).formatted.external_file_attributes); \
 143        ) \
 144} while (0)
 145
 146#define CDE_HEADER_LEN 16
 147
 148typedef union {
 149        uint8_t raw[CDE_HEADER_LEN];
 150        struct {
 151                /* uint32_t signature; 50 4b 05 06 */
 152                uint16_t this_disk_no;
 153                uint16_t disk_with_cdf_no;
 154                uint16_t cdf_entries_on_this_disk;
 155                uint16_t cdf_entries_total;
 156                uint32_t cdf_size;
 157                uint32_t cdf_offset;
 158                /* uint16_t file_comment_length; */
 159                /* .ZIP file comment (variable size) */
 160        } formatted PACKED;
 161} cde_header_t;
 162
 163struct BUG_cde_header_must_be_16_bytes {
 164        char BUG_cde_header_must_be_16_bytes[
 165                sizeof(cde_header_t) == CDE_HEADER_LEN ? 1 : -1];
 166};
 167
 168#define FIX_ENDIANNESS_CDE(cde_header) do { \
 169        (cde_header).formatted.cdf_offset = SWAP_LE32((cde_header).formatted.cdf_offset); \
 170} while (0)
 171
 172enum { zip_fd = 3 };
 173
 174
 175#if ENABLE_DESKTOP
 176
 177/* Seen in the wild:
 178 * Self-extracting PRO2K3XP_32.exe contains 19078464 byte zip archive,
 179 * where CDE was nearly 48 kbytes before EOF.
 180 * (Surprisingly, it also apparently has *another* CDE structure
 181 * closer to the end, with bogus cdf_offset).
 182 * To make extraction work, bumped PEEK_FROM_END from 16k to 64k.
 183 */
 184#define PEEK_FROM_END (64*1024)
 185
 186/* This value means that we failed to find CDF */
 187#define BAD_CDF_OFFSET ((uint32_t)0xffffffff)
 188
 189/* NB: does not preserve file position! */
 190static uint32_t find_cdf_offset(void)
 191{
 192        cde_header_t cde_header;
 193        unsigned char *p;
 194        off_t end;
 195        unsigned char *buf = xzalloc(PEEK_FROM_END);
 196
 197        end = xlseek(zip_fd, 0, SEEK_END);
 198        end -= PEEK_FROM_END;
 199        if (end < 0)
 200                end = 0;
 201        xlseek(zip_fd, end, SEEK_SET);
 202        full_read(zip_fd, buf, PEEK_FROM_END);
 203
 204        cde_header.formatted.cdf_offset = BAD_CDF_OFFSET;
 205        p = buf;
 206        while (p <= buf + PEEK_FROM_END - CDE_HEADER_LEN - 4) {
 207                if (*p != 'P') {
 208                        p++;
 209                        continue;
 210                }
 211                if (*++p != 'K')
 212                        continue;
 213                if (*++p != 5)
 214                        continue;
 215                if (*++p != 6)
 216                        continue;
 217                /* we found CDE! */
 218                memcpy(cde_header.raw, p + 1, CDE_HEADER_LEN);
 219                FIX_ENDIANNESS_CDE(cde_header);
 220                /*
 221                 * I've seen .ZIP files with seemingly valid CDEs
 222                 * where cdf_offset points past EOF - ??
 223                 * Ignore such CDEs:
 224                 */
 225                if (cde_header.formatted.cdf_offset < end + (p - buf))
 226                        break;
 227                cde_header.formatted.cdf_offset = BAD_CDF_OFFSET;
 228        }
 229        free(buf);
 230        return cde_header.formatted.cdf_offset;
 231};
 232
 233static uint32_t read_next_cdf(uint32_t cdf_offset, cdf_header_t *cdf_ptr)
 234{
 235        off_t org;
 236
 237        org = xlseek(zip_fd, 0, SEEK_CUR);
 238
 239        if (!cdf_offset)
 240                cdf_offset = find_cdf_offset();
 241
 242        if (cdf_offset != BAD_CDF_OFFSET) {
 243                xlseek(zip_fd, cdf_offset + 4, SEEK_SET);
 244                xread(zip_fd, cdf_ptr->raw, CDF_HEADER_LEN);
 245                FIX_ENDIANNESS_CDF(*cdf_ptr);
 246                cdf_offset += 4 + CDF_HEADER_LEN
 247                        + cdf_ptr->formatted.file_name_length
 248                        + cdf_ptr->formatted.extra_field_length
 249                        + cdf_ptr->formatted.file_comment_length;
 250        }
 251
 252        xlseek(zip_fd, org, SEEK_SET);
 253        return cdf_offset;
 254};
 255#endif
 256
 257static void unzip_skip(off_t skip)
 258{
 259        if (skip != 0)
 260                if (lseek(zip_fd, skip, SEEK_CUR) == (off_t)-1)
 261                        bb_copyfd_exact_size(zip_fd, -1, skip);
 262}
 263
 264static void unzip_create_leading_dirs(const char *fn)
 265{
 266        /* Create all leading directories */
 267        char *name = xstrdup(fn);
 268        if (bb_make_directory(dirname(name), 0777, FILEUTILS_RECUR)) {
 269                xfunc_die(); /* bb_make_directory is noisy */
 270        }
 271        free(name);
 272}
 273
 274static void unzip_extract(zip_header_t *zip_header, int dst_fd)
 275{
 276        if (zip_header->formatted.method == 0) {
 277                /* Method 0 - stored (not compressed) */
 278                off_t size = zip_header->formatted.ucmpsize;
 279                if (size)
 280                        bb_copyfd_exact_size(zip_fd, dst_fd, size);
 281        } else {
 282                /* Method 8 - inflate */
 283                transformer_state_t xstate;
 284                init_transformer_state(&xstate);
 285                xstate.bytes_in = zip_header->formatted.cmpsize;
 286                xstate.src_fd = zip_fd;
 287                xstate.dst_fd = dst_fd;
 288                if (inflate_unzip(&xstate) < 0)
 289                        bb_error_msg_and_die("inflate error");
 290                /* Validate decompression - crc */
 291                if (zip_header->formatted.crc32 != (xstate.crc32 ^ 0xffffffffL)) {
 292                        bb_error_msg_and_die("crc error");
 293                }
 294                /* Validate decompression - size */
 295                if (zip_header->formatted.ucmpsize != xstate.bytes_out) {
 296                        /* Don't die. Who knows, maybe len calculation
 297                         * was botched somewhere. After all, crc matched! */
 298                        bb_error_msg("bad length");
 299                }
 300        }
 301}
 302
 303static void my_fgets80(char *buf80)
 304{
 305        fflush_all();
 306        if (!fgets(buf80, 80, stdin)) {
 307                bb_perror_msg_and_die("can't read standard input");
 308        }
 309}
 310
 311int unzip_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 312int unzip_main(int argc, char **argv)
 313{
 314        enum { O_PROMPT, O_NEVER, O_ALWAYS };
 315
 316        zip_header_t zip_header;
 317        smallint quiet = 0;
 318        IF_NOT_DESKTOP(const) smallint verbose = 0;
 319        smallint listing = 0;
 320        smallint overwrite = O_PROMPT;
 321        smallint x_opt_seen;
 322#if ENABLE_DESKTOP
 323        uint32_t cdf_offset;
 324#endif
 325        unsigned long total_usize;
 326        unsigned long total_size;
 327        unsigned total_entries;
 328        int dst_fd = -1;
 329        char *src_fn = NULL;
 330        char *dst_fn = NULL;
 331        llist_t *zaccept = NULL;
 332        llist_t *zreject = NULL;
 333        char *base_dir = NULL;
 334        int i, opt;
 335        char key_buf[80]; /* must match size used by my_fgets80 */
 336        struct stat stat_buf;
 337
 338/* -q, -l and -v: UnZip 5.52 of 28 February 2005, by Info-ZIP:
 339 *
 340 * # /usr/bin/unzip -qq -v decompress_unlzma.i.zip
 341 *   204372  Defl:N    35278  83%  09-06-09 14:23  0d056252  decompress_unlzma.i
 342 * # /usr/bin/unzip -q -v decompress_unlzma.i.zip
 343 *  Length   Method    Size  Ratio   Date   Time   CRC-32    Name
 344 * --------  ------  ------- -----   ----   ----   ------    ----
 345 *   204372  Defl:N    35278  83%  09-06-09 14:23  0d056252  decompress_unlzma.i
 346 * --------          -------  ---                            -------
 347 *   204372            35278  83%                            1 file
 348 * # /usr/bin/unzip -v decompress_unlzma.i.zip
 349 * Archive:  decompress_unlzma.i.zip
 350 *  Length   Method    Size  Ratio   Date   Time   CRC-32    Name
 351 * --------  ------  ------- -----   ----   ----   ------    ----
 352 *   204372  Defl:N    35278  83%  09-06-09 14:23  0d056252  decompress_unlzma.i
 353 * --------          -------  ---                            -------
 354 *   204372            35278  83%                            1 file
 355 * # unzip -v decompress_unlzma.i.zip
 356 * Archive:  decompress_unlzma.i.zip
 357 *   Length     Date   Time    Name
 358 *  --------    ----   ----    ----
 359 *    204372  09-06-09 14:23   decompress_unlzma.i
 360 *  --------                   -------
 361 *    204372                   1 files
 362 * # /usr/bin/unzip -l -qq decompress_unlzma.i.zip
 363 *    204372  09-06-09 14:23   decompress_unlzma.i
 364 * # /usr/bin/unzip -l -q decompress_unlzma.i.zip
 365 *   Length     Date   Time    Name
 366 *  --------    ----   ----    ----
 367 *    204372  09-06-09 14:23   decompress_unlzma.i
 368 *  --------                   -------
 369 *    204372                   1 file
 370 * # /usr/bin/unzip -l decompress_unlzma.i.zip
 371 * Archive:  decompress_unlzma.i.zip
 372 *   Length     Date   Time    Name
 373 *  --------    ----   ----    ----
 374 *    204372  09-06-09 14:23   decompress_unlzma.i
 375 *  --------                   -------
 376 *    204372                   1 file
 377 */
 378
 379        x_opt_seen = 0;
 380        /* '-' makes getopt return 1 for non-options */
 381        while ((opt = getopt(argc, argv, "-d:lnopqxv")) != -1) {
 382                switch (opt) {
 383                case 'd':  /* Extract to base directory */
 384                        base_dir = optarg;
 385                        break;
 386
 387                case 'l': /* List */
 388                        listing = 1;
 389                        break;
 390
 391                case 'n': /* Never overwrite existing files */
 392                        overwrite = O_NEVER;
 393                        break;
 394
 395                case 'o': /* Always overwrite existing files */
 396                        overwrite = O_ALWAYS;
 397                        break;
 398
 399                case 'p': /* Extract files to stdout and fall through to set verbosity */
 400                        dst_fd = STDOUT_FILENO;
 401
 402                case 'q': /* Be quiet */
 403                        quiet++;
 404                        break;
 405
 406                case 'v': /* Verbose list */
 407                        IF_DESKTOP(verbose++;)
 408                        listing = 1;
 409                        break;
 410
 411                case 'x':
 412                        x_opt_seen = 1;
 413                        break;
 414
 415                case 1:
 416                        if (!src_fn) {
 417                                /* The zip file */
 418                                /* +5: space for ".zip" and NUL */
 419                                src_fn = xmalloc(strlen(optarg) + 5);
 420                                strcpy(src_fn, optarg);
 421                        } else if (!x_opt_seen) {
 422                                /* Include files */
 423                                llist_add_to(&zaccept, optarg);
 424                        } else {
 425                                /* Exclude files */
 426                                llist_add_to(&zreject, optarg);
 427                        }
 428                        break;
 429
 430                default:
 431                        bb_show_usage();
 432                }
 433        }
 434
 435#ifndef __GLIBC__
 436        /*
 437         * This code is needed for non-GNU getopt
 438         * which doesn't understand "-" in option string.
 439         * The -x option won't work properly in this case:
 440         * "unzip a.zip q -x w e" will be interpreted as
 441         * "unzip a.zip q w e -x" = "unzip a.zip q w e"
 442         */
 443        argv += optind;
 444        if (argv[0]) {
 445                /* +5: space for ".zip" and NUL */
 446                src_fn = xmalloc(strlen(argv[0]) + 5);
 447                strcpy(src_fn, argv[0]);
 448                while (*++argv)
 449                        llist_add_to(&zaccept, *argv);
 450        }
 451#endif
 452
 453        if (!src_fn) {
 454                bb_show_usage();
 455        }
 456
 457        /* Open input file */
 458        if (LONE_DASH(src_fn)) {
 459                xdup2(STDIN_FILENO, zip_fd);
 460                /* Cannot use prompt mode since zip data is arriving on STDIN */
 461                if (overwrite == O_PROMPT)
 462                        overwrite = O_NEVER;
 463        } else {
 464                static const char extn[][5] = { ".zip", ".ZIP" };
 465                char *ext = src_fn + strlen(src_fn);
 466                int src_fd;
 467
 468                i = 0;
 469                for (;;) {
 470                        src_fd = open(src_fn, O_RDONLY);
 471                        if (src_fd >= 0)
 472                                break;
 473                        if (++i > 2) {
 474                                *ext = '\0';
 475                                bb_error_msg_and_die("can't open %s[.zip]", src_fn);
 476                        }
 477                        strcpy(ext, extn[i - 1]);
 478                }
 479                xmove_fd(src_fd, zip_fd);
 480        }
 481
 482        /* Change dir if necessary */
 483        if (base_dir)
 484                xchdir(base_dir);
 485
 486        if (quiet <= 1) { /* not -qq */
 487                if (quiet == 0)
 488                        printf("Archive:  %s\n", src_fn);
 489                if (listing) {
 490                        puts(verbose ?
 491                                " Length   Method    Size  Ratio   Date   Time   CRC-32    Name\n"
 492                                "--------  ------  ------- -----   ----   ----   ------    ----"
 493                                :
 494                                "  Length     Date   Time    Name\n"
 495                                " --------    ----   ----    ----"
 496                                );
 497                }
 498        }
 499
 500/* Example of an archive with one 0-byte long file named 'z'
 501 * created by Zip 2.31 on Unix:
 502 * 0000 [50 4b]03 04 0a 00 00 00 00 00 42 1a b8 3c 00 00 |PK........B..<..|
 503 *       sig........ vneed flags compr mtime mdate crc32>
 504 * 0010  00 00 00 00 00 00 00 00 00 00 01 00 15 00 7a 55 |..............zU|
 505 *      >..... csize...... usize...... fnlen exlen fn ex>
 506 * 0020  54 09 00 03 cc d3 f9 4b cc d3 f9 4b 55 78 04 00 |T......K...KUx..|
 507 *      >tra_field......................................
 508 * 0030  00 00 00 00[50 4b]01 02 17 03 0a 00 00 00 00 00 |....PK..........|
 509 *       ........... sig........ vmade vneed flags compr
 510 * 0040  42 1a b8 3c 00 00 00 00 00 00 00 00 00 00 00 00 |B..<............|
 511 *       mtime mdate crc32...... csize...... usize......
 512 * 0050  01 00 0d 00 00 00 00 00 00 00 00 00 a4 81 00 00 |................|
 513 *       fnlen exlen clen. dnum. iattr eattr...... relofs> (eattr = rw-r--r--)
 514 * 0060  00 00 7a 55 54 05 00 03 cc d3 f9 4b 55 78 00 00 |..zUT......KUx..|
 515 *      >..... fn extra_field...........................
 516 * 0070 [50 4b]05 06 00 00 00 00 01 00 01 00 3c 00 00 00 |PK..........<...|
 517 * 0080  34 00 00 00 00 00                               |4.....|
 518 */
 519        total_usize = 0;
 520        total_size = 0;
 521        total_entries = 0;
 522#if ENABLE_DESKTOP
 523        cdf_offset = 0;
 524#endif
 525        while (1) {
 526                uint32_t magic;
 527                mode_t dir_mode = 0777;
 528#if ENABLE_DESKTOP
 529                mode_t file_mode = 0666;
 530#endif
 531
 532                /* Check magic number */
 533                xread(zip_fd, &magic, 4);
 534                /* Central directory? It's at the end, so exit */
 535                if (magic == ZIP_CDF_MAGIC)
 536                        break;
 537#if ENABLE_DESKTOP
 538                /* Data descriptor? It was a streaming file, go on */
 539                if (magic == ZIP_DD_MAGIC) {
 540                        /* skip over duplicate crc32, cmpsize and ucmpsize */
 541                        unzip_skip(3 * 4);
 542                        continue;
 543                }
 544#endif
 545                if (magic != ZIP_FILEHEADER_MAGIC)
 546                        bb_error_msg_and_die("invalid zip magic %08X", (int)magic);
 547
 548                /* Read the file header */
 549                xread(zip_fd, zip_header.raw, ZIP_HEADER_LEN);
 550                FIX_ENDIANNESS_ZIP(zip_header);
 551                if ((zip_header.formatted.method != 0) && (zip_header.formatted.method != 8)) {
 552                        bb_error_msg_and_die("unsupported method %d", zip_header.formatted.method);
 553                }
 554#if !ENABLE_DESKTOP
 555                if (zip_header.formatted.zip_flags & SWAP_LE16(0x0009)) {
 556                        bb_error_msg_and_die("zip flags 1 and 8 are not supported");
 557                }
 558#else
 559                if (zip_header.formatted.zip_flags & SWAP_LE16(0x0001)) {
 560                        /* 0x0001 - encrypted */
 561                        bb_error_msg_and_die("zip flag 1 (encryption) is not supported");
 562                }
 563
 564                if (cdf_offset != BAD_CDF_OFFSET) {
 565                        cdf_header_t cdf_header;
 566                        cdf_offset = read_next_cdf(cdf_offset, &cdf_header);
 567                        /*
 568                         * Note: cdf_offset can become BAD_CDF_OFFSET after the above call.
 569                         */
 570                        if (zip_header.formatted.zip_flags & SWAP_LE16(0x0008)) {
 571                                /* 0x0008 - streaming. [u]cmpsize can be reliably gotten
 572                                 * only from Central Directory. See unzip_doc.txt
 573                                 */
 574                                zip_header.formatted.crc32    = cdf_header.formatted.crc32;
 575                                zip_header.formatted.cmpsize  = cdf_header.formatted.cmpsize;
 576                                zip_header.formatted.ucmpsize = cdf_header.formatted.ucmpsize;
 577                        }
 578                        if ((cdf_header.formatted.version_made_by >> 8) == 3) {
 579                                /* This archive is created on Unix */
 580                                dir_mode = file_mode = (cdf_header.formatted.external_file_attributes >> 16);
 581                        }
 582                }
 583                if (cdf_offset == BAD_CDF_OFFSET
 584                 && (zip_header.formatted.zip_flags & SWAP_LE16(0x0008))
 585                ) {
 586                        /* If it's a streaming zip, we _require_ CDF */
 587                        bb_error_msg_and_die("can't find file table");
 588                }
 589#endif
 590
 591                /* Read filename */
 592                free(dst_fn);
 593                dst_fn = xzalloc(zip_header.formatted.filename_len + 1);
 594                xread(zip_fd, dst_fn, zip_header.formatted.filename_len);
 595
 596                /* Skip extra header bytes */
 597                unzip_skip(zip_header.formatted.extra_len);
 598
 599                /* Filter zip entries */
 600                if (find_list_entry(zreject, dst_fn)
 601                 || (zaccept && !find_list_entry(zaccept, dst_fn))
 602                ) { /* Skip entry */
 603                        i = 'n';
 604
 605                } else { /* Extract entry */
 606                        if (listing) { /* List entry */
 607                                unsigned dostime = zip_header.formatted.modtime | (zip_header.formatted.moddate << 16);
 608                                if (!verbose) {
 609                                        //      "  Length     Date   Time    Name\n"
 610                                        //      " --------    ----   ----    ----"
 611                                        printf(       "%9u  %02u-%02u-%02u %02u:%02u   %s\n",
 612                                                (unsigned)zip_header.formatted.ucmpsize,
 613                                                (dostime & 0x01e00000) >> 21,
 614                                                (dostime & 0x001f0000) >> 16,
 615                                                (((dostime & 0xfe000000) >> 25) + 1980) % 100,
 616                                                (dostime & 0x0000f800) >> 11,
 617                                                (dostime & 0x000007e0) >> 5,
 618                                                dst_fn);
 619                                        total_usize += zip_header.formatted.ucmpsize;
 620                                } else {
 621                                        unsigned long percents = zip_header.formatted.ucmpsize - zip_header.formatted.cmpsize;
 622                                        percents = percents * 100;
 623                                        if (zip_header.formatted.ucmpsize)
 624                                                percents /= zip_header.formatted.ucmpsize;
 625                                        //      " Length   Method    Size  Ratio   Date   Time   CRC-32    Name\n"
 626                                        //      "--------  ------  ------- -----   ----   ----   ------    ----"
 627                                        printf(      "%8u  Defl:N"    "%9u%4u%%  %02u-%02u-%02u %02u:%02u  %08x  %s\n",
 628                                                (unsigned)zip_header.formatted.ucmpsize,
 629                                                (unsigned)zip_header.formatted.cmpsize,
 630                                                (unsigned)percents,
 631                                                (dostime & 0x01e00000) >> 21,
 632                                                (dostime & 0x001f0000) >> 16,
 633                                                (((dostime & 0xfe000000) >> 25) + 1980) % 100,
 634                                                (dostime & 0x0000f800) >> 11,
 635                                                (dostime & 0x000007e0) >> 5,
 636                                                zip_header.formatted.crc32,
 637                                                dst_fn);
 638                                        total_usize += zip_header.formatted.ucmpsize;
 639                                        total_size += zip_header.formatted.cmpsize;
 640                                }
 641                                i = 'n';
 642                        } else if (dst_fd == STDOUT_FILENO) { /* Extracting to STDOUT */
 643                                i = -1;
 644                        } else if (last_char_is(dst_fn, '/')) { /* Extract directory */
 645                                if (stat(dst_fn, &stat_buf) == -1) {
 646                                        if (errno != ENOENT) {
 647                                                bb_perror_msg_and_die("can't stat '%s'", dst_fn);
 648                                        }
 649                                        if (!quiet) {
 650                                                printf("   creating: %s\n", dst_fn);
 651                                        }
 652                                        unzip_create_leading_dirs(dst_fn);
 653                                        if (bb_make_directory(dst_fn, dir_mode, FILEUTILS_IGNORE_CHMOD_ERR)) {
 654                                                xfunc_die();
 655                                        }
 656                                } else {
 657                                        if (!S_ISDIR(stat_buf.st_mode)) {
 658                                                bb_error_msg_and_die("'%s' exists but is not directory", dst_fn);
 659                                        }
 660                                }
 661                                i = 'n';
 662
 663                        } else {  /* Extract file */
 664 check_file:
 665                                if (stat(dst_fn, &stat_buf) == -1) { /* File does not exist */
 666                                        if (errno != ENOENT) {
 667                                                bb_perror_msg_and_die("can't stat '%s'", dst_fn);
 668                                        }
 669                                        i = 'y';
 670                                } else { /* File already exists */
 671                                        if (overwrite == O_NEVER) {
 672                                                i = 'n';
 673                                        } else if (S_ISREG(stat_buf.st_mode)) { /* File is regular file */
 674                                                if (overwrite == O_ALWAYS) {
 675                                                        i = 'y';
 676                                                } else {
 677                                                        printf("replace %s? [y]es, [n]o, [A]ll, [N]one, [r]ename: ", dst_fn);
 678                                                        my_fgets80(key_buf);
 679                                                        i = key_buf[0];
 680                                                }
 681                                        } else { /* File is not regular file */
 682                                                bb_error_msg_and_die("'%s' exists but is not regular file", dst_fn);
 683                                        }
 684                                }
 685                        }
 686                }
 687
 688                switch (i) {
 689                case 'A':
 690                        overwrite = O_ALWAYS;
 691                case 'y': /* Open file and fall into unzip */
 692                        unzip_create_leading_dirs(dst_fn);
 693#if ENABLE_DESKTOP
 694                        dst_fd = xopen3(dst_fn, O_WRONLY | O_CREAT | O_TRUNC, file_mode);
 695#else
 696                        dst_fd = xopen(dst_fn, O_WRONLY | O_CREAT | O_TRUNC);
 697#endif
 698                case -1: /* Unzip */
 699                        if (!quiet) {
 700                                printf("  inflating: %s\n", dst_fn);
 701                        }
 702                        unzip_extract(&zip_header, dst_fd);
 703                        if (dst_fd != STDOUT_FILENO) {
 704                                /* closing STDOUT is potentially bad for future business */
 705                                close(dst_fd);
 706                        }
 707                        break;
 708
 709                case 'N':
 710                        overwrite = O_NEVER;
 711                case 'n':
 712                        /* Skip entry data */
 713                        unzip_skip(zip_header.formatted.cmpsize);
 714                        break;
 715
 716                case 'r':
 717                        /* Prompt for new name */
 718                        printf("new name: ");
 719                        my_fgets80(key_buf);
 720                        free(dst_fn);
 721                        dst_fn = xstrdup(key_buf);
 722                        chomp(dst_fn);
 723                        goto check_file;
 724
 725                default:
 726                        printf("error: invalid response [%c]\n", (char)i);
 727                        goto check_file;
 728                }
 729
 730                total_entries++;
 731        }
 732
 733        if (listing && quiet <= 1) {
 734                if (!verbose) {
 735                        //      "  Length     Date   Time    Name\n"
 736                        //      " --------    ----   ----    ----"
 737                        printf( " --------                   -------\n"
 738                                "%9lu"   "                   %u files\n",
 739                                total_usize, total_entries);
 740                } else {
 741                        unsigned long percents = total_usize - total_size;
 742                        percents = percents * 100;
 743                        if (total_usize)
 744                                percents /= total_usize;
 745                        //      " Length   Method    Size  Ratio   Date   Time   CRC-32    Name\n"
 746                        //      "--------  ------  ------- -----   ----   ----   ------    ----"
 747                        printf( "--------          -------  ---                            -------\n"
 748                                "%8lu"              "%17lu%4u%%                            %u files\n",
 749                                total_usize, total_size, (unsigned)percents,
 750                                total_entries);
 751                }
 752        }
 753
 754        return 0;
 755}
 756