17dd65febSAlbin Tonnerre /* 27dd65febSAlbin Tonnerre * LZO decompressor for the Linux kernel. Code borrowed from the lzo 37dd65febSAlbin Tonnerre * implementation by Markus Franz Xaver Johannes Oberhumer. 47dd65febSAlbin Tonnerre * 57dd65febSAlbin Tonnerre * Linux kernel adaptation: 67dd65febSAlbin Tonnerre * Copyright (C) 2009 77dd65febSAlbin Tonnerre * Albin Tonnerre, Free Electrons <albin.tonnerre@free-electrons.com> 87dd65febSAlbin Tonnerre * 97dd65febSAlbin Tonnerre * Original code: 107dd65febSAlbin Tonnerre * Copyright (C) 1996-2005 Markus Franz Xaver Johannes Oberhumer 117dd65febSAlbin Tonnerre * All Rights Reserved. 127dd65febSAlbin Tonnerre * 137dd65febSAlbin Tonnerre * lzop and the LZO library are free software; you can redistribute them 147dd65febSAlbin Tonnerre * and/or modify them under the terms of the GNU General Public License as 157dd65febSAlbin Tonnerre * published by the Free Software Foundation; either version 2 of 167dd65febSAlbin Tonnerre * the License, or (at your option) any later version. 177dd65febSAlbin Tonnerre * 187dd65febSAlbin Tonnerre * This program is distributed in the hope that it will be useful, 197dd65febSAlbin Tonnerre * but WITHOUT ANY WARRANTY; without even the implied warranty of 207dd65febSAlbin Tonnerre * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 217dd65febSAlbin Tonnerre * GNU General Public License for more details. 227dd65febSAlbin Tonnerre * 237dd65febSAlbin Tonnerre * You should have received a copy of the GNU General Public License 247dd65febSAlbin Tonnerre * along with this program; see the file COPYING. 257dd65febSAlbin Tonnerre * If not, write to the Free Software Foundation, Inc., 267dd65febSAlbin Tonnerre * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 277dd65febSAlbin Tonnerre * 287dd65febSAlbin Tonnerre * Markus F.X.J. Oberhumer 297dd65febSAlbin Tonnerre * <markus@oberhumer.com> 307dd65febSAlbin Tonnerre * http://www.oberhumer.com/opensource/lzop/ 317dd65febSAlbin Tonnerre */ 327dd65febSAlbin Tonnerre 337dd65febSAlbin Tonnerre #ifdef STATIC 347dd65febSAlbin Tonnerre #include "lzo/lzo1x_decompress.c" 357dd65febSAlbin Tonnerre #else 367dd65febSAlbin Tonnerre #include <linux/decompress/unlzo.h> 377dd65febSAlbin Tonnerre #endif 387dd65febSAlbin Tonnerre 397dd65febSAlbin Tonnerre #include <linux/types.h> 407dd65febSAlbin Tonnerre #include <linux/lzo.h> 417dd65febSAlbin Tonnerre #include <linux/decompress/mm.h> 427dd65febSAlbin Tonnerre 437dd65febSAlbin Tonnerre #include <linux/compiler.h> 447dd65febSAlbin Tonnerre #include <asm/unaligned.h> 457dd65febSAlbin Tonnerre 467dd65febSAlbin Tonnerre static const unsigned char lzop_magic[] = { 477dd65febSAlbin Tonnerre 0x89, 0x4c, 0x5a, 0x4f, 0x00, 0x0d, 0x0a, 0x1a, 0x0a }; 487dd65febSAlbin Tonnerre 497dd65febSAlbin Tonnerre #define LZO_BLOCK_SIZE (256*1024l) 507dd65febSAlbin Tonnerre #define HEADER_HAS_FILTER 0x00000800L 517dd65febSAlbin Tonnerre 527dd65febSAlbin Tonnerre STATIC inline int INIT parse_header(u8 *input, u8 *skip) 537dd65febSAlbin Tonnerre { 547dd65febSAlbin Tonnerre int l; 557dd65febSAlbin Tonnerre u8 *parse = input; 567dd65febSAlbin Tonnerre u8 level = 0; 577dd65febSAlbin Tonnerre u16 version; 587dd65febSAlbin Tonnerre 597dd65febSAlbin Tonnerre /* read magic: 9 first bits */ 607dd65febSAlbin Tonnerre for (l = 0; l < 9; l++) { 617dd65febSAlbin Tonnerre if (*parse++ != lzop_magic[l]) 627dd65febSAlbin Tonnerre return 0; 637dd65febSAlbin Tonnerre } 647dd65febSAlbin Tonnerre /* get version (2bytes), skip library version (2), 657dd65febSAlbin Tonnerre * 'need to be extracted' version (2) and 667dd65febSAlbin Tonnerre * method (1) */ 677dd65febSAlbin Tonnerre version = get_unaligned_be16(parse); 687dd65febSAlbin Tonnerre parse += 7; 697dd65febSAlbin Tonnerre if (version >= 0x0940) 707dd65febSAlbin Tonnerre level = *parse++; 717dd65febSAlbin Tonnerre if (get_unaligned_be32(parse) & HEADER_HAS_FILTER) 727dd65febSAlbin Tonnerre parse += 8; /* flags + filter info */ 737dd65febSAlbin Tonnerre else 747dd65febSAlbin Tonnerre parse += 4; /* flags */ 757dd65febSAlbin Tonnerre 767dd65febSAlbin Tonnerre /* skip mode and mtime_low */ 777dd65febSAlbin Tonnerre parse += 8; 787dd65febSAlbin Tonnerre if (version >= 0x0940) 797dd65febSAlbin Tonnerre parse += 4; /* skip mtime_high */ 807dd65febSAlbin Tonnerre 817dd65febSAlbin Tonnerre l = *parse++; 827dd65febSAlbin Tonnerre /* don't care about the file name, and skip checksum */ 837dd65febSAlbin Tonnerre parse += l + 4; 847dd65febSAlbin Tonnerre 857dd65febSAlbin Tonnerre *skip = parse - input; 867dd65febSAlbin Tonnerre return 1; 877dd65febSAlbin Tonnerre } 887dd65febSAlbin Tonnerre 897dd65febSAlbin Tonnerre STATIC inline int INIT unlzo(u8 *input, int in_len, 907dd65febSAlbin Tonnerre int (*fill) (void *, unsigned int), 917dd65febSAlbin Tonnerre int (*flush) (void *, unsigned int), 927dd65febSAlbin Tonnerre u8 *output, int *posp, 9393685ad2SLasse Collin void (*error) (char *x)) 947dd65febSAlbin Tonnerre { 957dd65febSAlbin Tonnerre u8 skip = 0, r = 0; 967dd65febSAlbin Tonnerre u32 src_len, dst_len; 977dd65febSAlbin Tonnerre size_t tmp; 987dd65febSAlbin Tonnerre u8 *in_buf, *in_buf_save, *out_buf; 99ccdb4004SAlbin Tonnerre int ret = -1; 1007dd65febSAlbin Tonnerre 1017dd65febSAlbin Tonnerre if (output) { 1027dd65febSAlbin Tonnerre out_buf = output; 1037dd65febSAlbin Tonnerre } else if (!flush) { 1047dd65febSAlbin Tonnerre error("NULL output pointer and no flush function provided"); 1057dd65febSAlbin Tonnerre goto exit; 1067dd65febSAlbin Tonnerre } else { 1077dd65febSAlbin Tonnerre out_buf = malloc(LZO_BLOCK_SIZE); 1087dd65febSAlbin Tonnerre if (!out_buf) { 1097dd65febSAlbin Tonnerre error("Could not allocate output buffer"); 1107dd65febSAlbin Tonnerre goto exit; 1117dd65febSAlbin Tonnerre } 1127dd65febSAlbin Tonnerre } 1137dd65febSAlbin Tonnerre 1147dd65febSAlbin Tonnerre if (input && fill) { 1157dd65febSAlbin Tonnerre error("Both input pointer and fill function provided, don't know what to do"); 1167dd65febSAlbin Tonnerre goto exit_1; 1177dd65febSAlbin Tonnerre } else if (input) { 1187dd65febSAlbin Tonnerre in_buf = input; 1197dd65febSAlbin Tonnerre } else if (!fill || !posp) { 1207dd65febSAlbin Tonnerre error("NULL input pointer and missing position pointer or fill function"); 1217dd65febSAlbin Tonnerre goto exit_1; 1227dd65febSAlbin Tonnerre } else { 1237dd65febSAlbin Tonnerre in_buf = malloc(lzo1x_worst_compress(LZO_BLOCK_SIZE)); 1247dd65febSAlbin Tonnerre if (!in_buf) { 1257dd65febSAlbin Tonnerre error("Could not allocate input buffer"); 1267dd65febSAlbin Tonnerre goto exit_1; 1277dd65febSAlbin Tonnerre } 1287dd65febSAlbin Tonnerre } 1297dd65febSAlbin Tonnerre in_buf_save = in_buf; 1307dd65febSAlbin Tonnerre 1317dd65febSAlbin Tonnerre if (posp) 1327dd65febSAlbin Tonnerre *posp = 0; 1337dd65febSAlbin Tonnerre 1347dd65febSAlbin Tonnerre if (fill) 1357dd65febSAlbin Tonnerre fill(in_buf, lzo1x_worst_compress(LZO_BLOCK_SIZE)); 1367dd65febSAlbin Tonnerre 1377dd65febSAlbin Tonnerre if (!parse_header(input, &skip)) { 1387dd65febSAlbin Tonnerre error("invalid header"); 1397dd65febSAlbin Tonnerre goto exit_2; 1407dd65febSAlbin Tonnerre } 1417dd65febSAlbin Tonnerre in_buf += skip; 1427dd65febSAlbin Tonnerre 1437dd65febSAlbin Tonnerre if (posp) 1447dd65febSAlbin Tonnerre *posp = skip; 1457dd65febSAlbin Tonnerre 1467dd65febSAlbin Tonnerre for (;;) { 1477dd65febSAlbin Tonnerre /* read uncompressed block size */ 1487dd65febSAlbin Tonnerre dst_len = get_unaligned_be32(in_buf); 1497dd65febSAlbin Tonnerre in_buf += 4; 1507dd65febSAlbin Tonnerre 1517dd65febSAlbin Tonnerre /* exit if last block */ 1527dd65febSAlbin Tonnerre if (dst_len == 0) { 1537dd65febSAlbin Tonnerre if (posp) 1547dd65febSAlbin Tonnerre *posp += 4; 1557dd65febSAlbin Tonnerre break; 1567dd65febSAlbin Tonnerre } 1577dd65febSAlbin Tonnerre 1587dd65febSAlbin Tonnerre if (dst_len > LZO_BLOCK_SIZE) { 1597dd65febSAlbin Tonnerre error("dest len longer than block size"); 1607dd65febSAlbin Tonnerre goto exit_2; 1617dd65febSAlbin Tonnerre } 1627dd65febSAlbin Tonnerre 1637dd65febSAlbin Tonnerre /* read compressed block size, and skip block checksum info */ 1647dd65febSAlbin Tonnerre src_len = get_unaligned_be32(in_buf); 1657dd65febSAlbin Tonnerre in_buf += 8; 1667dd65febSAlbin Tonnerre 1677dd65febSAlbin Tonnerre if (src_len <= 0 || src_len > dst_len) { 1687dd65febSAlbin Tonnerre error("file corrupted"); 1697dd65febSAlbin Tonnerre goto exit_2; 1707dd65febSAlbin Tonnerre } 1717dd65febSAlbin Tonnerre 1727dd65febSAlbin Tonnerre /* decompress */ 1737dd65febSAlbin Tonnerre tmp = dst_len; 174ccdb4004SAlbin Tonnerre 175ccdb4004SAlbin Tonnerre /* When the input data is not compressed at all, 176ccdb4004SAlbin Tonnerre * lzo1x_decompress_safe will fail, so call memcpy() 177ccdb4004SAlbin Tonnerre * instead */ 178ccdb4004SAlbin Tonnerre if (unlikely(dst_len == src_len)) 179ccdb4004SAlbin Tonnerre memcpy(out_buf, in_buf, src_len); 180ccdb4004SAlbin Tonnerre else { 1817dd65febSAlbin Tonnerre r = lzo1x_decompress_safe((u8 *) in_buf, src_len, 1827dd65febSAlbin Tonnerre out_buf, &tmp); 1837dd65febSAlbin Tonnerre 1847dd65febSAlbin Tonnerre if (r != LZO_E_OK || dst_len != tmp) { 1857dd65febSAlbin Tonnerre error("Compressed data violation"); 1867dd65febSAlbin Tonnerre goto exit_2; 1877dd65febSAlbin Tonnerre } 188ccdb4004SAlbin Tonnerre } 1897dd65febSAlbin Tonnerre 190*8f9b54a3SLasse Collin if (flush && flush(out_buf, dst_len) != dst_len) 191*8f9b54a3SLasse Collin goto exit_2; 1927dd65febSAlbin Tonnerre if (output) 1937dd65febSAlbin Tonnerre out_buf += dst_len; 1947dd65febSAlbin Tonnerre if (posp) 1957dd65febSAlbin Tonnerre *posp += src_len + 12; 1967dd65febSAlbin Tonnerre if (fill) { 1977dd65febSAlbin Tonnerre in_buf = in_buf_save; 1987dd65febSAlbin Tonnerre fill(in_buf, lzo1x_worst_compress(LZO_BLOCK_SIZE)); 1997dd65febSAlbin Tonnerre } else 2007dd65febSAlbin Tonnerre in_buf += src_len; 2017dd65febSAlbin Tonnerre } 2027dd65febSAlbin Tonnerre 203ccdb4004SAlbin Tonnerre ret = 0; 2047dd65febSAlbin Tonnerre exit_2: 2057dd65febSAlbin Tonnerre if (!input) 2067dd65febSAlbin Tonnerre free(in_buf); 2077dd65febSAlbin Tonnerre exit_1: 2087dd65febSAlbin Tonnerre if (!output) 2097dd65febSAlbin Tonnerre free(out_buf); 2107dd65febSAlbin Tonnerre exit: 211ccdb4004SAlbin Tonnerre return ret; 2127dd65febSAlbin Tonnerre } 2137dd65febSAlbin Tonnerre 2147dd65febSAlbin Tonnerre #define decompress unlzo 215