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