diff --git a/lib/decompress_unlzo.c b/lib/decompress_unlzo.c index 8f9cdcf..fc5c49f 100644 --- a/lib/decompress_unlzo.c +++ b/lib/decompress_unlzo.c @@ -47,98 +47,73 @@ #define LZO_BLOCK_SIZE (256*1024l) #define HEADER_HAS_FILTER 0x00000800L -static inline int parse_header(int in_fd) +static inline int parse_header(u8 *input, u8 *skip) { - u8 l; + int l; + u8 *parse = input; + u8 level = 0; u16 version; - int ret; - unsigned char buf[256]; /* maximum filename length + 1 */ - /* read magic (9), library version (2), 'need to be extracted' - * version (2) and method (1) - */ - ret = read(in_fd, buf, 9); - if (ret < 0) - return ret; - - /* check magic */ + /* read magic: 9 first bits */ for (l = 0; l < 9; l++) { - if (buf[l] != lzop_magic[l]) - return -EINVAL; + if (*parse++ != lzop_magic[l]) + return 0; } - - ret = read(in_fd, buf, 4); /* version, lib_version */ - if (ret < 0) - return ret; - version = get_unaligned_be16(buf); - - if (version >= 0x0940) { - ret = read(in_fd, buf, 2); /* version to extract */ - if (ret < 0) - return ret; - } - - ret = read(in_fd, buf, 1); /* method */ - if (ret < 0) - return ret; - + /* get version (2bytes), skip library version (2), + * 'need to be extracted' version (2) and + * method (1) */ + version = get_unaligned_be16(parse); + parse += 7; if (version >= 0x0940) - read(in_fd, buf, 1); /* level */ - - ret = read(in_fd, buf, 4); /* flags */ - if (ret < 0) - return ret; - - if (get_unaligned_be32(buf) & HEADER_HAS_FILTER) { - ret = read(in_fd, buf, 4); /* skip filter info */ - if (ret < 0) - return ret; - } + level = *parse++; + if (get_unaligned_be32(parse) & HEADER_HAS_FILTER) + parse += 8; /* flags + filter info */ + else + parse += 4; /* flags */ /* skip mode and mtime_low */ - ret = read(in_fd, buf, 8); - if (ret < 0) - return ret; + parse += 8; + if (version >= 0x0940) + parse += 4; /* skip mtime_high */ - if (version >= 0x0940) { - ret = read(in_fd, buf, 4); /* skip mtime_high */ - if (ret < 0) - return ret; - } - - ret = read(in_fd, &l, 1); - if (ret < 0) - return ret; + l = *parse++; /* don't care about the file name, and skip checksum */ - ret = read(in_fd, buf, l + 4); - if (ret < 0) - return ret; + parse += l + 4; - return 0; + *skip = parse - input; + return 1; } -int unlzo(int in_fd, int out_fd, int *dest_len) +static int __unlzo(int *dest_len, + int (*fill) (void *, unsigned int), + int (*flush) (void *, unsigned int)) { - u8 r = 0; + u8 skip = 0, r = 0; u32 src_len, dst_len; size_t tmp; - u8 *in_buf, *out_buf; + u8 *in_buf, *in_buf_save, *out_buf, *out_buf_save; int obytes_processed = 0; - unsigned char buf[8]; - int ret; - - if (parse_header(in_fd)) - return -EINVAL; + int ret = -1; out_buf = xmalloc(LZO_BLOCK_SIZE); in_buf = xmalloc(lzo1x_worst_compress(LZO_BLOCK_SIZE)); + in_buf_save = in_buf; + out_buf_save = out_buf; + + ret = fill(in_buf, lzo1x_worst_compress(LZO_BLOCK_SIZE)); + if (ret < 0) + goto exit_free; + + if (!parse_header(in_buf, &skip)) + return -EINVAL; + + in_buf += skip; + for (;;) { /* read uncompressed block size */ - ret = read(in_fd, buf, 4); - if (ret < 0) - goto exit_free; - dst_len = get_unaligned_be32(buf); + dst_len = get_unaligned_be32(in_buf); + in_buf += 4; /* exit if last block */ if (dst_len == 0) @@ -150,43 +125,53 @@ } /* read compressed block size, and skip block checksum info */ - ret = read(in_fd, buf, 8); - if (ret < 0) - goto exit_free; - - src_len = get_unaligned_be32(buf); + src_len = get_unaligned_be32(in_buf); + in_buf += 8; if (src_len <= 0 || src_len > dst_len) { printf("file corrupted"); goto exit_free; } - ret = read(in_fd, in_buf, src_len); - if (ret < 0) - goto exit_free; /* decompress */ tmp = dst_len; - if (src_len < dst_len) { - r = lzo1x_decompress_safe((u8 *) in_buf, src_len, - out_buf, &tmp); - if (r != LZO_E_OK || dst_len != tmp) { - printf("Compressed data violation"); - goto exit_free; - } - ret = write(out_fd, out_buf, dst_len); - if (ret < 0) - goto exit_free; - } else { + + /* When the input data is not compressed at all, + * lzo1x_decompress_safe will fail, so call memcpy() + * instead */ + if (unlikely(dst_len == src_len)) { if (src_len != dst_len) { printf("Compressed data violation"); goto exit_free; } - ret = write(out_fd, in_buf, dst_len); - if (ret < 0) + out_buf = in_buf; + } else { + r = lzo1x_decompress_safe((u8 *) in_buf, src_len, + out_buf, &tmp); + + if (r != LZO_E_OK || dst_len != tmp) { + printf("Compressed data violation"); goto exit_free; + } } + ret = flush(out_buf, dst_len); + if (ret < 0) + goto exit_free; + + out_buf = in_buf + src_len; + in_buf = in_buf_save; + ret = fill(in_buf, lzo1x_worst_compress(LZO_BLOCK_SIZE)); + if (ret < 0) + goto exit_free; + + if (ret == 0) + in_buf = out_buf; + + out_buf = out_buf_save; + obytes_processed += dst_len; + } exit_free: @@ -197,3 +182,22 @@ return 0; } +static int in_fd; +static int out_fd; + +static int unlzo_fill(void *buf, unsigned int len) +{ + return read(in_fd, buf, len); +} + +static int unlzo_flush(void *buf, unsigned int len) +{ + return write(out_fd, buf, len); +} + +int unlzo(int _in_fd, int _out_fd, int *dest_len) +{ + in_fd = _in_fd; + out_fd = _out_fd; + return __unlzo(dest_len, unlzo_fill, unlzo_flush); +}