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