uboot/lib/lzo/lzo1x_decompress.c
<<
>>
Prefs
   1/*
   2 *  LZO1X Decompressor from MiniLZO
   3 *
   4 *  Copyright (C) 1996-2005 Markus F.X.J. Oberhumer <markus@oberhumer.com>
   5 *
   6 *  The full LZO package can be found at:
   7 *  http://www.oberhumer.com/opensource/lzo/
   8 *
   9 *  Changed for kernel use by:
  10 *  Nitin Gupta <nitingupta910@gmail.com>
  11 *  Richard Purdie <rpurdie@openedhand.com>
  12 */
  13
  14#include <common.h>
  15#include <linux/lzo.h>
  16#include <asm/byteorder.h>
  17#include <asm/unaligned.h>
  18#include "lzodefs.h"
  19
  20#define HAVE_IP(x, ip_end, ip) ((size_t)(ip_end - ip) < (x))
  21#define HAVE_OP(x, op_end, op) ((size_t)(op_end - op) < (x))
  22#define HAVE_LB(m_pos, out, op) (m_pos < out || m_pos >= op)
  23
  24#define COPY4(dst, src) \
  25                put_unaligned(get_unaligned((const u32 *)(src)), (u32 *)(dst))
  26
  27static const unsigned char lzop_magic[] = {
  28        0x89, 0x4c, 0x5a, 0x4f, 0x00, 0x0d, 0x0a, 0x1a, 0x0a
  29};
  30
  31#define HEADER_HAS_FILTER       0x00000800L
  32
  33static inline const unsigned char *parse_header(const unsigned char *src)
  34{
  35        u16 version;
  36        int i;
  37
  38        /* read magic: 9 first bytes */
  39        for (i = 0; i < ARRAY_SIZE(lzop_magic); i++) {
  40                if (*src++ != lzop_magic[i])
  41                        return NULL;
  42        }
  43        /* get version (2bytes), skip library version (2),
  44         * 'need to be extracted' version (2) and
  45         * method (1) */
  46        version = get_unaligned_be16(src);
  47        src += 7;
  48        if (version >= 0x0940)
  49                src++;
  50        if (get_unaligned_be32(src) & HEADER_HAS_FILTER)
  51                src += 4; /* filter info */
  52
  53        /* skip flags, mode and mtime_low */
  54        src += 12;
  55        if (version >= 0x0940)
  56                src += 4;       /* skip mtime_high */
  57
  58        i = *src++;
  59        /* don't care about the file name, and skip checksum */
  60        src += i + 4;
  61
  62        return src;
  63}
  64
  65int lzop_decompress(const unsigned char *src, size_t src_len,
  66                    unsigned char *dst, size_t *dst_len)
  67{
  68        unsigned char *start = dst;
  69        const unsigned char *send = src + src_len;
  70        u32 slen, dlen;
  71        size_t tmp, remaining;
  72        int r;
  73
  74        src = parse_header(src);
  75        if (!src)
  76                return LZO_E_ERROR;
  77
  78        remaining = *dst_len;
  79        while (src < send) {
  80                /* read uncompressed block size */
  81                dlen = get_unaligned_be32(src);
  82                src += 4;
  83
  84                /* exit if last block */
  85                if (dlen == 0) {
  86                        *dst_len = dst - start;
  87                        return LZO_E_OK;
  88                }
  89
  90                /* read compressed block size, and skip block checksum info */
  91                slen = get_unaligned_be32(src);
  92                src += 8;
  93
  94                if (slen <= 0 || slen > dlen)
  95                        return LZO_E_ERROR;
  96
  97                /* abort if buffer ran out of room */
  98                if (dlen > remaining)
  99                        return LZO_E_OUTPUT_OVERRUN;
 100
 101                /* When the input data is not compressed at all,
 102                 * lzo1x_decompress_safe will fail, so call memcpy()
 103                 * instead */
 104                if (dlen == slen) {
 105                        memcpy(dst, src, slen);
 106                } else {
 107                        /* decompress */
 108                        tmp = dlen;
 109                        r = lzo1x_decompress_safe((u8 *)src, slen, dst, &tmp);
 110
 111                        if (r != LZO_E_OK) {
 112                                *dst_len = dst - start;
 113                                return r;
 114                        }
 115
 116                        if (dlen != tmp)
 117                                return LZO_E_ERROR;
 118                }
 119
 120                src += slen;
 121                dst += dlen;
 122                remaining -= dlen;
 123        }
 124
 125        return LZO_E_INPUT_OVERRUN;
 126}
 127
 128int lzo1x_decompress_safe(const unsigned char *in, size_t in_len,
 129                        unsigned char *out, size_t *out_len)
 130{
 131        const unsigned char * const ip_end = in + in_len;
 132        unsigned char * const op_end = out + *out_len;
 133        const unsigned char *ip = in, *m_pos;
 134        unsigned char *op = out;
 135        size_t t;
 136
 137        *out_len = 0;
 138
 139        if (*ip > 17) {
 140                t = *ip++ - 17;
 141                if (t < 4)
 142                        goto match_next;
 143                if (HAVE_OP(t, op_end, op))
 144                        goto output_overrun;
 145                if (HAVE_IP(t + 1, ip_end, ip))
 146                        goto input_overrun;
 147                do {
 148                        *op++ = *ip++;
 149                } while (--t > 0);
 150                goto first_literal_run;
 151        }
 152
 153        while ((ip < ip_end)) {
 154                t = *ip++;
 155                if (t >= 16)
 156                        goto match;
 157                if (t == 0) {
 158                        if (HAVE_IP(1, ip_end, ip))
 159                                goto input_overrun;
 160                        while (*ip == 0) {
 161                                t += 255;
 162                                ip++;
 163                                if (HAVE_IP(1, ip_end, ip))
 164                                        goto input_overrun;
 165                        }
 166                        t += 15 + *ip++;
 167                }
 168                if (HAVE_OP(t + 3, op_end, op))
 169                        goto output_overrun;
 170                if (HAVE_IP(t + 4, ip_end, ip))
 171                        goto input_overrun;
 172
 173                COPY4(op, ip);
 174                op += 4;
 175                ip += 4;
 176                if (--t > 0) {
 177                        if (t >= 4) {
 178                                do {
 179                                        COPY4(op, ip);
 180                                        op += 4;
 181                                        ip += 4;
 182                                        t -= 4;
 183                                } while (t >= 4);
 184                                if (t > 0) {
 185                                        do {
 186                                                *op++ = *ip++;
 187                                        } while (--t > 0);
 188                                }
 189                        } else {
 190                                do {
 191                                        *op++ = *ip++;
 192                                } while (--t > 0);
 193                        }
 194                }
 195
 196first_literal_run:
 197                t = *ip++;
 198                if (t >= 16)
 199                        goto match;
 200                m_pos = op - (1 + M2_MAX_OFFSET);
 201                m_pos -= t >> 2;
 202                m_pos -= *ip++ << 2;
 203
 204                if (HAVE_LB(m_pos, out, op))
 205                        goto lookbehind_overrun;
 206
 207                if (HAVE_OP(3, op_end, op))
 208                        goto output_overrun;
 209                *op++ = *m_pos++;
 210                *op++ = *m_pos++;
 211                *op++ = *m_pos;
 212
 213                goto match_done;
 214
 215                do {
 216match:
 217                        if (t >= 64) {
 218                                m_pos = op - 1;
 219                                m_pos -= (t >> 2) & 7;
 220                                m_pos -= *ip++ << 3;
 221                                t = (t >> 5) - 1;
 222                                if (HAVE_LB(m_pos, out, op))
 223                                        goto lookbehind_overrun;
 224                                if (HAVE_OP(t + 3 - 1, op_end, op))
 225                                        goto output_overrun;
 226                                goto copy_match;
 227                        } else if (t >= 32) {
 228                                t &= 31;
 229                                if (t == 0) {
 230                                        if (HAVE_IP(1, ip_end, ip))
 231                                                goto input_overrun;
 232                                        while (*ip == 0) {
 233                                                t += 255;
 234                                                ip++;
 235                                                if (HAVE_IP(1, ip_end, ip))
 236                                                        goto input_overrun;
 237                                        }
 238                                        t += 31 + *ip++;
 239                                }
 240                                m_pos = op - 1;
 241                                m_pos -= get_unaligned_le16(ip) >> 2;
 242                                ip += 2;
 243                        } else if (t >= 16) {
 244                                m_pos = op;
 245                                m_pos -= (t & 8) << 11;
 246
 247                                t &= 7;
 248                                if (t == 0) {
 249                                        if (HAVE_IP(1, ip_end, ip))
 250                                                goto input_overrun;
 251                                        while (*ip == 0) {
 252                                                t += 255;
 253                                                ip++;
 254                                                if (HAVE_IP(1, ip_end, ip))
 255                                                        goto input_overrun;
 256                                        }
 257                                        t += 7 + *ip++;
 258                                }
 259                                m_pos -= get_unaligned_le16(ip) >> 2;
 260                                ip += 2;
 261                                if (m_pos == op)
 262                                        goto eof_found;
 263                                m_pos -= 0x4000;
 264                        } else {
 265                                m_pos = op - 1;
 266                                m_pos -= t >> 2;
 267                                m_pos -= *ip++ << 2;
 268
 269                                if (HAVE_LB(m_pos, out, op))
 270                                        goto lookbehind_overrun;
 271                                if (HAVE_OP(2, op_end, op))
 272                                        goto output_overrun;
 273
 274                                *op++ = *m_pos++;
 275                                *op++ = *m_pos;
 276                                goto match_done;
 277                        }
 278
 279                        if (HAVE_LB(m_pos, out, op))
 280                                goto lookbehind_overrun;
 281                        if (HAVE_OP(t + 3 - 1, op_end, op))
 282                                goto output_overrun;
 283
 284                        if (t >= 2 * 4 - (3 - 1) && (op - m_pos) >= 4) {
 285                                COPY4(op, m_pos);
 286                                op += 4;
 287                                m_pos += 4;
 288                                t -= 4 - (3 - 1);
 289                                do {
 290                                        COPY4(op, m_pos);
 291                                        op += 4;
 292                                        m_pos += 4;
 293                                        t -= 4;
 294                                } while (t >= 4);
 295                                if (t > 0)
 296                                        do {
 297                                                *op++ = *m_pos++;
 298                                        } while (--t > 0);
 299                        } else {
 300copy_match:
 301                                *op++ = *m_pos++;
 302                                *op++ = *m_pos++;
 303                                do {
 304                                        *op++ = *m_pos++;
 305                                } while (--t > 0);
 306                        }
 307match_done:
 308                        t = ip[-2] & 3;
 309                        if (t == 0)
 310                                break;
 311match_next:
 312                        if (HAVE_OP(t, op_end, op))
 313                                goto output_overrun;
 314                        if (HAVE_IP(t + 1, ip_end, ip))
 315                                goto input_overrun;
 316
 317                        *op++ = *ip++;
 318                        if (t > 1) {
 319                                *op++ = *ip++;
 320                                if (t > 2)
 321                                        *op++ = *ip++;
 322                        }
 323
 324                        t = *ip++;
 325                } while (ip < ip_end);
 326        }
 327
 328        *out_len = op - out;
 329        return LZO_E_EOF_NOT_FOUND;
 330
 331eof_found:
 332        *out_len = op - out;
 333        return (ip == ip_end ? LZO_E_OK :
 334                (ip < ip_end ? LZO_E_INPUT_NOT_CONSUMED : LZO_E_INPUT_OVERRUN));
 335input_overrun:
 336        *out_len = op - out;
 337        return LZO_E_INPUT_OVERRUN;
 338
 339output_overrun:
 340        *out_len = op - out;
 341        return LZO_E_OUTPUT_OVERRUN;
 342
 343lookbehind_overrun:
 344        *out_len = op - out;
 345        return LZO_E_LOOKBEHIND_OVERRUN;
 346}
 347