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