File Index
Files and directories
Sign in
FILE

deflate.c

File preview with syntax highlighting.

linux-6.19.14/crypto/deflate.c C
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * Cryptographic API.
  4 *
  5 * Deflate algorithm (RFC 1951), implemented here primarily for use
  6 * by IPCOMP (RFC 3173 & RFC 2394).
  7 *
  8 * Copyright (c) 2003 James Morris <jmorris@intercode.com.au>
  9 * Copyright (c) 2023 Google, LLC. <ardb@kernel.org>
 10 * Copyright (c) 2025 Herbert Xu <herbert@gondor.apana.org.au>
 11 */
 12#include <crypto/internal/acompress.h>
 13#include <crypto/scatterwalk.h>
 14#include <linux/init.h>
 15#include <linux/kernel.h>
 16#include <linux/module.h>
 17#include <linux/mutex.h>
 18#include <linux/overflow.h>
 19#include <linux/percpu.h>
 20#include <linux/scatterlist.h>
 21#include <linux/slab.h>
 22#include <linux/spinlock.h>
 23#include <linux/zlib.h>
 24
 25#define DEFLATE_DEF_LEVEL		Z_DEFAULT_COMPRESSION
 26#define DEFLATE_DEF_WINBITS		11
 27#define DEFLATE_DEF_MEMLEVEL		MAX_MEM_LEVEL
 28
 29struct deflate_stream {
 30	struct z_stream_s stream;
 31	u8 workspace[];
 32};
 33
 34static DEFINE_MUTEX(deflate_stream_lock);
 35
 36static void *deflate_alloc_stream(void)
 37{
 38	size_t size = max(zlib_inflate_workspacesize(),
 39			  zlib_deflate_workspacesize(-DEFLATE_DEF_WINBITS,
 40						     DEFLATE_DEF_MEMLEVEL));
 41	struct deflate_stream *ctx;
 42
 43	ctx = kvmalloc(struct_size(ctx, workspace, size), GFP_KERNEL);
 44	if (!ctx)
 45		return ERR_PTR(-ENOMEM);
 46
 47	ctx->stream.workspace = ctx->workspace;
 48
 49	return ctx;
 50}
 51
 52static void deflate_free_stream(void *ctx)
 53{
 54	kvfree(ctx);
 55}
 56
 57static struct crypto_acomp_streams deflate_streams = {
 58	.alloc_ctx = deflate_alloc_stream,
 59	.free_ctx = deflate_free_stream,
 60};
 61
 62static int deflate_compress_one(struct acomp_req *req,
 63				struct deflate_stream *ds)
 64{
 65	struct z_stream_s *stream = &ds->stream;
 66	struct acomp_walk walk;
 67	int ret;
 68
 69	ret = acomp_walk_virt(&walk, req, true);
 70	if (ret)
 71		return ret;
 72
 73	do {
 74		unsigned int dcur;
 75
 76		dcur = acomp_walk_next_dst(&walk);
 77		if (!dcur)
 78			return -ENOSPC;
 79
 80		stream->avail_out = dcur;
 81		stream->next_out = walk.dst.virt.addr;
 82
 83		do {
 84			int flush = Z_FINISH;
 85			unsigned int scur;
 86
 87			stream->avail_in = 0;
 88			stream->next_in = NULL;
 89
 90			scur = acomp_walk_next_src(&walk);
 91			if (scur) {
 92				if (acomp_walk_more_src(&walk, scur))
 93					flush = Z_NO_FLUSH;
 94				stream->avail_in = scur;
 95				stream->next_in = walk.src.virt.addr;
 96			}
 97
 98			ret = zlib_deflate(stream, flush);
 99
100			if (scur) {
101				scur -= stream->avail_in;
102				acomp_walk_done_src(&walk, scur);
103			}
104		} while (ret == Z_OK && stream->avail_out);
105
106		acomp_walk_done_dst(&walk, dcur);
107	} while (ret == Z_OK);
108
109	if (ret != Z_STREAM_END)
110		return -EINVAL;
111
112	req->dlen = stream->total_out;
113	return 0;
114}
115
116static int deflate_compress(struct acomp_req *req)
117{
118	struct crypto_acomp_stream *s;
119	struct deflate_stream *ds;
120	int err;
121
122	s = crypto_acomp_lock_stream_bh(&deflate_streams);
123	ds = s->ctx;
124
125	err = zlib_deflateInit2(&ds->stream, DEFLATE_DEF_LEVEL, Z_DEFLATED,
126				-DEFLATE_DEF_WINBITS, DEFLATE_DEF_MEMLEVEL,
127				Z_DEFAULT_STRATEGY);
128	if (err != Z_OK) {
129		err = -EINVAL;
130		goto out;
131	}
132
133	err = deflate_compress_one(req, ds);
134
135out:
136	crypto_acomp_unlock_stream_bh(s);
137
138	return err;
139}
140
141static int deflate_decompress_one(struct acomp_req *req,
142				  struct deflate_stream *ds)
143{
144	struct z_stream_s *stream = &ds->stream;
145	bool out_of_space = false;
146	struct acomp_walk walk;
147	int ret;
148
149	ret = acomp_walk_virt(&walk, req, true);
150	if (ret)
151		return ret;
152
153	do {
154		unsigned int scur;
155
156		stream->avail_in = 0;
157		stream->next_in = NULL;
158
159		scur = acomp_walk_next_src(&walk);
160		if (scur) {
161			stream->avail_in = scur;
162			stream->next_in = walk.src.virt.addr;
163		}
164
165		do {
166			unsigned int dcur;
167			unsigned long avail_in;
168
169			dcur = acomp_walk_next_dst(&walk);
170
171			stream->avail_out = dcur;
172			stream->next_out = walk.dst.virt.addr;
173			avail_in = stream->avail_in;
174
175			ret = zlib_inflate(stream, Z_NO_FLUSH);
176
177			if (!dcur && avail_in == stream->avail_in) {
178				out_of_space = true;
179				break;
180			}
181
182			dcur -= stream->avail_out;
183			acomp_walk_done_dst(&walk, dcur);
184		} while (ret == Z_OK && stream->avail_in);
185
186		if (scur)
187			acomp_walk_done_src(&walk, scur);
188
189		if (out_of_space)
190			return -ENOSPC;
191	} while (ret == Z_OK);
192
193	if (ret != Z_STREAM_END)
194		return -EINVAL;
195
196	req->dlen = stream->total_out;
197	return 0;
198}
199
200static int deflate_decompress(struct acomp_req *req)
201{
202	struct crypto_acomp_stream *s;
203	struct deflate_stream *ds;
204	int err;
205
206	s = crypto_acomp_lock_stream_bh(&deflate_streams);
207	ds = s->ctx;
208
209	err = zlib_inflateInit2(&ds->stream, -DEFLATE_DEF_WINBITS);
210	if (err != Z_OK) {
211		err = -EINVAL;
212		goto out;
213	}
214
215	err = deflate_decompress_one(req, ds);
216
217out:
218	crypto_acomp_unlock_stream_bh(s);
219
220	return err;
221}
222
223static int deflate_init(struct crypto_acomp *tfm)
224{
225	int ret;
226
227	mutex_lock(&deflate_stream_lock);
228	ret = crypto_acomp_alloc_streams(&deflate_streams);
229	mutex_unlock(&deflate_stream_lock);
230
231	return ret;
232}
233
234static struct acomp_alg acomp = {
235	.compress		= deflate_compress,
236	.decompress		= deflate_decompress,
237	.init			= deflate_init,
238	.base.cra_name		= "deflate",
239	.base.cra_driver_name	= "deflate-generic",
240	.base.cra_flags		= CRYPTO_ALG_REQ_VIRT,
241	.base.cra_module	= THIS_MODULE,
242};
243
244static int __init deflate_mod_init(void)
245{
246	return crypto_register_acomp(&acomp);
247}
248
249static void __exit deflate_mod_fini(void)
250{
251	crypto_unregister_acomp(&acomp);
252	crypto_acomp_free_streams(&deflate_streams);
253}
254
255module_init(deflate_mod_init);
256module_exit(deflate_mod_fini);
257
258MODULE_LICENSE("GPL");
259MODULE_DESCRIPTION("Deflate Compression Algorithm for IPCOMP");
260MODULE_AUTHOR("James Morris <jmorris@intercode.com.au>");
261MODULE_AUTHOR("Ard Biesheuvel <ardb@kernel.org>");
262MODULE_AUTHOR("Herbert Xu <herbert@gondor.apana.org.au>");
263MODULE_ALIAS_CRYPTO("deflate");
264MODULE_ALIAS_CRYPTO("deflate-generic");
~/ Read-only file preview.