linux/lib/decompress_unlzo.c
<<
>>
Prefs
   1/*
   2 * LZO decompressor for the Linux kernel. Code borrowed from the lzo
   3 * implementation by Markus Franz Xaver Johannes Oberhumer.
   4 *
   5 * Linux kernel adaptation:
   6 * Copyright (C) 2009
   7 * Albin Tonnerre, Free Electrons <albin.tonnerre@free-electrons.com>
   8 *
   9 * Original code:
  10 * Copyright (C) 1996-2005 Markus Franz Xaver Johannes Oberhumer
  11 * All Rights Reserved.
  12 *
  13 * lzop and the LZO library are free software; you can redistribute them
  14 * and/or modify them under the terms of the GNU General Public License as
  15 * published by the Free Software Foundation; either version 2 of
  16 * the License, or (at your option) any later version.
  17 *
  18 * This program is distributed in the hope that it will be useful,
  19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  21 * GNU General Public License for more details.
  22 *
  23 * You should have received a copy of the GNU General Public License
  24 * along with this program; see the file COPYING.
  25 * If not, write to the Free Software Foundation, Inc.,
  26 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  27 *
  28 * Markus F.X.J. Oberhumer
  29 * <markus@oberhumer.com>
  30 * http://www.oberhumer.com/opensource/lzop/
  31 */
  32
  33#ifdef STATIC
  34#define PREBOOT
  35#include "lzo/lzo1x_decompress_safe.c"
  36#else
  37#include <linux/decompress/unlzo.h>
  38#endif
  39
  40#include <linux/types.h>
  41#include <linux/lzo.h>
  42#include <linux/decompress/mm.h>
  43
  44#include <linux/compiler.h>
  45#include <asm/unaligned.h>
  46
  47static const unsigned char lzop_magic[] = {
  48        0x89, 0x4c, 0x5a, 0x4f, 0x00, 0x0d, 0x0a, 0x1a, 0x0a };
  49
  50#define LZO_BLOCK_SIZE        (256*1024l)
  51#define HEADER_HAS_FILTER      0x00000800L
  52#define HEADER_SIZE_MIN       (9 + 7     + 4 + 8     + 1       + 4)
  53#define HEADER_SIZE_MAX       (9 + 7 + 1 + 8 + 8 + 4 + 1 + 255 + 4)
  54
  55STATIC inline long INIT parse_header(u8 *input, long *skip, long in_len)
  56{
  57        int l;
  58        u8 *parse = input;
  59        u8 *end = input + in_len;
  60        u8 level = 0;
  61        u16 version;
  62
  63        /*
  64         * Check that there's enough input to possibly have a valid header.
  65         * Then it is possible to parse several fields until the minimum
  66         * size may have been used.
  67         */
  68        if (in_len < HEADER_SIZE_MIN)
  69                return 0;
  70
  71        /* read magic: 9 first bits */
  72        for (l = 0; l < 9; l++) {
  73                if (*parse++ != lzop_magic[l])
  74                        return 0;
  75        }
  76        /* get version (2bytes), skip library version (2),
  77         * 'need to be extracted' version (2) and
  78         * method (1) */
  79        version = get_unaligned_be16(parse);
  80        parse += 7;
  81        if (version >= 0x0940)
  82                level = *parse++;
  83        if (get_unaligned_be32(parse) & HEADER_HAS_FILTER)
  84                parse += 8; /* flags + filter info */
  85        else
  86                parse += 4; /* flags */
  87
  88        /*
  89         * At least mode, mtime_low, filename length, and checksum must
  90         * be left to be parsed. If also mtime_high is present, it's OK
  91         * because the next input buffer check is after reading the
  92         * filename length.
  93         */
  94        if (end - parse < 8 + 1 + 4)
  95                return 0;
  96
  97        /* skip mode and mtime_low */
  98        parse += 8;
  99        if (version >= 0x0940)
 100                parse += 4;     /* skip mtime_high */
 101
 102        l = *parse++;
 103        /* don't care about the file name, and skip checksum */
 104        if (end - parse < l + 4)
 105                return 0;
 106        parse += l + 4;
 107
 108        *skip = parse - input;
 109        return 1;
 110}
 111
 112STATIC int INIT unlzo(u8 *input, long in_len,
 113                                long (*fill)(void *, unsigned long),
 114                                long (*flush)(void *, unsigned long),
 115                                u8 *output, long *posp,
 116                                void (*error) (char *x))
 117{
 118        u8 r = 0;
 119        long skip = 0;
 120        u32 src_len, dst_len;
 121        size_t tmp;
 122        u8 *in_buf, *in_buf_save, *out_buf;
 123        int ret = -1;
 124
 125        if (output) {
 126                out_buf = output;
 127        } else if (!flush) {
 128                error("NULL output pointer and no flush function provided");
 129                goto exit;
 130        } else {
 131                out_buf = malloc(LZO_BLOCK_SIZE);
 132                if (!out_buf) {
 133                        error("Could not allocate output buffer");
 134                        goto exit;
 135                }
 136        }
 137
 138        if (input && fill) {
 139                error("Both input pointer and fill function provided, don't know what to do");
 140                goto exit_1;
 141        } else if (input) {
 142                in_buf = input;
 143        } else if (!fill) {
 144                error("NULL input pointer and missing fill function");
 145                goto exit_1;
 146        } else {
 147                in_buf = malloc(lzo1x_worst_compress(LZO_BLOCK_SIZE));
 148                if (!in_buf) {
 149                        error("Could not allocate input buffer");
 150                        goto exit_1;
 151                }
 152        }
 153        in_buf_save = in_buf;
 154
 155        if (posp)
 156                *posp = 0;
 157
 158        if (fill) {
 159                /*
 160                 * Start from in_buf + HEADER_SIZE_MAX to make it possible
 161                 * to use memcpy() to copy the unused data to the beginning
 162                 * of the buffer. This way memmove() isn't needed which
 163                 * is missing from pre-boot environments of most archs.
 164                 */
 165                in_buf += HEADER_SIZE_MAX;
 166                in_len = fill(in_buf, HEADER_SIZE_MAX);
 167        }
 168
 169        if (!parse_header(in_buf, &skip, in_len)) {
 170                error("invalid header");
 171                goto exit_2;
 172        }
 173        in_buf += skip;
 174        in_len -= skip;
 175
 176        if (fill) {
 177                /* Move the unused data to the beginning of the buffer. */
 178                memcpy(in_buf_save, in_buf, in_len);
 179                in_buf = in_buf_save;
 180        }
 181
 182        if (posp)
 183                *posp = skip;
 184
 185        for (;;) {
 186                /* read uncompressed block size */
 187                if (fill && in_len < 4) {
 188                        skip = fill(in_buf + in_len, 4 - in_len);
 189                        if (skip > 0)
 190                                in_len += skip;
 191                }
 192                if (in_len < 4) {
 193                        error("file corrupted");
 194                        goto exit_2;
 195                }
 196                dst_len = get_unaligned_be32(in_buf);
 197                in_buf += 4;
 198                in_len -= 4;
 199
 200                /* exit if last block */
 201                if (dst_len == 0) {
 202                        if (posp)
 203                                *posp += 4;
 204                        break;
 205                }
 206
 207                if (dst_len > LZO_BLOCK_SIZE) {
 208                        error("dest len longer than block size");
 209                        goto exit_2;
 210                }
 211
 212                /* read compressed block size, and skip block checksum info */
 213                if (fill && in_len < 8) {
 214                        skip = fill(in_buf + in_len, 8 - in_len);
 215                        if (skip > 0)
 216                                in_len += skip;
 217                }
 218                if (in_len < 8) {
 219                        error("file corrupted");
 220                        goto exit_2;
 221                }
 222                src_len = get_unaligned_be32(in_buf);
 223                in_buf += 8;
 224                in_len -= 8;
 225
 226                if (src_len <= 0 || src_len > dst_len) {
 227                        error("file corrupted");
 228                        goto exit_2;
 229                }
 230
 231                /* decompress */
 232                if (fill && in_len < src_len) {
 233                        skip = fill(in_buf + in_len, src_len - in_len);
 234                        if (skip > 0)
 235                                in_len += skip;
 236                }
 237                if (in_len < src_len) {
 238                        error("file corrupted");
 239                        goto exit_2;
 240                }
 241                tmp = dst_len;
 242
 243                /* When the input data is not compressed at all,
 244                 * lzo1x_decompress_safe will fail, so call memcpy()
 245                 * instead */
 246                if (unlikely(dst_len == src_len))
 247                        memcpy(out_buf, in_buf, src_len);
 248                else {
 249                        r = lzo1x_decompress_safe((u8 *) in_buf, src_len,
 250                                                out_buf, &tmp);
 251
 252                        if (r != LZO_E_OK || dst_len != tmp) {
 253                                error("Compressed data violation");
 254                                goto exit_2;
 255                        }
 256                }
 257
 258                if (flush && flush(out_buf, dst_len) != dst_len)
 259                        goto exit_2;
 260                if (output)
 261                        out_buf += dst_len;
 262                if (posp)
 263                        *posp += src_len + 12;
 264
 265                in_buf += src_len;
 266                in_len -= src_len;
 267                if (fill) {
 268                        /*
 269                         * If there happens to still be unused data left in
 270                         * in_buf, move it to the beginning of the buffer.
 271                         * Use a loop to avoid memmove() dependency.
 272                         */
 273                        if (in_len > 0)
 274                                for (skip = 0; skip < in_len; ++skip)
 275                                        in_buf_save[skip] = in_buf[skip];
 276                        in_buf = in_buf_save;
 277                }
 278        }
 279
 280        ret = 0;
 281exit_2:
 282        if (!input)
 283                free(in_buf_save);
 284exit_1:
 285        if (!output)
 286                free(out_buf);
 287exit:
 288        return ret;
 289}
 290
 291#ifdef PREBOOT
 292STATIC int INIT __decompress(unsigned char *buf, long len,
 293                           long (*fill)(void*, unsigned long),
 294                           long (*flush)(void*, unsigned long),
 295                           unsigned char *out_buf, long olen,
 296                           long *pos,
 297                           void (*error)(char *x))
 298{
 299        return unlzo(buf, len, fill, flush, out_buf, pos, error);
 300}
 301#endif
 302