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 STATIC 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 minimum 51 * 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) and 63 * 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 else 71 parse += 4; /* flags */ 72 73 /* 74 * At least mode, mtime_low, filename length, and checksum must 75 * be left to be parsed. If also mtime_high is present, it's OK 76 * because the next input buffer check is after reading the 77 * 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 97 STATIC 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 possible 146 * to use memcpy() to copy the unused data to the beginning 147 * of the buffer. This way memmove() isn't needed which 148 * 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 in 255 * 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; 266 exit_2: 267 if (!input) 268 free(in_buf_save); 269 exit_1: 270 if (!output) 271 free(out_buf); 272 exit: 273 return ret; 274 } 275 276 #ifdef PREBOOT 277 STATIC 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 #endif 287
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.