| /* |
| * Copyright (C) 2008 Nokia Corporation. |
| * Copyright (C) 2008 University of Szeged, Hungary |
| * |
| * This program is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 as published by |
| * the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
| * more details. |
| * |
| * You should have received a copy of the GNU General Public License along with |
| * this program; if not, write to the Free Software Foundation, Inc., 51 |
| * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| * |
| * Authors: Artem Bityutskiy |
| * Adrian Hunter |
| * Zoltan Sogor |
| */ |
| |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <stdint.h> |
| #include <string.h> |
| #include <lzo/lzo1x.h> |
| #include <linux/types.h> |
| |
| #define crc32 __zlib_crc32 |
| #include <zlib.h> |
| #undef crc32 |
| |
| #include "compr.h" |
| #include "mkfs.ubifs.h" |
| |
| static void *lzo_mem; |
| static unsigned long long errcnt = 0; |
| static struct ubifs_info *c = &info_; |
| |
| #define DEFLATE_DEF_LEVEL Z_DEFAULT_COMPRESSION |
| #define DEFLATE_DEF_WINBITS 11 |
| #define DEFLATE_DEF_MEMLEVEL 8 |
| |
| static int zlib_deflate(void *in_buf, size_t in_len, void *out_buf, |
| size_t *out_len) |
| { |
| z_stream strm; |
| |
| strm.zalloc = NULL; |
| strm.zfree = NULL; |
| |
| /* |
| * Match exactly the zlib parameters used by the Linux kernel crypto |
| * API. |
| */ |
| if (deflateInit2(&strm, DEFLATE_DEF_LEVEL, Z_DEFLATED, |
| -DEFLATE_DEF_WINBITS, DEFLATE_DEF_MEMLEVEL, |
| Z_DEFAULT_STRATEGY)) { |
| errcnt += 1; |
| return -1; |
| } |
| |
| strm.next_in = in_buf; |
| strm.avail_in = in_len; |
| strm.total_in = 0; |
| |
| strm.next_out = out_buf; |
| strm.avail_out = *out_len; |
| strm.total_out = 0; |
| |
| if (deflate(&strm, Z_FINISH) != Z_STREAM_END) { |
| deflateEnd(&strm); |
| errcnt += 1; |
| return -1; |
| } |
| |
| if (deflateEnd(&strm) != Z_OK) { |
| errcnt += 1; |
| return -1; |
| } |
| |
| *out_len = strm.total_out; |
| |
| return 0; |
| } |
| |
| static int lzo_compress(void *in_buf, size_t in_len, void *out_buf, |
| size_t *out_len) |
| { |
| lzo_uint len; |
| int ret; |
| |
| len = *out_len; |
| ret = lzo1x_999_compress(in_buf, in_len, out_buf, &len, lzo_mem); |
| *out_len = len; |
| |
| if (ret != LZO_E_OK) { |
| errcnt += 1; |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| static int no_compress(void *in_buf, size_t in_len, void *out_buf, |
| size_t *out_len) |
| { |
| memcpy(out_buf, in_buf, in_len); |
| *out_len = in_len; |
| return 0; |
| } |
| |
| static char *zlib_buf; |
| |
| static int favor_lzo_compress(void *in_buf, size_t in_len, void *out_buf, |
| size_t *out_len, int *type) |
| { |
| int lzo_ret, zlib_ret; |
| size_t lzo_len, zlib_len; |
| |
| lzo_len = zlib_len = *out_len; |
| lzo_ret = lzo_compress(in_buf, in_len, out_buf, &lzo_len); |
| zlib_ret = zlib_deflate(in_buf, in_len, zlib_buf, &zlib_len); |
| |
| if (lzo_ret && zlib_ret) |
| /* Both compressors failed */ |
| return -1; |
| |
| if (!lzo_ret && !zlib_ret) { |
| double percent; |
| |
| /* Both compressors succeeded */ |
| if (lzo_len <= zlib_len ) |
| goto select_lzo; |
| |
| percent = (double)zlib_len / (double)lzo_len; |
| percent *= 100; |
| if (percent > 100 - c->favor_percent) |
| goto select_lzo; |
| goto select_zlib; |
| } |
| |
| if (lzo_ret) |
| /* Only zlib compressor succeeded */ |
| goto select_zlib; |
| |
| /* Only LZO compressor succeeded */ |
| |
| select_lzo: |
| *out_len = lzo_len; |
| *type = MKFS_UBIFS_COMPR_LZO; |
| return 0; |
| |
| select_zlib: |
| *out_len = zlib_len; |
| *type = MKFS_UBIFS_COMPR_ZLIB; |
| memcpy(out_buf, zlib_buf, zlib_len); |
| return 0; |
| } |
| |
| int compress_data(void *in_buf, size_t in_len, void *out_buf, size_t *out_len, |
| int type) |
| { |
| int ret; |
| |
| if (in_len < UBIFS_MIN_COMPR_LEN) { |
| no_compress(in_buf, in_len, out_buf, out_len); |
| return MKFS_UBIFS_COMPR_NONE; |
| } |
| |
| if (c->favor_lzo) |
| ret = favor_lzo_compress(in_buf, in_len, out_buf, out_len, &type); |
| else { |
| switch (type) { |
| case MKFS_UBIFS_COMPR_LZO: |
| ret = lzo_compress(in_buf, in_len, out_buf, out_len); |
| break; |
| case MKFS_UBIFS_COMPR_ZLIB: |
| ret = zlib_deflate(in_buf, in_len, out_buf, out_len); |
| break; |
| case MKFS_UBIFS_COMPR_NONE: |
| ret = 1; |
| break; |
| default: |
| errcnt += 1; |
| ret = 1; |
| break; |
| } |
| } |
| if (ret || *out_len >= in_len) { |
| no_compress(in_buf, in_len, out_buf, out_len); |
| return MKFS_UBIFS_COMPR_NONE; |
| } |
| return type; |
| } |
| |
| int init_compression(void) |
| { |
| lzo_mem = malloc(LZO1X_999_MEM_COMPRESS); |
| if (!lzo_mem) |
| return -1; |
| |
| zlib_buf = malloc(UBIFS_BLOCK_SIZE * WORST_COMPR_FACTOR); |
| if (!zlib_buf) { |
| free(lzo_mem); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| void destroy_compression(void) |
| { |
| free(zlib_buf); |
| free(lzo_mem); |
| if (errcnt) |
| fprintf(stderr, "%llu compression errors occurred\n", errcnt); |
| } |