| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264 |
- // SPDX-License-Identifier: GPL-2.0-or-later
- /*
- * Cryptographic API.
- *
- * Deflate algorithm (RFC 1951), implemented here primarily for use
- * by IPCOMP (RFC 3173 & RFC 2394).
- *
- * Copyright (c) 2003 James Morris <jmorris@intercode.com.au>
- * Copyright (c) 2023 Google, LLC. <ardb@kernel.org>
- * Copyright (c) 2025 Herbert Xu <herbert@gondor.apana.org.au>
- */
- #include <crypto/internal/acompress.h>
- #include <crypto/scatterwalk.h>
- #include <linux/init.h>
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/mutex.h>
- #include <linux/overflow.h>
- #include <linux/percpu.h>
- #include <linux/scatterlist.h>
- #include <linux/slab.h>
- #include <linux/spinlock.h>
- #include <linux/zlib.h>
- #define DEFLATE_DEF_LEVEL Z_DEFAULT_COMPRESSION
- #define DEFLATE_DEF_WINBITS 11
- #define DEFLATE_DEF_MEMLEVEL MAX_MEM_LEVEL
- struct deflate_stream {
- struct z_stream_s stream;
- u8 workspace[];
- };
- static DEFINE_MUTEX(deflate_stream_lock);
- static void *deflate_alloc_stream(void)
- {
- size_t size = max(zlib_inflate_workspacesize(),
- zlib_deflate_workspacesize(-DEFLATE_DEF_WINBITS,
- DEFLATE_DEF_MEMLEVEL));
- struct deflate_stream *ctx;
- ctx = kvmalloc_flex(*ctx, workspace, size);
- if (!ctx)
- return ERR_PTR(-ENOMEM);
- ctx->stream.workspace = ctx->workspace;
- return ctx;
- }
- static void deflate_free_stream(void *ctx)
- {
- kvfree(ctx);
- }
- static struct crypto_acomp_streams deflate_streams = {
- .alloc_ctx = deflate_alloc_stream,
- .free_ctx = deflate_free_stream,
- };
- static int deflate_compress_one(struct acomp_req *req,
- struct deflate_stream *ds)
- {
- struct z_stream_s *stream = &ds->stream;
- struct acomp_walk walk;
- int ret;
- ret = acomp_walk_virt(&walk, req, true);
- if (ret)
- return ret;
- do {
- unsigned int dcur;
- dcur = acomp_walk_next_dst(&walk);
- if (!dcur)
- return -ENOSPC;
- stream->avail_out = dcur;
- stream->next_out = walk.dst.virt.addr;
- do {
- int flush = Z_FINISH;
- unsigned int scur;
- stream->avail_in = 0;
- stream->next_in = NULL;
- scur = acomp_walk_next_src(&walk);
- if (scur) {
- if (acomp_walk_more_src(&walk, scur))
- flush = Z_NO_FLUSH;
- stream->avail_in = scur;
- stream->next_in = walk.src.virt.addr;
- }
- ret = zlib_deflate(stream, flush);
- if (scur) {
- scur -= stream->avail_in;
- acomp_walk_done_src(&walk, scur);
- }
- } while (ret == Z_OK && stream->avail_out);
- acomp_walk_done_dst(&walk, dcur);
- } while (ret == Z_OK);
- if (ret != Z_STREAM_END)
- return -EINVAL;
- req->dlen = stream->total_out;
- return 0;
- }
- static int deflate_compress(struct acomp_req *req)
- {
- struct crypto_acomp_stream *s;
- struct deflate_stream *ds;
- int err;
- s = crypto_acomp_lock_stream_bh(&deflate_streams);
- ds = s->ctx;
- err = zlib_deflateInit2(&ds->stream, DEFLATE_DEF_LEVEL, Z_DEFLATED,
- -DEFLATE_DEF_WINBITS, DEFLATE_DEF_MEMLEVEL,
- Z_DEFAULT_STRATEGY);
- if (err != Z_OK) {
- err = -EINVAL;
- goto out;
- }
- err = deflate_compress_one(req, ds);
- out:
- crypto_acomp_unlock_stream_bh(s);
- return err;
- }
- static int deflate_decompress_one(struct acomp_req *req,
- struct deflate_stream *ds)
- {
- struct z_stream_s *stream = &ds->stream;
- bool out_of_space = false;
- struct acomp_walk walk;
- int ret;
- ret = acomp_walk_virt(&walk, req, true);
- if (ret)
- return ret;
- do {
- unsigned int scur;
- stream->avail_in = 0;
- stream->next_in = NULL;
- scur = acomp_walk_next_src(&walk);
- if (scur) {
- stream->avail_in = scur;
- stream->next_in = walk.src.virt.addr;
- }
- do {
- unsigned int dcur;
- unsigned long avail_in;
- dcur = acomp_walk_next_dst(&walk);
- stream->avail_out = dcur;
- stream->next_out = walk.dst.virt.addr;
- avail_in = stream->avail_in;
- ret = zlib_inflate(stream, Z_NO_FLUSH);
- if (!dcur && avail_in == stream->avail_in) {
- out_of_space = true;
- break;
- }
- dcur -= stream->avail_out;
- acomp_walk_done_dst(&walk, dcur);
- } while (ret == Z_OK && stream->avail_in);
- if (scur)
- acomp_walk_done_src(&walk, scur);
- if (out_of_space)
- return -ENOSPC;
- } while (ret == Z_OK);
- if (ret != Z_STREAM_END)
- return -EINVAL;
- req->dlen = stream->total_out;
- return 0;
- }
- static int deflate_decompress(struct acomp_req *req)
- {
- struct crypto_acomp_stream *s;
- struct deflate_stream *ds;
- int err;
- s = crypto_acomp_lock_stream_bh(&deflate_streams);
- ds = s->ctx;
- err = zlib_inflateInit2(&ds->stream, -DEFLATE_DEF_WINBITS);
- if (err != Z_OK) {
- err = -EINVAL;
- goto out;
- }
- err = deflate_decompress_one(req, ds);
- out:
- crypto_acomp_unlock_stream_bh(s);
- return err;
- }
- static int deflate_init(struct crypto_acomp *tfm)
- {
- int ret;
- mutex_lock(&deflate_stream_lock);
- ret = crypto_acomp_alloc_streams(&deflate_streams);
- mutex_unlock(&deflate_stream_lock);
- return ret;
- }
- static struct acomp_alg acomp = {
- .compress = deflate_compress,
- .decompress = deflate_decompress,
- .init = deflate_init,
- .base.cra_name = "deflate",
- .base.cra_driver_name = "deflate-generic",
- .base.cra_flags = CRYPTO_ALG_REQ_VIRT,
- .base.cra_module = THIS_MODULE,
- };
- static int __init deflate_mod_init(void)
- {
- return crypto_register_acomp(&acomp);
- }
- static void __exit deflate_mod_fini(void)
- {
- crypto_unregister_acomp(&acomp);
- crypto_acomp_free_streams(&deflate_streams);
- }
- module_init(deflate_mod_init);
- module_exit(deflate_mod_fini);
- MODULE_LICENSE("GPL");
- MODULE_DESCRIPTION("Deflate Compression Algorithm for IPCOMP");
- MODULE_AUTHOR("James Morris <jmorris@intercode.com.au>");
- MODULE_AUTHOR("Ard Biesheuvel <ardb@kernel.org>");
- MODULE_AUTHOR("Herbert Xu <herbert@gondor.apana.org.au>");
- MODULE_ALIAS_CRYPTO("deflate");
- MODULE_ALIAS_CRYPTO("deflate-generic");
|