linux/lib/decompress_inflate.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2#ifdef STATIC
   3#define PREBOOT
   4/* Pre-boot environment: included */
   5
   6/* prevent inclusion of _LINUX_KERNEL_H in pre-boot environment: lots
   7 * errors about console_printk etc... on ARM */
   8#define _LINUX_KERNEL_H
   9
  10#include "zlib_inflate/inftrees.c"
  11#include "zlib_inflate/inffast.c"
  12#include "zlib_inflate/inflate.c"
  13#ifdef CONFIG_ZLIB_DFLTCC
  14#include "zlib_dfltcc/dfltcc.c"
  15#include "zlib_dfltcc/dfltcc_inflate.c"
  16#endif
  17
  18#else /* STATIC */
  19/* initramfs et al: linked */
  20
  21#include <linux/zutil.h>
  22
  23#include "zlib_inflate/inftrees.h"
  24#include "zlib_inflate/inffast.h"
  25#include "zlib_inflate/inflate.h"
  26
  27#include "zlib_inflate/infutil.h"
  28#include <linux/decompress/inflate.h>
  29
  30#endif /* STATIC */
  31
  32#include <linux/decompress/mm.h>
  33
  34#define GZIP_IOBUF_SIZE (16*1024)
  35
  36static long INIT nofill(void *buffer, unsigned long len)
  37{
  38        return -1;
  39}
  40
  41/* Included from initramfs et al code */
  42STATIC int INIT __gunzip(unsigned char *buf, long len,
  43                       long (*fill)(void*, unsigned long),
  44                       long (*flush)(void*, unsigned long),
  45                       unsigned char *out_buf, long out_len,
  46                       long *pos,
  47                       void(*error)(char *x)) {
  48        u8 *zbuf;
  49        struct z_stream_s *strm;
  50        int rc;
  51
  52        rc = -1;
  53        if (flush) {
  54                out_len = 0x8000; /* 32 K */
  55                out_buf = malloc(out_len);
  56        } else {
  57                if (!out_len)
  58                        out_len = ((size_t)~0) - (size_t)out_buf; /* no limit */
  59        }
  60        if (!out_buf) {
  61                error("Out of memory while allocating output buffer");
  62                goto gunzip_nomem1;
  63        }
  64
  65        if (buf)
  66                zbuf = buf;
  67        else {
  68                zbuf = malloc(GZIP_IOBUF_SIZE);
  69                len = 0;
  70        }
  71        if (!zbuf) {
  72                error("Out of memory while allocating input buffer");
  73                goto gunzip_nomem2;
  74        }
  75
  76        strm = malloc(sizeof(*strm));
  77        if (strm == NULL) {
  78                error("Out of memory while allocating z_stream");
  79                goto gunzip_nomem3;
  80        }
  81
  82        strm->workspace = malloc(flush ? zlib_inflate_workspacesize() :
  83#ifdef CONFIG_ZLIB_DFLTCC
  84        /* Always allocate the full workspace for DFLTCC */
  85                                 zlib_inflate_workspacesize());
  86#else
  87                                 sizeof(struct inflate_state));
  88#endif
  89        if (strm->workspace == NULL) {
  90                error("Out of memory while allocating workspace");
  91                goto gunzip_nomem4;
  92        }
  93
  94        if (!fill)
  95                fill = nofill;
  96
  97        if (len == 0)
  98                len = fill(zbuf, GZIP_IOBUF_SIZE);
  99
 100        /* verify the gzip header */
 101        if (len < 10 ||
 102           zbuf[0] != 0x1f || zbuf[1] != 0x8b || zbuf[2] != 0x08) {
 103                if (pos)
 104                        *pos = 0;
 105                error("Not a gzip file");
 106                goto gunzip_5;
 107        }
 108
 109        /* skip over gzip header (1f,8b,08... 10 bytes total +
 110         * possible asciz filename)
 111         */
 112        strm->next_in = zbuf + 10;
 113        strm->avail_in = len - 10;
 114        /* skip over asciz filename */
 115        if (zbuf[3] & 0x8) {
 116                do {
 117                        /*
 118                         * If the filename doesn't fit into the buffer,
 119                         * the file is very probably corrupt. Don't try
 120                         * to read more data.
 121                         */
 122                        if (strm->avail_in == 0) {
 123                                error("header error");
 124                                goto gunzip_5;
 125                        }
 126                        --strm->avail_in;
 127                } while (*strm->next_in++);
 128        }
 129
 130        strm->next_out = out_buf;
 131        strm->avail_out = out_len;
 132
 133        rc = zlib_inflateInit2(strm, -MAX_WBITS);
 134
 135#ifdef CONFIG_ZLIB_DFLTCC
 136        /* Always keep the window for DFLTCC */
 137#else
 138        if (!flush) {
 139                WS(strm)->inflate_state.wsize = 0;
 140                WS(strm)->inflate_state.window = NULL;
 141        }
 142#endif
 143
 144        while (rc == Z_OK) {
 145                if (strm->avail_in == 0) {
 146                        /* TODO: handle case where both pos and fill are set */
 147                        len = fill(zbuf, GZIP_IOBUF_SIZE);
 148                        if (len < 0) {
 149                                rc = -1;
 150                                error("read error");
 151                                break;
 152                        }
 153                        strm->next_in = zbuf;
 154                        strm->avail_in = len;
 155                }
 156                rc = zlib_inflate(strm, 0);
 157
 158                /* Write any data generated */
 159                if (flush && strm->next_out > out_buf) {
 160                        long l = strm->next_out - out_buf;
 161                        if (l != flush(out_buf, l)) {
 162                                rc = -1;
 163                                error("write error");
 164                                break;
 165                        }
 166                        strm->next_out = out_buf;
 167                        strm->avail_out = out_len;
 168                }
 169
 170                /* after Z_FINISH, only Z_STREAM_END is "we unpacked it all" */
 171                if (rc == Z_STREAM_END) {
 172                        rc = 0;
 173                        break;
 174                } else if (rc != Z_OK) {
 175                        error("uncompression error");
 176                        rc = -1;
 177                }
 178        }
 179
 180        zlib_inflateEnd(strm);
 181        if (pos)
 182                /* add + 8 to skip over trailer */
 183                *pos = strm->next_in - zbuf+8;
 184
 185gunzip_5:
 186        free(strm->workspace);
 187gunzip_nomem4:
 188        free(strm);
 189gunzip_nomem3:
 190        if (!buf)
 191                free(zbuf);
 192gunzip_nomem2:
 193        if (flush)
 194                free(out_buf);
 195gunzip_nomem1:
 196        return rc; /* returns Z_OK (0) if successful */
 197}
 198
 199#ifndef PREBOOT
 200STATIC int INIT gunzip(unsigned char *buf, long len,
 201                       long (*fill)(void*, unsigned long),
 202                       long (*flush)(void*, unsigned long),
 203                       unsigned char *out_buf,
 204                       long *pos,
 205                       void (*error)(char *x))
 206{
 207        return __gunzip(buf, len, fill, flush, out_buf, 0, pos, error);
 208}
 209#else
 210STATIC int INIT __decompress(unsigned char *buf, long len,
 211                           long (*fill)(void*, unsigned long),
 212                           long (*flush)(void*, unsigned long),
 213                           unsigned char *out_buf, long out_len,
 214                           long *pos,
 215                           void (*error)(char *x))
 216{
 217        return __gunzip(buf, len, fill, flush, out_buf, out_len, pos, error);
 218}
 219#endif
 220