123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381 |
- /*
- * Cryptographic API.
- *
- * Zlib algorithm
- *
- * Copyright 2008 Sony Corporation
- *
- * Based on deflate.c, which is
- * Copyright (c) 2003 James Morris <jmorris@intercode.com.au>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * FIXME: deflate transforms will require up to a total of about 436k of kernel
- * memory on i386 (390k for compression, the rest for decompression), as the
- * current zlib kernel code uses a worst case pre-allocation system by default.
- * This needs to be fixed so that the amount of memory required is properly
- * related to the winbits and memlevel parameters.
- */
- #define pr_fmt(fmt) "%s: " fmt, __func__
- #include <linux/init.h>
- #include <linux/module.h>
- #include <linux/zlib.h>
- #include <linux/vmalloc.h>
- #include <linux/interrupt.h>
- #include <linux/mm.h>
- #include <linux/net.h>
- #include <crypto/internal/compress.h>
- #include <net/netlink.h>
- struct zlib_ctx {
- struct z_stream_s comp_stream;
- struct z_stream_s decomp_stream;
- int decomp_windowBits;
- };
- static void zlib_comp_exit(struct zlib_ctx *ctx)
- {
- struct z_stream_s *stream = &ctx->comp_stream;
- if (stream->workspace) {
- zlib_deflateEnd(stream);
- vfree(stream->workspace);
- stream->workspace = NULL;
- }
- }
- static void zlib_decomp_exit(struct zlib_ctx *ctx)
- {
- struct z_stream_s *stream = &ctx->decomp_stream;
- if (stream->workspace) {
- zlib_inflateEnd(stream);
- vfree(stream->workspace);
- stream->workspace = NULL;
- }
- }
- static int zlib_init(struct crypto_tfm *tfm)
- {
- return 0;
- }
- static void zlib_exit(struct crypto_tfm *tfm)
- {
- struct zlib_ctx *ctx = crypto_tfm_ctx(tfm);
- zlib_comp_exit(ctx);
- zlib_decomp_exit(ctx);
- }
- static int zlib_compress_setup(struct crypto_pcomp *tfm, void *params,
- unsigned int len)
- {
- struct zlib_ctx *ctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
- struct z_stream_s *stream = &ctx->comp_stream;
- struct nlattr *tb[ZLIB_COMP_MAX + 1];
- int window_bits, mem_level;
- size_t workspacesize;
- int ret;
- ret = nla_parse(tb, ZLIB_COMP_MAX, params, len, NULL);
- if (ret)
- return ret;
- zlib_comp_exit(ctx);
- window_bits = tb[ZLIB_COMP_WINDOWBITS]
- ? nla_get_u32(tb[ZLIB_COMP_WINDOWBITS])
- : MAX_WBITS;
- mem_level = tb[ZLIB_COMP_MEMLEVEL]
- ? nla_get_u32(tb[ZLIB_COMP_MEMLEVEL])
- : DEF_MEM_LEVEL;
- workspacesize = zlib_deflate_workspacesize(window_bits, mem_level);
- stream->workspace = vzalloc(workspacesize);
- if (!stream->workspace)
- return -ENOMEM;
- ret = zlib_deflateInit2(stream,
- tb[ZLIB_COMP_LEVEL]
- ? nla_get_u32(tb[ZLIB_COMP_LEVEL])
- : Z_DEFAULT_COMPRESSION,
- tb[ZLIB_COMP_METHOD]
- ? nla_get_u32(tb[ZLIB_COMP_METHOD])
- : Z_DEFLATED,
- window_bits,
- mem_level,
- tb[ZLIB_COMP_STRATEGY]
- ? nla_get_u32(tb[ZLIB_COMP_STRATEGY])
- : Z_DEFAULT_STRATEGY);
- if (ret != Z_OK) {
- vfree(stream->workspace);
- stream->workspace = NULL;
- return -EINVAL;
- }
- return 0;
- }
- static int zlib_compress_init(struct crypto_pcomp *tfm)
- {
- int ret;
- struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
- struct z_stream_s *stream = &dctx->comp_stream;
- ret = zlib_deflateReset(stream);
- if (ret != Z_OK)
- return -EINVAL;
- return 0;
- }
- static int zlib_compress_update(struct crypto_pcomp *tfm,
- struct comp_request *req)
- {
- int ret;
- struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
- struct z_stream_s *stream = &dctx->comp_stream;
- pr_debug("avail_in %u, avail_out %u\n", req->avail_in, req->avail_out);
- stream->next_in = req->next_in;
- stream->avail_in = req->avail_in;
- stream->next_out = req->next_out;
- stream->avail_out = req->avail_out;
- ret = zlib_deflate(stream, Z_NO_FLUSH);
- switch (ret) {
- case Z_OK:
- break;
- case Z_BUF_ERROR:
- pr_debug("zlib_deflate could not make progress\n");
- return -EAGAIN;
- default:
- pr_debug("zlib_deflate failed %d\n", ret);
- return -EINVAL;
- }
- ret = req->avail_out - stream->avail_out;
- pr_debug("avail_in %u, avail_out %u (consumed %u, produced %u)\n",
- stream->avail_in, stream->avail_out,
- req->avail_in - stream->avail_in, ret);
- req->next_in = stream->next_in;
- req->avail_in = stream->avail_in;
- req->next_out = stream->next_out;
- req->avail_out = stream->avail_out;
- return ret;
- }
- static int zlib_compress_final(struct crypto_pcomp *tfm,
- struct comp_request *req)
- {
- int ret;
- struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
- struct z_stream_s *stream = &dctx->comp_stream;
- pr_debug("avail_in %u, avail_out %u\n", req->avail_in, req->avail_out);
- stream->next_in = req->next_in;
- stream->avail_in = req->avail_in;
- stream->next_out = req->next_out;
- stream->avail_out = req->avail_out;
- ret = zlib_deflate(stream, Z_FINISH);
- if (ret != Z_STREAM_END) {
- pr_debug("zlib_deflate failed %d\n", ret);
- return -EINVAL;
- }
- ret = req->avail_out - stream->avail_out;
- pr_debug("avail_in %u, avail_out %u (consumed %u, produced %u)\n",
- stream->avail_in, stream->avail_out,
- req->avail_in - stream->avail_in, ret);
- req->next_in = stream->next_in;
- req->avail_in = stream->avail_in;
- req->next_out = stream->next_out;
- req->avail_out = stream->avail_out;
- return ret;
- }
- static int zlib_decompress_setup(struct crypto_pcomp *tfm, void *params,
- unsigned int len)
- {
- struct zlib_ctx *ctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
- struct z_stream_s *stream = &ctx->decomp_stream;
- struct nlattr *tb[ZLIB_DECOMP_MAX + 1];
- int ret = 0;
- ret = nla_parse(tb, ZLIB_DECOMP_MAX, params, len, NULL);
- if (ret)
- return ret;
- zlib_decomp_exit(ctx);
- ctx->decomp_windowBits = tb[ZLIB_DECOMP_WINDOWBITS]
- ? nla_get_u32(tb[ZLIB_DECOMP_WINDOWBITS])
- : DEF_WBITS;
- stream->workspace = vzalloc(zlib_inflate_workspacesize());
- if (!stream->workspace)
- return -ENOMEM;
- ret = zlib_inflateInit2(stream, ctx->decomp_windowBits);
- if (ret != Z_OK) {
- vfree(stream->workspace);
- stream->workspace = NULL;
- return -EINVAL;
- }
- return 0;
- }
- static int zlib_decompress_init(struct crypto_pcomp *tfm)
- {
- int ret;
- struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
- struct z_stream_s *stream = &dctx->decomp_stream;
- ret = zlib_inflateReset(stream);
- if (ret != Z_OK)
- return -EINVAL;
- return 0;
- }
- static int zlib_decompress_update(struct crypto_pcomp *tfm,
- struct comp_request *req)
- {
- int ret;
- struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
- struct z_stream_s *stream = &dctx->decomp_stream;
- pr_debug("avail_in %u, avail_out %u\n", req->avail_in, req->avail_out);
- stream->next_in = req->next_in;
- stream->avail_in = req->avail_in;
- stream->next_out = req->next_out;
- stream->avail_out = req->avail_out;
- ret = zlib_inflate(stream, Z_SYNC_FLUSH);
- switch (ret) {
- case Z_OK:
- case Z_STREAM_END:
- break;
- case Z_BUF_ERROR:
- pr_debug("zlib_inflate could not make progress\n");
- return -EAGAIN;
- default:
- pr_debug("zlib_inflate failed %d\n", ret);
- return -EINVAL;
- }
- ret = req->avail_out - stream->avail_out;
- pr_debug("avail_in %u, avail_out %u (consumed %u, produced %u)\n",
- stream->avail_in, stream->avail_out,
- req->avail_in - stream->avail_in, ret);
- req->next_in = stream->next_in;
- req->avail_in = stream->avail_in;
- req->next_out = stream->next_out;
- req->avail_out = stream->avail_out;
- return ret;
- }
- static int zlib_decompress_final(struct crypto_pcomp *tfm,
- struct comp_request *req)
- {
- int ret;
- struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
- struct z_stream_s *stream = &dctx->decomp_stream;
- pr_debug("avail_in %u, avail_out %u\n", req->avail_in, req->avail_out);
- stream->next_in = req->next_in;
- stream->avail_in = req->avail_in;
- stream->next_out = req->next_out;
- stream->avail_out = req->avail_out;
- if (dctx->decomp_windowBits < 0) {
- ret = zlib_inflate(stream, Z_SYNC_FLUSH);
- /*
- * Work around a bug in zlib, which sometimes wants to taste an
- * extra byte when being used in the (undocumented) raw deflate
- * mode. (From USAGI).
- */
- if (ret == Z_OK && !stream->avail_in && stream->avail_out) {
- const void *saved_next_in = stream->next_in;
- u8 zerostuff = 0;
- stream->next_in = &zerostuff;
- stream->avail_in = 1;
- ret = zlib_inflate(stream, Z_FINISH);
- stream->next_in = saved_next_in;
- stream->avail_in = 0;
- }
- } else
- ret = zlib_inflate(stream, Z_FINISH);
- if (ret != Z_STREAM_END) {
- pr_debug("zlib_inflate failed %d\n", ret);
- return -EINVAL;
- }
- ret = req->avail_out - stream->avail_out;
- pr_debug("avail_in %u, avail_out %u (consumed %u, produced %u)\n",
- stream->avail_in, stream->avail_out,
- req->avail_in - stream->avail_in, ret);
- req->next_in = stream->next_in;
- req->avail_in = stream->avail_in;
- req->next_out = stream->next_out;
- req->avail_out = stream->avail_out;
- return ret;
- }
- static struct pcomp_alg zlib_alg = {
- .compress_setup = zlib_compress_setup,
- .compress_init = zlib_compress_init,
- .compress_update = zlib_compress_update,
- .compress_final = zlib_compress_final,
- .decompress_setup = zlib_decompress_setup,
- .decompress_init = zlib_decompress_init,
- .decompress_update = zlib_decompress_update,
- .decompress_final = zlib_decompress_final,
- .base = {
- .cra_name = "zlib",
- .cra_flags = CRYPTO_ALG_TYPE_PCOMPRESS,
- .cra_ctxsize = sizeof(struct zlib_ctx),
- .cra_module = THIS_MODULE,
- .cra_init = zlib_init,
- .cra_exit = zlib_exit,
- }
- };
- static int __init zlib_mod_init(void)
- {
- return crypto_register_pcomp(&zlib_alg);
- }
- static void __exit zlib_mod_fini(void)
- {
- crypto_unregister_pcomp(&zlib_alg);
- }
- module_init(zlib_mod_init);
- module_exit(zlib_mod_fini);
- MODULE_LICENSE("GPL");
- MODULE_DESCRIPTION("Zlib Compression Algorithm");
- MODULE_AUTHOR("Sony Corporation");
|