"Fossies" - the Fresh Open Source Software Archive

Member "rufus-3.13/src/bled/decompress_unzip.c" (20 Nov 2020, 11361 Bytes) of package /linux/misc/rufus-3.13.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "decompress_unzip.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 3.12_vs_3.13.

    1 /*
    2  * unzip implementation for Bled/busybox
    3  *
    4  * Copyright © 2015-2020 Pete Batard <pete@akeo.ie>
    5  * Based on mini unzip implementation for busybox © Ed Clark
    6  * Loosely based on original busybox unzip applet © Laurence Anderson.
    7  *
    8  * Licensed under GPLv2 or later, see file LICENSE in this source tree.
    9  */
   10 
   11 /* The following are not currently supported:
   12  * - Store (uncompressed)
   13  * - Zip64
   14  */
   15 
   16 #include "libbb.h"
   17 #include "bb_archive.h"
   18 
   19 enum {
   20 #if BB_BIG_ENDIAN
   21     ZIP_FILEHEADER_MAGIC = 0x504b0304,
   22     ZIP_CDF_MAGIC        = 0x504b0102, /* central directory's file header */
   23     ZIP_CDE_MAGIC        = 0x504b0506, /* "end of central directory" record */
   24     ZIP_DD_MAGIC         = 0x504b0708,
   25 #else
   26     ZIP_FILEHEADER_MAGIC = 0x04034b50,
   27     ZIP_CDF_MAGIC        = 0x02014b50,
   28     ZIP_CDE_MAGIC        = 0x06054b50,
   29     ZIP_DD_MAGIC         = 0x08074b50,
   30 #endif
   31 };
   32 
   33 #define ZIP_HEADER_LEN 26
   34 
   35 PRAGMA_BEGIN_PACKED
   36 typedef union {
   37     uint8_t raw[ZIP_HEADER_LEN];
   38     struct {
   39         uint16_t version;               /* 0-1 */
   40         uint16_t zip_flags;             /* 2-3 */
   41         uint16_t method;                /* 4-5 */
   42         uint16_t modtime;               /* 6-7 */
   43         uint16_t moddate;               /* 8-9 */
   44         uint32_t crc32;                 /* 10-13 */
   45         uint32_t cmpsize;               /* 14-17 */
   46         uint32_t ucmpsize;              /* 18-21 */
   47         uint16_t filename_len;          /* 22-23 */
   48         uint16_t extra_len;             /* 24-25 */
   49     } PACKED formatted;
   50 } PACKED zip_header_t;
   51 PRAGMA_END_PACKED
   52 
   53 /* Check the offset of the last element, not the length.  This leniency
   54  * allows for poor packing, whereby the overall struct may be too long,
   55  * even though the elements are all in the right place.
   56  */
   57 struct BUG_zip_header_must_be_26_bytes {
   58     char BUG_zip_header_must_be_26_bytes[
   59         offsetof(zip_header_t, formatted.extra_len) + 2
   60             == ZIP_HEADER_LEN ? 1 : -1];
   61 };
   62 
   63 #define FIX_ENDIANNESS_ZIP(zip_header) do { \
   64     (zip_header).formatted.version      = SWAP_LE16((zip_header).formatted.version     ); \
   65     (zip_header).formatted.method       = SWAP_LE16((zip_header).formatted.method      ); \
   66     (zip_header).formatted.modtime      = SWAP_LE16((zip_header).formatted.modtime     ); \
   67     (zip_header).formatted.moddate      = SWAP_LE16((zip_header).formatted.moddate     ); \
   68     (zip_header).formatted.crc32        = SWAP_LE32((zip_header).formatted.crc32       ); \
   69     (zip_header).formatted.cmpsize      = SWAP_LE32((zip_header).formatted.cmpsize     ); \
   70     (zip_header).formatted.ucmpsize     = SWAP_LE32((zip_header).formatted.ucmpsize    ); \
   71     (zip_header).formatted.filename_len = SWAP_LE16((zip_header).formatted.filename_len); \
   72     (zip_header).formatted.extra_len    = SWAP_LE16((zip_header).formatted.extra_len   ); \
   73 } while (0)
   74 
   75 #define CDF_HEADER_LEN 42
   76 
   77 PRAGMA_BEGIN_PACKED
   78 typedef union {
   79     uint8_t raw[CDF_HEADER_LEN];
   80     struct {
   81         /* uint32_t signature; 50 4b 01 02 */
   82         uint16_t version_made_by;       /* 0-1 */
   83         uint16_t version_needed;        /* 2-3 */
   84         uint16_t cdf_flags;             /* 4-5 */
   85         uint16_t method;                /* 6-7 */
   86         uint16_t mtime;                 /* 8-9 */
   87         uint16_t mdate;                 /* 10-11 */
   88         uint32_t crc32;                 /* 12-15 */
   89         uint32_t cmpsize;               /* 16-19 */
   90         uint32_t ucmpsize;              /* 20-23 */
   91         uint16_t file_name_length;      /* 24-25 */
   92         uint16_t extra_field_length;    /* 26-27 */
   93         uint16_t file_comment_length;   /* 28-29 */
   94         uint16_t disk_number_start;     /* 30-31 */
   95         uint16_t internal_file_attributes; /* 32-33 */
   96         uint32_t external_file_attributes; /* 34-37 */
   97         uint32_t relative_offset_of_local_header; /* 38-41 */
   98     } PACKED formatted;
   99 } cdf_header_t;
  100 PRAGMA_END_PACKED
  101 
  102 struct BUG_cdf_header_must_be_42_bytes {
  103     char BUG_cdf_header_must_be_42_bytes[
  104         offsetof(cdf_header_t, formatted.relative_offset_of_local_header) + 4
  105             == CDF_HEADER_LEN ? 1 : -1];
  106 };
  107 
  108 #define FIX_ENDIANNESS_CDF(cdf_header) do { \
  109     (cdf_header).formatted.crc32        = SWAP_LE32((cdf_header).formatted.crc32       ); \
  110     (cdf_header).formatted.cmpsize      = SWAP_LE32((cdf_header).formatted.cmpsize     ); \
  111     (cdf_header).formatted.ucmpsize     = SWAP_LE32((cdf_header).formatted.ucmpsize    ); \
  112     (cdf_header).formatted.file_name_length = SWAP_LE16((cdf_header).formatted.file_name_length); \
  113     (cdf_header).formatted.extra_field_length = SWAP_LE16((cdf_header).formatted.extra_field_length); \
  114     (cdf_header).formatted.file_comment_length = SWAP_LE16((cdf_header).formatted.file_comment_length); \
  115     IF_DESKTOP( \
  116     (cdf_header).formatted.version_made_by = SWAP_LE16((cdf_header).formatted.version_made_by); \
  117     (cdf_header).formatted.external_file_attributes = SWAP_LE32((cdf_header).formatted.external_file_attributes); \
  118     ) \
  119 } while (0)
  120 
  121 #define CDE_HEADER_LEN 16
  122 
  123 PRAGMA_BEGIN_PACKED
  124 typedef union {
  125     uint8_t raw[CDE_HEADER_LEN];
  126     struct {
  127         /* uint32_t signature; 50 4b 05 06 */
  128         uint16_t this_disk_no;
  129         uint16_t disk_with_cdf_no;
  130         uint16_t cdf_entries_on_this_disk;
  131         uint16_t cdf_entries_total;
  132         uint32_t cdf_size;
  133         uint32_t cdf_offset;
  134         /* uint16_t file_comment_length; */
  135         /* .ZIP file comment (variable size) */
  136     } PACKED formatted;
  137 } cde_header_t;
  138 PRAGMA_END_PACKED
  139 
  140 struct BUG_cde_header_must_be_16_bytes {
  141     char BUG_cde_header_must_be_16_bytes[
  142         sizeof(cde_header_t) == CDE_HEADER_LEN ? 1 : -1];
  143 };
  144 
  145 #define FIX_ENDIANNESS_CDE(cde_header) do { \
  146     (cde_header).formatted.cdf_offset = SWAP_LE32((cde_header).formatted.cdf_offset); \
  147 } while (0)
  148 
  149 
  150 #if ENABLE_DESKTOP
  151 
  152 /* Seen in the wild:
  153  * Self-extracting PRO2K3XP_32.exe contains 19078464 byte zip archive,
  154  * where CDE was nearly 48 kbytes before EOF.
  155  * (Surprisingly, it also apparently has *another* CDE structure
  156  * closer to the end, with bogus cdf_offset).
  157  * To make extraction work, bumped PEEK_FROM_END from 16k to 64k.
  158  */
  159 #define PEEK_FROM_END (64*1024)
  160 
  161 /* This value means that we failed to find CDF */
  162 #define BAD_CDF_OFFSET ((uint32_t)0xffffffff)
  163 
  164 /* NB: does not preserve file position! */
  165 static uint32_t find_cdf_offset(int fd)
  166 {
  167     cde_header_t cde_header;
  168     unsigned char *p;
  169     off_t end;
  170     unsigned char *buf = xzalloc(PEEK_FROM_END);
  171 
  172     if (buf == NULL)
  173         return 0;
  174     end = lseek(fd, 0, SEEK_END);
  175     end -= PEEK_FROM_END;
  176     if (end < 0)
  177         end = 0;
  178     lseek(fd, end, SEEK_SET);
  179     full_read(fd, buf, PEEK_FROM_END);
  180 
  181     cde_header.formatted.cdf_offset = BAD_CDF_OFFSET;
  182     p = buf;
  183     while (p <= buf + PEEK_FROM_END - CDE_HEADER_LEN - 4) {
  184         if (*p != 'P') {
  185             p++;
  186             continue;
  187         }
  188         if (*++p != 'K')
  189             continue;
  190         if (*++p != 5)
  191             continue;
  192         if (*++p != 6)
  193             continue;
  194         /* we found CDE! */
  195         memcpy(cde_header.raw, p + 1, CDE_HEADER_LEN);
  196         FIX_ENDIANNESS_CDE(cde_header);
  197         /*
  198          * I've seen .ZIP files with seemingly valid CDEs
  199          * where cdf_offset points past EOF - ??
  200          * Ignore such CDEs:
  201          */
  202         if (cde_header.formatted.cdf_offset < end + (p - buf))
  203             break;
  204         cde_header.formatted.cdf_offset = BAD_CDF_OFFSET;
  205     }
  206     free(buf);
  207     return cde_header.formatted.cdf_offset;
  208 };
  209 
  210 static uint32_t read_next_cdf(int fd, uint32_t cdf_offset, cdf_header_t *cdf_ptr)
  211 {
  212     off_t org;
  213 
  214     org = lseek(fd, 0, SEEK_CUR);
  215 
  216     if (!cdf_offset)
  217         cdf_offset = find_cdf_offset(fd);
  218 
  219     if (cdf_offset != BAD_CDF_OFFSET) {
  220         lseek(fd, cdf_offset + 4, SEEK_SET);
  221         if (_read(fd, cdf_ptr->raw, CDF_HEADER_LEN) != CDF_HEADER_LEN)
  222             return 0;
  223         FIX_ENDIANNESS_CDF(*cdf_ptr);
  224         cdf_offset += 4 + CDF_HEADER_LEN
  225             + cdf_ptr->formatted.file_name_length
  226             + cdf_ptr->formatted.extra_field_length
  227             + cdf_ptr->formatted.file_comment_length;
  228     }
  229 
  230     lseek(fd, org, SEEK_SET);
  231     return cdf_offset;
  232 };
  233 #endif
  234 
  235 static void unzip_skip(int fd, off_t skip)
  236 {
  237     if (skip != 0)
  238         if (lseek(fd, skip, SEEK_CUR) == (off_t)-1)
  239             bb_copyfd_exact_size(fd, -1, skip);
  240 }
  241 
  242 IF_DESKTOP(long long) int FAST_FUNC unpack_zip_stream(transformer_state_t *xstate)
  243 {
  244     IF_DESKTOP(long long) int n = -EFAULT;
  245     zip_header_t zip_header;
  246 #if ENABLE_DESKTOP
  247     uint32_t cdf_offset = 0;
  248 #endif
  249 
  250     while (1) {
  251         uint32_t magic;
  252         bool is_dir = false;
  253         /* Check magic number */
  254         safe_read(xstate->src_fd, &magic, 4);
  255         /* Central directory? It's at the end, so exit */
  256         if (magic == ZIP_CDF_MAGIC)
  257             break;
  258 #if ENABLE_DESKTOP
  259         /* Data descriptor? It was a streaming file, go on */
  260         if (magic == ZIP_DD_MAGIC) {
  261             /* skip over duplicate crc32, cmpsize and ucmpsize */
  262             unzip_skip(xstate->src_fd, 3 * 4);
  263             continue;
  264         }
  265 #endif
  266         if (magic != ZIP_FILEHEADER_MAGIC)
  267             bb_error_msg_and_err("invalid zip magic 0x%08X", magic);
  268 
  269         /* Read the file header */
  270         safe_read(xstate->src_fd, zip_header.raw, ZIP_HEADER_LEN);
  271         FIX_ENDIANNESS_ZIP(zip_header);
  272         if ((zip_header.formatted.method != 8) && (zip_header.formatted.method != 0)) {
  273             bb_error_msg_and_err("zip method method %d is not supported", zip_header.formatted.method);
  274         }
  275 #if !ENABLE_DESKTOP
  276         if (zip_header.formatted.zip_flags & SWAP_LE16(0x0009)) {
  277             bb_error_msg_and_err("zip flags 1 and 8 are not supported");
  278         }
  279 #else
  280         if (zip_header.formatted.zip_flags & SWAP_LE16(0x0001)) {
  281             /* 0x0001 - encrypted */
  282             bb_error_msg_and_die("zip flag 1 (encryption) is not supported");
  283         }
  284 
  285         if (cdf_offset != BAD_CDF_OFFSET) {
  286             cdf_header_t cdf_header;
  287             cdf_offset = read_next_cdf(xstate->src_fd, cdf_offset, &cdf_header);
  288             /*
  289              * Note: cdf_offset can become BAD_CDF_OFFSET after the above call.
  290              */
  291             if (zip_header.formatted.zip_flags & SWAP_LE16(0x0008)) {
  292                 /* 0x0008 - streaming. [u]cmpsize can be reliably gotten
  293                  * only from Central Directory. See unzip_doc.txt
  294                  */
  295                 zip_header.formatted.crc32    = cdf_header.formatted.crc32;
  296                 zip_header.formatted.cmpsize  = cdf_header.formatted.cmpsize;
  297                 zip_header.formatted.ucmpsize = cdf_header.formatted.ucmpsize;
  298             }
  299             /* Check for UNIX/DOS/WIN directory */
  300             is_dir = cdf_header.formatted.external_file_attributes & 0x40000010;
  301         }
  302         if (cdf_offset == BAD_CDF_OFFSET
  303             && (zip_header.formatted.zip_flags & SWAP_LE16(0x0008))
  304         ) {
  305             /* If it's a streaming zip, we _require_ CDF */
  306             bb_error_msg_and_die("can't find file table");
  307         }
  308 #endif
  309 
  310         /* Handle multiple file switching */
  311         if ((!is_dir) && (xstate->dst_dir != NULL)) {
  312             xstate->dst_size = zip_header.formatted.ucmpsize;
  313             xstate->dst_name = xzalloc(zip_header.formatted.filename_len + 1);
  314             safe_read(xstate->src_fd, xstate->dst_name, zip_header.formatted.filename_len);
  315             n = transformer_switch_file(xstate);
  316             free(xstate->dst_name);
  317             if (n < 0)
  318                 goto err;
  319         } else {
  320             unzip_skip(xstate->src_fd, zip_header.formatted.filename_len);
  321         }
  322 
  323         /* Skip extra header bytes */
  324         unzip_skip(xstate->src_fd, zip_header.formatted.extra_len);
  325 
  326         if (zip_header.formatted.method == 0) {
  327             if (!is_dir)
  328                 bb_error_msg_and_err("zip method method 0 is only supported for directories");
  329         } else {
  330             /* Method 8 - inflate */
  331             xstate->bytes_in = zip_header.formatted.cmpsize;
  332             n = inflate_unzip(xstate);
  333 
  334             /* Validate decompression */
  335             if (n >= 0) {
  336                 if (zip_header.formatted.ucmpsize != xstate->bytes_out)
  337                     bb_error_msg_and_err("bad length");
  338                 else if (zip_header.formatted.crc32 != (xstate->crc32 ^ 0xffffffffL))
  339                     bb_error_msg_and_err("crc error");
  340             } else if (n != -ENOSPC) {
  341                 bb_error_msg_and_err("inflate error");
  342             }
  343         }
  344         /* Only process the first file if not extracting to a dir */
  345         if (xstate->dst_dir == NULL)
  346             break;
  347     }
  348 
  349 err:
  350     if (n > 0)
  351         return xstate->bytes_out;
  352     else if (n == -ENOSPC)
  353         return xstate->mem_output_size_max;
  354     else
  355         return n;
  356 }