Changeset 3621 in MondoRescue for branches/3.3/mindi-busybox/archival/unzip.c


Ignore:
Timestamp:
Dec 20, 2016, 4:07:32 PM (7 years ago)
Author:
Bruno Cornec
Message:

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

Location:
branches/3.3
Files:
1 edited
1 copied

Legend:

Unmodified
Added
Removed
  • branches/3.3/mindi-busybox/archival/unzip.c

    r3232 r3621  
    1010 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
    1111 */
    12 
    1312/* For reference see
    1413 * http://www.pkware.com/company/standards/appnote/
    1514 * http://www.info-zip.org/pub/infozip/doc/appnote-iz-latest.zip
    16  */
    17 
    18 /* TODO
     15 *
     16 * TODO
    1917 * Zip64 + other methods
    2018 */
     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
    2132
    2233//usage:#define unzip_trivial_usage
     
    3445#include "libbb.h"
    3546#include "bb_archive.h"
     47
     48#if 0
     49# define dbg(...) bb_error_msg(__VA_ARGS__)
     50#else
     51# define dbg(...) ((void)0)
     52#endif
    3653
    3754enum {
     
    164181#if ENABLE_DESKTOP
    165182
    166 #define PEEK_FROM_END 16384
     183/* Seen in the wild:
     184 * Self-extracting PRO2K3XP_32.exe contains 19078464 byte zip archive,
     185 * where CDE was nearly 48 kbytes before EOF.
     186 * (Surprisingly, it also apparently has *another* CDE structure
     187 * closer to the end, with bogus cdf_offset).
     188 * To make extraction work, bumped PEEK_FROM_END from 16k to 64k.
     189 */
     190#define PEEK_FROM_END (64*1024)
     191
     192/* This value means that we failed to find CDF */
     193#define BAD_CDF_OFFSET ((uint32_t)0xffffffff)
    167194
    168195/* NB: does not preserve file position! */
     
    173200    off_t end;
    174201    unsigned char *buf = xzalloc(PEEK_FROM_END);
     202    uint32_t found;
    175203
    176204    end = xlseek(zip_fd, 0, SEEK_END);
     
    178206    if (end < 0)
    179207        end = 0;
    180     xlseek(zip_fd, end, SEEK_SET);
     208    dbg("Looking for cdf_offset starting from 0x%"OFF_FMT"x", end);
     209    xlseek(zip_fd, end, SEEK_SET);
    181210    full_read(zip_fd, buf, PEEK_FROM_END);
    182211
     212    found = BAD_CDF_OFFSET;
    183213    p = buf;
    184214    while (p <= buf + PEEK_FROM_END - CDE_HEADER_LEN - 4) {
     
    196226        memcpy(cde_header.raw, p + 1, CDE_HEADER_LEN);
    197227        FIX_ENDIANNESS_CDE(cde_header);
    198         free(buf);
    199         return cde_header.formatted.cdf_offset;
    200     }
    201     //free(buf);
    202     bb_error_msg_and_die("can't find file table");
     228        /*
     229         * I've seen .ZIP files with seemingly valid CDEs
     230         * where cdf_offset points past EOF - ??
     231         * This check ignores such CDEs:
     232         */
     233        if (cde_header.formatted.cdf_offset < end + (p - buf)) {
     234            found = cde_header.formatted.cdf_offset;
     235            dbg("Possible cdf_offset:0x%x at 0x%"OFF_FMT"x",
     236                (unsigned)found, end + (p-3 - buf));
     237            dbg("  cdf_offset+cdf_size:0x%x",
     238                (unsigned)(found + SWAP_LE32(cde_header.formatted.cdf_size)));
     239            /*
     240             * We do not "break" here because only the last CDE is valid.
     241             * I've seen a .zip archive which contained a .zip file,
     242             * uncompressed, and taking the first CDE was using
     243             * the CDE inside that file!
     244             */
     245        }
     246    }
     247    free(buf);
     248    dbg("Found cdf_offset:0x%x", (unsigned)found);
     249    return found;
    203250};
    204251
     
    212259        cdf_offset = find_cdf_offset();
    213260
    214     xlseek(zip_fd, cdf_offset + 4, SEEK_SET);
    215     xread(zip_fd, cdf_ptr->raw, CDF_HEADER_LEN);
    216     FIX_ENDIANNESS_CDF(*cdf_ptr);
    217     cdf_offset += 4 + CDF_HEADER_LEN
    218         + cdf_ptr->formatted.file_name_length
    219         + cdf_ptr->formatted.extra_field_length
    220         + cdf_ptr->formatted.file_comment_length;
    221 
     261    if (cdf_offset != BAD_CDF_OFFSET) {
     262        dbg("Reading CDF at 0x%x", (unsigned)cdf_offset);
     263        xlseek(zip_fd, cdf_offset + 4, SEEK_SET);
     264        xread(zip_fd, cdf_ptr->raw, CDF_HEADER_LEN);
     265        FIX_ENDIANNESS_CDF(*cdf_ptr);
     266        dbg("  file_name_length:%u extra_field_length:%u file_comment_length:%u",
     267            (unsigned)cdf_ptr->formatted.file_name_length,
     268            (unsigned)cdf_ptr->formatted.extra_field_length,
     269            (unsigned)cdf_ptr->formatted.file_comment_length
     270        );
     271        cdf_offset += 4 + CDF_HEADER_LEN
     272            + cdf_ptr->formatted.file_name_length
     273            + cdf_ptr->formatted.extra_field_length
     274            + cdf_ptr->formatted.file_comment_length;
     275    }
     276
     277    dbg("Returning file position to 0x%"OFF_FMT"x", org);
    222278    xlseek(zip_fd, org, SEEK_SET);
    223279    return cdf_offset;
     
    227283static void unzip_skip(off_t skip)
    228284{
    229     if (lseek(zip_fd, skip, SEEK_CUR) == (off_t)-1)
    230         bb_copyfd_exact_size(zip_fd, -1, skip);
     285    if (skip != 0)
     286        if (lseek(zip_fd, skip, SEEK_CUR) == (off_t)-1)
     287            bb_copyfd_exact_size(zip_fd, -1, skip);
    231288}
    232289
     
    250307    } else {
    251308        /* Method 8 - inflate */
    252         transformer_aux_data_t aux;
    253         init_transformer_aux_data(&aux);
    254         aux.bytes_in = zip_header->formatted.cmpsize;
    255         if (inflate_unzip(&aux, zip_fd, dst_fd) < 0)
     309        transformer_state_t xstate;
     310        init_transformer_state(&xstate);
     311        xstate.bytes_in = zip_header->formatted.cmpsize;
     312        xstate.src_fd = zip_fd;
     313        xstate.dst_fd = dst_fd;
     314        if (inflate_unzip(&xstate) < 0)
    256315            bb_error_msg_and_die("inflate error");
    257316        /* Validate decompression - crc */
    258         if (zip_header->formatted.crc32 != (aux.crc32 ^ 0xffffffffL)) {
     317        if (zip_header->formatted.crc32 != (xstate.crc32 ^ 0xffffffffL)) {
    259318            bb_error_msg_and_die("crc error");
    260319        }
    261320        /* Validate decompression - size */
    262         if (zip_header->formatted.ucmpsize != aux.bytes_out) {
     321        if (zip_header->formatted.ucmpsize != xstate.bytes_out) {
    263322            /* Don't die. Who knows, maybe len calculation
    264323             * was botched somewhere. After all, crc matched! */
    265324            bb_error_msg("bad length");
    266325        }
     326    }
     327}
     328
     329static void my_fgets80(char *buf80)
     330{
     331    fflush_all();
     332    if (!fgets(buf80, 80, stdin)) {
     333        bb_perror_msg_and_die("can't read standard input");
    267334    }
    268335}
     
    292359    char *base_dir = NULL;
    293360    int i, opt;
    294     char key_buf[80];
     361    char key_buf[80]; /* must match size used by my_fgets80 */
    295362    struct stat stat_buf;
    296363
     
    421488            overwrite = O_NEVER;
    422489    } else {
    423         static const char extn[][5] = { ".zip", ".ZIP" };
     490        static const char extn[][5] ALIGN1 = { ".zip", ".ZIP" };
    424491        char *ext = src_fn + strlen(src_fn);
    425492        int src_fd;
     
    448515        if (listing) {
    449516            puts(verbose ?
    450                 " Length   Method    Size  Ratio   Date   Time   CRC-32    Name\n"
    451                 "--------  ------  ------- -----   ----   ----   ------    ----"
     517                " Length   Method    Size  Cmpr    Date    Time   CRC-32   Name\n"
     518                "--------  ------  ------- ---- ---------- ----- --------  ----"
    452519                :
    453                 "  Length     Date   Time    Name\n"
    454                 " --------    ----   ----    ----"
     520                "  Length      Date    Time    Name\n"
     521                "---------  ---------- -----   ----"
    455522                );
    456523        }
     
    492559        xread(zip_fd, &magic, 4);
    493560        /* Central directory? It's at the end, so exit */
    494         if (magic == ZIP_CDF_MAGIC)
    495             break;
     561        if (magic == ZIP_CDF_MAGIC) {
     562            dbg("got ZIP_CDF_MAGIC");
     563            break;
     564        }
    496565#if ENABLE_DESKTOP
    497566        /* Data descriptor? It was a streaming file, go on */
    498567        if (magic == ZIP_DD_MAGIC) {
     568            dbg("got ZIP_DD_MAGIC");
    499569            /* skip over duplicate crc32, cmpsize and ucmpsize */
    500570            unzip_skip(3 * 4);
     
    504574        if (magic != ZIP_FILEHEADER_MAGIC)
    505575            bb_error_msg_and_die("invalid zip magic %08X", (int)magic);
     576        dbg("got ZIP_FILEHEADER_MAGIC");
    506577
    507578        /* Read the file header */
     
    521592        }
    522593
    523         {
     594        if (cdf_offset != BAD_CDF_OFFSET) {
    524595            cdf_header_t cdf_header;
    525596            cdf_offset = read_next_cdf(cdf_offset, &cdf_header);
     597            /*
     598             * Note: cdf_offset can become BAD_CDF_OFFSET after the above call.
     599             */
    526600            if (zip_header.formatted.zip_flags & SWAP_LE16(0x0008)) {
    527601                /* 0x0008 - streaming. [u]cmpsize can be reliably gotten
    528                  * only from Central Directory. See unzip_doc.txt */
     602                 * only from Central Directory. See unzip_doc.txt
     603                 */
    529604                zip_header.formatted.crc32    = cdf_header.formatted.crc32;
    530605                zip_header.formatted.cmpsize  = cdf_header.formatted.cmpsize;
     
    532607            }
    533608            if ((cdf_header.formatted.version_made_by >> 8) == 3) {
    534                 /* this archive is created on Unix */
     609                /* This archive is created on Unix */
    535610                dir_mode = file_mode = (cdf_header.formatted.external_file_attributes >> 16);
    536611            }
    537612        }
    538 #endif
     613        if (cdf_offset == BAD_CDF_OFFSET
     614         && (zip_header.formatted.zip_flags & SWAP_LE16(0x0008))
     615        ) {
     616            /* If it's a streaming zip, we _require_ CDF */
     617            bb_error_msg_and_die("can't find file table");
     618        }
     619#endif
     620        dbg("File cmpsize:0x%x extra_len:0x%x ucmpsize:0x%x",
     621            (unsigned)zip_header.formatted.cmpsize,
     622            (unsigned)zip_header.formatted.extra_len,
     623            (unsigned)zip_header.formatted.ucmpsize
     624        );
    539625
    540626        /* Read filename */
     
    545631        /* Skip extra header bytes */
    546632        unzip_skip(zip_header.formatted.extra_len);
     633
     634        /* Guard against "/abspath", "/../" and similar attacks */
     635        overlapping_strcpy(dst_fn, strip_unsafe_prefix(dst_fn));
    547636
    548637        /* Filter zip entries */
     
    551640        ) { /* Skip entry */
    552641            i = 'n';
    553 
    554         } else { /* Extract entry */
    555             if (listing) { /* List entry */
    556                 unsigned dostime = zip_header.formatted.modtime | (zip_header.formatted.moddate << 16);
     642        } else {
     643            if (listing) {
     644                /* List entry */
     645                char dtbuf[sizeof("mm-dd-yyyy hh:mm")];
     646                sprintf(dtbuf, "%02u-%02u-%04u %02u:%02u",
     647                    (zip_header.formatted.moddate >> 5) & 0xf,  // mm: 0x01e0
     648                    (zip_header.formatted.moddate)      & 0x1f, // dd: 0x001f
     649                    (zip_header.formatted.moddate >> 9) + 1980, // yy: 0xfe00
     650                    (zip_header.formatted.modtime >> 11),       // hh: 0xf800
     651                    (zip_header.formatted.modtime >> 5) & 0x3f  // mm: 0x07e0
     652                    // seconds/2 are not shown, encoded in ----------- 0x001f
     653                );
    557654                if (!verbose) {
    558                     //      "  Length     Date   Time    Name\n"
    559                     //      " --------    ----   ----    ----"
    560                     printf(       "%9u  %02u-%02u-%02u %02u:%02u   %s\n",
     655                    //      "  Length      Date    Time    Name\n"
     656                    //      "---------  ---------- -----   ----"
     657                    printf(       "%9u  " "%s   "         "%s\n",
    561658                        (unsigned)zip_header.formatted.ucmpsize,
    562                         (dostime & 0x01e00000) >> 21,
    563                         (dostime & 0x001f0000) >> 16,
    564                         (((dostime & 0xfe000000) >> 25) + 1980) % 100,
    565                         (dostime & 0x0000f800) >> 11,
    566                         (dostime & 0x000007e0) >> 5,
     659                        dtbuf,
    567660                        dst_fn);
    568                     total_usize += zip_header.formatted.ucmpsize;
    569661                } else {
    570662                    unsigned long percents = zip_header.formatted.ucmpsize - zip_header.formatted.cmpsize;
     663                    if ((int32_t)percents < 0)
     664                        percents = 0; /* happens if ucmpsize < cmpsize */
    571665                    percents = percents * 100;
    572666                    if (zip_header.formatted.ucmpsize)
    573667                        percents /= zip_header.formatted.ucmpsize;
    574                     //      " Length   Method    Size  Ratio   Date   Time   CRC-32    Name\n"
    575                     //      "--------  ------  ------- -----   ----   ----   ------    ----"
    576                     printf(      "%8u  Defl:N"    "%9u%4u%%  %02u-%02u-%02u %02u:%02u  %08x  %s\n",
     668                    //      " Length   Method    Size  Cmpr    Date    Time   CRC-32   Name\n"
     669                    //      "--------  ------  ------- ---- ---------- ----- --------  ----"
     670                    printf(      "%8u  %s"        "%9u%4u%% " "%s "         "%08x  "  "%s\n",
    577671                        (unsigned)zip_header.formatted.ucmpsize,
     672                        zip_header.formatted.method == 0 ? "Stored" : "Defl:N", /* Defl is method 8 */
     673/* TODO: show other methods?
     674 *  1 - Shrunk
     675 *  2 - Reduced with compression factor 1
     676 *  3 - Reduced with compression factor 2
     677 *  4 - Reduced with compression factor 3
     678 *  5 - Reduced with compression factor 4
     679 *  6 - Imploded
     680 *  7 - Reserved for Tokenizing compression algorithm
     681 *  9 - Deflate64
     682 * 10 - PKWARE Data Compression Library Imploding
     683 * 11 - Reserved by PKWARE
     684 * 12 - BZIP2
     685 */
    578686                        (unsigned)zip_header.formatted.cmpsize,
    579687                        (unsigned)percents,
    580                         (dostime & 0x01e00000) >> 21,
    581                         (dostime & 0x001f0000) >> 16,
    582                         (((dostime & 0xfe000000) >> 25) + 1980) % 100,
    583                         (dostime & 0x0000f800) >> 11,
    584                         (dostime & 0x000007e0) >> 5,
     688                        dtbuf,
    585689                        zip_header.formatted.crc32,
    586690                        dst_fn);
    587                     total_usize += zip_header.formatted.ucmpsize;
    588691                    total_size += zip_header.formatted.cmpsize;
    589692                }
     693                total_usize += zip_header.formatted.ucmpsize;
    590694                i = 'n';
    591             } else if (dst_fd == STDOUT_FILENO) { /* Extracting to STDOUT */
     695            } else if (dst_fd == STDOUT_FILENO) {
     696                /* Extracting to STDOUT */
    592697                i = -1;
    593             } else if (last_char_is(dst_fn, '/')) { /* Extract directory */
     698            } else if (last_char_is(dst_fn, '/')) {
     699                /* Extract directory */
    594700                if (stat(dst_fn, &stat_buf) == -1) {
    595701                    if (errno != ENOENT) {
     
    605711                } else {
    606712                    if (!S_ISDIR(stat_buf.st_mode)) {
    607                         bb_error_msg_and_die("'%s' exists but is not directory", dst_fn);
     713                        bb_error_msg_and_die("'%s' exists but is not a %s",
     714                            dst_fn, "directory");
    608715                    }
    609716                }
    610717                i = 'n';
    611 
    612             } else {  /* Extract file */
     718            } else {
     719                /* Extract file */
    613720 check_file:
    614                 if (stat(dst_fn, &stat_buf) == -1) { /* File does not exist */
     721                if (stat(dst_fn, &stat_buf) == -1) {
     722                    /* File does not exist */
    615723                    if (errno != ENOENT) {
    616724                        bb_perror_msg_and_die("can't stat '%s'", dst_fn);
    617725                    }
    618726                    i = 'y';
    619                 } else { /* File already exists */
     727                } else {
     728                    /* File already exists */
    620729                    if (overwrite == O_NEVER) {
    621730                        i = 'n';
    622                     } else if (S_ISREG(stat_buf.st_mode)) { /* File is regular file */
     731                    } else if (S_ISREG(stat_buf.st_mode)) {
     732                        /* File is regular file */
    623733                        if (overwrite == O_ALWAYS) {
    624734                            i = 'y';
    625735                        } else {
    626736                            printf("replace %s? [y]es, [n]o, [A]ll, [N]one, [r]ename: ", dst_fn);
    627                             fflush_all();
    628                             if (!fgets(key_buf, sizeof(key_buf), stdin)) {
    629                                 bb_perror_msg_and_die("can't read input");
    630                             }
     737                            my_fgets80(key_buf);
    631738                            i = key_buf[0];
    632739                        }
    633                     } else { /* File is not regular file */
    634                         bb_error_msg_and_die("'%s' exists but is not regular file", dst_fn);
     740                    } else {
     741                        /* File is not regular file */
     742                        bb_error_msg_and_die("'%s' exists but is not a %s",
     743                            dst_fn, "regular file");
    635744                    }
    636745                }
     
    669778            /* Prompt for new name */
    670779            printf("new name: ");
    671             if (!fgets(key_buf, sizeof(key_buf), stdin)) {
    672                 bb_perror_msg_and_die("can't read input");
    673             }
     780            my_fgets80(key_buf);
    674781            free(dst_fn);
    675782            dst_fn = xstrdup(key_buf);
     
    687794    if (listing && quiet <= 1) {
    688795        if (!verbose) {
    689             //      "  Length     Date   Time    Name\n"
    690             //      " --------    ----   ----    ----"
    691             printf( " --------                   -------\n"
    692                 "%9lu"   "                   %u files\n",
    693                 total_usize, total_entries);
     796            //  "  Length      Date    Time    Name\n"
     797            //  "---------  ---------- -----   ----"
     798            printf( " --------%21s"               "-------\n"
     799                     "%9lu%21s"               "%u files\n",
     800                "",
     801                total_usize, "", total_entries);
    694802        } else {
    695803            unsigned long percents = total_usize - total_size;
     804            if ((long)percents < 0)
     805                percents = 0; /* happens if usize < size */
    696806            percents = percents * 100;
    697807            if (total_usize)
    698808                percents /= total_usize;
    699             //      " Length   Method    Size  Ratio   Date   Time   CRC-32    Name\n"
    700             //      "--------  ------  ------- -----   ----   ----   ------    ----"
    701             printf( "--------          -------  ---                            -------\n"
    702                 "%8lu"              "%17lu%4u%%                            %u files\n",
    703                 total_usize, total_size, (unsigned)percents,
     809            //  " Length   Method    Size  Cmpr    Date    Time   CRC-32   Name\n"
     810            //  "--------  ------  ------- ---- ---------- ----- --------  ----"
     811            printf( "--------          ------- ----%28s"                      "----\n"
     812                "%8lu"              "%17lu%4u%%%28s"                      "%u files\n",
     813                "",
     814                total_usize, total_size, (unsigned)percents, "",
    704815                total_entries);
    705816        }
Note: See TracChangeset for help on using the changeset viewer.