1
2
3#define pr_fmt(fmt)	"%s: " fmt, __func__
4
5#include <linux/init.h>
6#include <linux/module.h>
7#include <linux/zlib.h>
8#include <linux/vmalloc.h>
9#include <linux/interrupt.h>
10#include <linux/mm.h>
11#include <linux/net.h>
12#include <linux/slab.h>
13
14#include <crypto/internal/compress.h>
15
16#include <net/netlink.h>
17
18
19struct zlib_ctx {
20	struct z_stream_s comp_stream;
21	struct z_stream_s decomp_stream;
22	int decomp_windowBits;
23};
24
25
26static void zlib_comp_exit(struct zlib_ctx *ctx)
27{
28	struct z_stream_s *stream = &ctx->comp_stream;
29
30	if (stream->workspace) {
31		zlib_deflateEnd(stream);
32		vfree(stream->workspace);
33		stream->workspace = NULL;
34	}
35}
36
37static void zlib_decomp_exit(struct zlib_ctx *ctx)
38{
39	struct z_stream_s *stream = &ctx->decomp_stream;
40
41	if (stream->workspace) {
42		zlib_inflateEnd(stream);
43		kfree(stream->workspace);
44		stream->workspace = NULL;
45	}
46}
47
48static int zlib_init(struct crypto_tfm *tfm)
49{
50	return 0;
51}
52
53static void zlib_exit(struct crypto_tfm *tfm)
54{
55	struct zlib_ctx *ctx = crypto_tfm_ctx(tfm);
56
57	zlib_comp_exit(ctx);
58	zlib_decomp_exit(ctx);
59}
60
61
62static int zlib_compress_setup(struct crypto_pcomp *tfm, void *params,
63			       unsigned int len)
64{
65	struct zlib_ctx *ctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
66	struct z_stream_s *stream = &ctx->comp_stream;
67	struct nlattr *tb[ZLIB_COMP_MAX + 1];
68	size_t workspacesize;
69	int ret;
70
71	ret = nla_parse(tb, ZLIB_COMP_MAX, params, len, NULL);
72	if (ret)
73		return ret;
74
75	zlib_comp_exit(ctx);
76
77	workspacesize = zlib_deflate_workspacesize();
78	stream->workspace = vmalloc(workspacesize);
79	if (!stream->workspace)
80		return -ENOMEM;
81
82	memset(stream->workspace, 0, workspacesize);
83	ret = zlib_deflateInit2(stream,
84				tb[ZLIB_COMP_LEVEL]
85					? nla_get_u32(tb[ZLIB_COMP_LEVEL])
86					: Z_DEFAULT_COMPRESSION,
87				tb[ZLIB_COMP_METHOD]
88					? nla_get_u32(tb[ZLIB_COMP_METHOD])
89					: Z_DEFLATED,
90				tb[ZLIB_COMP_WINDOWBITS]
91					? nla_get_u32(tb[ZLIB_COMP_WINDOWBITS])
92					: MAX_WBITS,
93				tb[ZLIB_COMP_MEMLEVEL]
94					? nla_get_u32(tb[ZLIB_COMP_MEMLEVEL])
95					: DEF_MEM_LEVEL,
96				tb[ZLIB_COMP_STRATEGY]
97					? nla_get_u32(tb[ZLIB_COMP_STRATEGY])
98					: Z_DEFAULT_STRATEGY);
99	if (ret != Z_OK) {
100		vfree(stream->workspace);
101		stream->workspace = NULL;
102		return -EINVAL;
103	}
104
105	return 0;
106}
107
108static int zlib_compress_init(struct crypto_pcomp *tfm)
109{
110	int ret;
111	struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
112	struct z_stream_s *stream = &dctx->comp_stream;
113
114	ret = zlib_deflateReset(stream);
115	if (ret != Z_OK)
116		return -EINVAL;
117
118	return 0;
119}
120
121static int zlib_compress_update(struct crypto_pcomp *tfm,
122				struct comp_request *req)
123{
124	int ret;
125	struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
126	struct z_stream_s *stream = &dctx->comp_stream;
127
128	pr_debug("avail_in %u, avail_out %u\n", req->avail_in, req->avail_out);
129	stream->next_in = req->next_in;
130	stream->avail_in = req->avail_in;
131	stream->next_out = req->next_out;
132	stream->avail_out = req->avail_out;
133
134	ret = zlib_deflate(stream, Z_NO_FLUSH);
135	switch (ret) {
136	case Z_OK:
137		break;
138
139	case Z_BUF_ERROR:
140		pr_debug("zlib_deflate could not make progress\n");
141		return -EAGAIN;
142
143	default:
144		pr_debug("zlib_deflate failed %d\n", ret);
145		return -EINVAL;
146	}
147
148	ret = req->avail_out - stream->avail_out;
149	pr_debug("avail_in %u, avail_out %u (consumed %u, produced %u)\n",
150		 stream->avail_in, stream->avail_out,
151		 req->avail_in - stream->avail_in, ret);
152	req->next_in = stream->next_in;
153	req->avail_in = stream->avail_in;
154	req->next_out = stream->next_out;
155	req->avail_out = stream->avail_out;
156	return ret;
157}
158
159static int zlib_compress_final(struct crypto_pcomp *tfm,
160			       struct comp_request *req)
161{
162	int ret;
163	struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
164	struct z_stream_s *stream = &dctx->comp_stream;
165
166	pr_debug("avail_in %u, avail_out %u\n", req->avail_in, req->avail_out);
167	stream->next_in = req->next_in;
168	stream->avail_in = req->avail_in;
169	stream->next_out = req->next_out;
170	stream->avail_out = req->avail_out;
171
172	ret = zlib_deflate(stream, Z_FINISH);
173	if (ret != Z_STREAM_END) {
174		pr_debug("zlib_deflate failed %d\n", ret);
175		return -EINVAL;
176	}
177
178	ret = req->avail_out - stream->avail_out;
179	pr_debug("avail_in %u, avail_out %u (consumed %u, produced %u)\n",
180		 stream->avail_in, stream->avail_out,
181		 req->avail_in - stream->avail_in, ret);
182	req->next_in = stream->next_in;
183	req->avail_in = stream->avail_in;
184	req->next_out = stream->next_out;
185	req->avail_out = stream->avail_out;
186	return ret;
187}
188
189
190static int zlib_decompress_setup(struct crypto_pcomp *tfm, void *params,
191				 unsigned int len)
192{
193	struct zlib_ctx *ctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
194	struct z_stream_s *stream = &ctx->decomp_stream;
195	struct nlattr *tb[ZLIB_DECOMP_MAX + 1];
196	int ret = 0;
197
198	ret = nla_parse(tb, ZLIB_DECOMP_MAX, params, len, NULL);
199	if (ret)
200		return ret;
201
202	zlib_decomp_exit(ctx);
203
204	ctx->decomp_windowBits = tb[ZLIB_DECOMP_WINDOWBITS]
205				 ? nla_get_u32(tb[ZLIB_DECOMP_WINDOWBITS])
206				 : DEF_WBITS;
207
208	stream->workspace = kzalloc(zlib_inflate_workspacesize(), GFP_KERNEL);
209	if (!stream->workspace)
210		return -ENOMEM;
211
212	ret = zlib_inflateInit2(stream, ctx->decomp_windowBits);
213	if (ret != Z_OK) {
214		kfree(stream->workspace);
215		stream->workspace = NULL;
216		return -EINVAL;
217	}
218
219	return 0;
220}
221
222static int zlib_decompress_init(struct crypto_pcomp *tfm)
223{
224	int ret;
225	struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
226	struct z_stream_s *stream = &dctx->decomp_stream;
227
228	ret = zlib_inflateReset(stream);
229	if (ret != Z_OK)
230		return -EINVAL;
231
232	return 0;
233}
234
235static int zlib_decompress_update(struct crypto_pcomp *tfm,
236				  struct comp_request *req)
237{
238	int ret;
239	struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
240	struct z_stream_s *stream = &dctx->decomp_stream;
241
242	pr_debug("avail_in %u, avail_out %u\n", req->avail_in, req->avail_out);
243	stream->next_in = req->next_in;
244	stream->avail_in = req->avail_in;
245	stream->next_out = req->next_out;
246	stream->avail_out = req->avail_out;
247
248	ret = zlib_inflate(stream, Z_SYNC_FLUSH);
249	switch (ret) {
250	case Z_OK:
251	case Z_STREAM_END:
252		break;
253
254	case Z_BUF_ERROR:
255		pr_debug("zlib_inflate could not make progress\n");
256		return -EAGAIN;
257
258	default:
259		pr_debug("zlib_inflate failed %d\n", ret);
260		return -EINVAL;
261	}
262
263	ret = req->avail_out - stream->avail_out;
264	pr_debug("avail_in %u, avail_out %u (consumed %u, produced %u)\n",
265		 stream->avail_in, stream->avail_out,
266		 req->avail_in - stream->avail_in, ret);
267	req->next_in = stream->next_in;
268	req->avail_in = stream->avail_in;
269	req->next_out = stream->next_out;
270	req->avail_out = stream->avail_out;
271	return ret;
272}
273
274static int zlib_decompress_final(struct crypto_pcomp *tfm,
275				 struct comp_request *req)
276{
277	int ret;
278	struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
279	struct z_stream_s *stream = &dctx->decomp_stream;
280
281	pr_debug("avail_in %u, avail_out %u\n", req->avail_in, req->avail_out);
282	stream->next_in = req->next_in;
283	stream->avail_in = req->avail_in;
284	stream->next_out = req->next_out;
285	stream->avail_out = req->avail_out;
286
287	if (dctx->decomp_windowBits < 0) {
288		ret = zlib_inflate(stream, Z_SYNC_FLUSH);
289		if (ret == Z_OK && !stream->avail_in && stream->avail_out) {
290			const void *saved_next_in = stream->next_in;
291			u8 zerostuff = 0;
292
293			stream->next_in = &zerostuff;
294			stream->avail_in = 1;
295			ret = zlib_inflate(stream, Z_FINISH);
296			stream->next_in = saved_next_in;
297			stream->avail_in = 0;
298		}
299	} else
300		ret = zlib_inflate(stream, Z_FINISH);
301	if (ret != Z_STREAM_END) {
302		pr_debug("zlib_inflate failed %d\n", ret);
303		return -EINVAL;
304	}
305
306	ret = req->avail_out - stream->avail_out;
307	pr_debug("avail_in %u, avail_out %u (consumed %u, produced %u)\n",
308		 stream->avail_in, stream->avail_out,
309		 req->avail_in - stream->avail_in, ret);
310	req->next_in = stream->next_in;
311	req->avail_in = stream->avail_in;
312	req->next_out = stream->next_out;
313	req->avail_out = stream->avail_out;
314	return ret;
315}
316
317
318static struct pcomp_alg zlib_alg = {
319	.compress_setup		= zlib_compress_setup,
320	.compress_init		= zlib_compress_init,
321	.compress_update	= zlib_compress_update,
322	.compress_final		= zlib_compress_final,
323	.decompress_setup	= zlib_decompress_setup,
324	.decompress_init	= zlib_decompress_init,
325	.decompress_update	= zlib_decompress_update,
326	.decompress_final	= zlib_decompress_final,
327
328	.base			= {
329		.cra_name	= "zlib",
330		.cra_flags	= CRYPTO_ALG_TYPE_PCOMPRESS,
331		.cra_ctxsize	= sizeof(struct zlib_ctx),
332		.cra_module	= THIS_MODULE,
333		.cra_init	= zlib_init,
334		.cra_exit	= zlib_exit,
335	}
336};
337
338static int __init zlib_mod_init(void)
339{
340	return crypto_register_pcomp(&zlib_alg);
341}
342
343static void __exit zlib_mod_fini(void)
344{
345	crypto_unregister_pcomp(&zlib_alg);
346}
347
348module_init(zlib_mod_init);
349module_exit(zlib_mod_fini);
350
351MODULE_LICENSE("GPL");
352MODULE_DESCRIPTION("Zlib Compression Algorithm");
353MODULE_AUTHOR("Sony Corporation");
354