Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * LZO decompressor for the Linux kernel. Code borrowed from the lzo
0004  * implementation by Markus Franz Xaver Johannes Oberhumer.
0005  *
0006  * Linux kernel adaptation:
0007  * Copyright (C) 2009
0008  * Albin Tonnerre, Free Electrons <albin.tonnerre@free-electrons.com>
0009  *
0010  * Original code:
0011  * Copyright (C) 1996-2005 Markus Franz Xaver Johannes Oberhumer
0012  * All Rights Reserved.
0013  *
0014  * Markus F.X.J. Oberhumer
0015  * <markus@oberhumer.com>
0016  * http://www.oberhumer.com/opensource/lzop/
0017  */
0018 
0019 #ifdef STATIC
0020 #define PREBOOT
0021 #include "lzo/lzo1x_decompress_safe.c"
0022 #else
0023 #include <linux/decompress/unlzo.h>
0024 #endif
0025 
0026 #include <linux/types.h>
0027 #include <linux/lzo.h>
0028 #include <linux/decompress/mm.h>
0029 
0030 #include <linux/compiler.h>
0031 #include <asm/unaligned.h>
0032 
0033 static const unsigned char lzop_magic[] = {
0034     0x89, 0x4c, 0x5a, 0x4f, 0x00, 0x0d, 0x0a, 0x1a, 0x0a };
0035 
0036 #define LZO_BLOCK_SIZE        (256*1024l)
0037 #define HEADER_HAS_FILTER      0x00000800L
0038 #define HEADER_SIZE_MIN       (9 + 7     + 4 + 8     + 1       + 4)
0039 #define HEADER_SIZE_MAX       (9 + 7 + 1 + 8 + 8 + 4 + 1 + 255 + 4)
0040 
0041 STATIC inline long INIT parse_header(u8 *input, long *skip, long in_len)
0042 {
0043     int l;
0044     u8 *parse = input;
0045     u8 *end = input + in_len;
0046     u16 version;
0047 
0048     /*
0049      * Check that there's enough input to possibly have a valid header.
0050      * Then it is possible to parse several fields until the minimum
0051      * size may have been used.
0052      */
0053     if (in_len < HEADER_SIZE_MIN)
0054         return 0;
0055 
0056     /* read magic: 9 first bits */
0057     for (l = 0; l < 9; l++) {
0058         if (*parse++ != lzop_magic[l])
0059             return 0;
0060     }
0061     /* get version (2bytes), skip library version (2),
0062      * 'need to be extracted' version (2) and
0063      * method (1) */
0064     version = get_unaligned_be16(parse);
0065     parse += 7;
0066     if (version >= 0x0940)
0067         parse++;
0068     if (get_unaligned_be32(parse) & HEADER_HAS_FILTER)
0069         parse += 8; /* flags + filter info */
0070     else
0071         parse += 4; /* flags */
0072 
0073     /*
0074      * At least mode, mtime_low, filename length, and checksum must
0075      * be left to be parsed. If also mtime_high is present, it's OK
0076      * because the next input buffer check is after reading the
0077      * filename length.
0078      */
0079     if (end - parse < 8 + 1 + 4)
0080         return 0;
0081 
0082     /* skip mode and mtime_low */
0083     parse += 8;
0084     if (version >= 0x0940)
0085         parse += 4; /* skip mtime_high */
0086 
0087     l = *parse++;
0088     /* don't care about the file name, and skip checksum */
0089     if (end - parse < l + 4)
0090         return 0;
0091     parse += l + 4;
0092 
0093     *skip = parse - input;
0094     return 1;
0095 }
0096 
0097 STATIC int INIT unlzo(u8 *input, long in_len,
0098                 long (*fill)(void *, unsigned long),
0099                 long (*flush)(void *, unsigned long),
0100                 u8 *output, long *posp,
0101                 void (*error) (char *x))
0102 {
0103     u8 r = 0;
0104     long skip = 0;
0105     u32 src_len, dst_len;
0106     size_t tmp;
0107     u8 *in_buf, *in_buf_save, *out_buf;
0108     int ret = -1;
0109 
0110     if (output) {
0111         out_buf = output;
0112     } else if (!flush) {
0113         error("NULL output pointer and no flush function provided");
0114         goto exit;
0115     } else {
0116         out_buf = malloc(LZO_BLOCK_SIZE);
0117         if (!out_buf) {
0118             error("Could not allocate output buffer");
0119             goto exit;
0120         }
0121     }
0122 
0123     if (input && fill) {
0124         error("Both input pointer and fill function provided, don't know what to do");
0125         goto exit_1;
0126     } else if (input) {
0127         in_buf = input;
0128     } else if (!fill) {
0129         error("NULL input pointer and missing fill function");
0130         goto exit_1;
0131     } else {
0132         in_buf = malloc(lzo1x_worst_compress(LZO_BLOCK_SIZE));
0133         if (!in_buf) {
0134             error("Could not allocate input buffer");
0135             goto exit_1;
0136         }
0137     }
0138     in_buf_save = in_buf;
0139 
0140     if (posp)
0141         *posp = 0;
0142 
0143     if (fill) {
0144         /*
0145          * Start from in_buf + HEADER_SIZE_MAX to make it possible
0146          * to use memcpy() to copy the unused data to the beginning
0147          * of the buffer. This way memmove() isn't needed which
0148          * is missing from pre-boot environments of most archs.
0149          */
0150         in_buf += HEADER_SIZE_MAX;
0151         in_len = fill(in_buf, HEADER_SIZE_MAX);
0152     }
0153 
0154     if (!parse_header(in_buf, &skip, in_len)) {
0155         error("invalid header");
0156         goto exit_2;
0157     }
0158     in_buf += skip;
0159     in_len -= skip;
0160 
0161     if (fill) {
0162         /* Move the unused data to the beginning of the buffer. */
0163         memcpy(in_buf_save, in_buf, in_len);
0164         in_buf = in_buf_save;
0165     }
0166 
0167     if (posp)
0168         *posp = skip;
0169 
0170     for (;;) {
0171         /* read uncompressed block size */
0172         if (fill && in_len < 4) {
0173             skip = fill(in_buf + in_len, 4 - in_len);
0174             if (skip > 0)
0175                 in_len += skip;
0176         }
0177         if (in_len < 4) {
0178             error("file corrupted");
0179             goto exit_2;
0180         }
0181         dst_len = get_unaligned_be32(in_buf);
0182         in_buf += 4;
0183         in_len -= 4;
0184 
0185         /* exit if last block */
0186         if (dst_len == 0) {
0187             if (posp)
0188                 *posp += 4;
0189             break;
0190         }
0191 
0192         if (dst_len > LZO_BLOCK_SIZE) {
0193             error("dest len longer than block size");
0194             goto exit_2;
0195         }
0196 
0197         /* read compressed block size, and skip block checksum info */
0198         if (fill && in_len < 8) {
0199             skip = fill(in_buf + in_len, 8 - in_len);
0200             if (skip > 0)
0201                 in_len += skip;
0202         }
0203         if (in_len < 8) {
0204             error("file corrupted");
0205             goto exit_2;
0206         }
0207         src_len = get_unaligned_be32(in_buf);
0208         in_buf += 8;
0209         in_len -= 8;
0210 
0211         if (src_len <= 0 || src_len > dst_len) {
0212             error("file corrupted");
0213             goto exit_2;
0214         }
0215 
0216         /* decompress */
0217         if (fill && in_len < src_len) {
0218             skip = fill(in_buf + in_len, src_len - in_len);
0219             if (skip > 0)
0220                 in_len += skip;
0221         }
0222         if (in_len < src_len) {
0223             error("file corrupted");
0224             goto exit_2;
0225         }
0226         tmp = dst_len;
0227 
0228         /* When the input data is not compressed at all,
0229          * lzo1x_decompress_safe will fail, so call memcpy()
0230          * instead */
0231         if (unlikely(dst_len == src_len))
0232             memcpy(out_buf, in_buf, src_len);
0233         else {
0234             r = lzo1x_decompress_safe((u8 *) in_buf, src_len,
0235                         out_buf, &tmp);
0236 
0237             if (r != LZO_E_OK || dst_len != tmp) {
0238                 error("Compressed data violation");
0239                 goto exit_2;
0240             }
0241         }
0242 
0243         if (flush && flush(out_buf, dst_len) != dst_len)
0244             goto exit_2;
0245         if (output)
0246             out_buf += dst_len;
0247         if (posp)
0248             *posp += src_len + 12;
0249 
0250         in_buf += src_len;
0251         in_len -= src_len;
0252         if (fill) {
0253             /*
0254              * If there happens to still be unused data left in
0255              * in_buf, move it to the beginning of the buffer.
0256              * Use a loop to avoid memmove() dependency.
0257              */
0258             if (in_len > 0)
0259                 for (skip = 0; skip < in_len; ++skip)
0260                     in_buf_save[skip] = in_buf[skip];
0261             in_buf = in_buf_save;
0262         }
0263     }
0264 
0265     ret = 0;
0266 exit_2:
0267     if (!input)
0268         free(in_buf_save);
0269 exit_1:
0270     if (!output)
0271         free(out_buf);
0272 exit:
0273     return ret;
0274 }
0275 
0276 #ifdef PREBOOT
0277 STATIC int INIT __decompress(unsigned char *buf, long len,
0278                long (*fill)(void*, unsigned long),
0279                long (*flush)(void*, unsigned long),
0280                unsigned char *out_buf, long olen,
0281                long *pos,
0282                void (*error)(char *x))
0283 {
0284     return unlzo(buf, len, fill, flush, out_buf, pos, error);
0285 }
0286 #endif