aboutsummaryrefslogtreecommitdiffstats
path: root/crypto/zlib.c
diff options
context:
space:
mode:
authorGeert Uytterhoeven <Geert.Uytterhoeven@sonycom.com>2009-03-04 02:15:49 -0500
committerHerbert Xu <herbert@gondor.apana.org.au>2009-03-04 02:16:19 -0500
commitbf68e65ec9ea61e32ab71bef59aa5d24d255241f (patch)
treeead13a824617ee21e137eb3e75336891e30ed508 /crypto/zlib.c
parent8064efb8740b8a0141d99a181cb5b9a430b1836c (diff)
crypto: zlib - New zlib crypto module, using pcomp
Signed-off-by: Geert Uytterhoeven <Geert.Uytterhoeven@sonycom.com> Cc: James Morris <jmorris@namei.org> Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Diffstat (limited to 'crypto/zlib.c')
-rw-r--r--crypto/zlib.c378
1 files changed, 378 insertions, 0 deletions
diff --git a/crypto/zlib.c b/crypto/zlib.c
new file mode 100644
index 000000000000..33609bab614e
--- /dev/null
+++ b/crypto/zlib.c
@@ -0,0 +1,378 @@
1/*
2 * Cryptographic API.
3 *
4 * Zlib algorithm
5 *
6 * Copyright 2008 Sony Corporation
7 *
8 * Based on deflate.c, which is
9 * Copyright (c) 2003 James Morris <jmorris@intercode.com.au>
10 *
11 * This program is free software; you can redistribute it and/or modify it
12 * under the terms of the GNU General Public License as published by the Free
13 * Software Foundation; either version 2 of the License, or (at your option)
14 * any later version.
15 *
16 * FIXME: deflate transforms will require up to a total of about 436k of kernel
17 * memory on i386 (390k for compression, the rest for decompression), as the
18 * current zlib kernel code uses a worst case pre-allocation system by default.
19 * This needs to be fixed so that the amount of memory required is properly
20 * related to the winbits and memlevel parameters.
21 */
22
23#define pr_fmt(fmt) "%s: " fmt, __func__
24
25#include <linux/init.h>
26#include <linux/module.h>
27#include <linux/zlib.h>
28#include <linux/vmalloc.h>
29#include <linux/interrupt.h>
30#include <linux/mm.h>
31#include <linux/net.h>
32#include <linux/slab.h>
33
34#include <crypto/internal/compress.h>
35
36#include <net/netlink.h>
37
38
39struct zlib_ctx {
40 struct z_stream_s comp_stream;
41 struct z_stream_s decomp_stream;
42 int decomp_windowBits;
43};
44
45
46static void zlib_comp_exit(struct zlib_ctx *ctx)
47{
48 struct z_stream_s *stream = &ctx->comp_stream;
49
50 if (stream->workspace) {
51 zlib_deflateEnd(stream);
52 vfree(stream->workspace);
53 stream->workspace = NULL;
54 }
55}
56
57static void zlib_decomp_exit(struct zlib_ctx *ctx)
58{
59 struct z_stream_s *stream = &ctx->decomp_stream;
60
61 if (stream->workspace) {
62 zlib_inflateEnd(stream);
63 kfree(stream->workspace);
64 stream->workspace = NULL;
65 }
66}
67
68static int zlib_init(struct crypto_tfm *tfm)
69{
70 return 0;
71}
72
73static void zlib_exit(struct crypto_tfm *tfm)
74{
75 struct zlib_ctx *ctx = crypto_tfm_ctx(tfm);
76
77 zlib_comp_exit(ctx);
78 zlib_decomp_exit(ctx);
79}
80
81
82static int zlib_compress_setup(struct crypto_pcomp *tfm, void *params,
83 unsigned int len)
84{
85 struct zlib_ctx *ctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
86 struct z_stream_s *stream = &ctx->comp_stream;
87 struct nlattr *tb[ZLIB_COMP_MAX + 1];
88 size_t workspacesize;
89 int ret;
90
91 ret = nla_parse(tb, ZLIB_COMP_MAX, params, len, NULL);
92 if (ret)
93 return ret;
94
95 zlib_comp_exit(ctx);
96
97 workspacesize = zlib_deflate_workspacesize();
98 stream->workspace = vmalloc(workspacesize);
99 if (!stream->workspace)
100 return -ENOMEM;
101
102 memset(stream->workspace, 0, workspacesize);
103 ret = zlib_deflateInit2(stream,
104 tb[ZLIB_COMP_LEVEL]
105 ? nla_get_u32(tb[ZLIB_COMP_LEVEL])
106 : Z_DEFAULT_COMPRESSION,
107 tb[ZLIB_COMP_METHOD]
108 ? nla_get_u32(tb[ZLIB_COMP_METHOD])
109 : Z_DEFLATED,
110 tb[ZLIB_COMP_WINDOWBITS]
111 ? nla_get_u32(tb[ZLIB_COMP_WINDOWBITS])
112 : MAX_WBITS,
113 tb[ZLIB_COMP_MEMLEVEL]
114 ? nla_get_u32(tb[ZLIB_COMP_MEMLEVEL])
115 : DEF_MEM_LEVEL,
116 tb[ZLIB_COMP_STRATEGY]
117 ? nla_get_u32(tb[ZLIB_COMP_STRATEGY])
118 : Z_DEFAULT_STRATEGY);
119 if (ret != Z_OK) {
120 vfree(stream->workspace);
121 stream->workspace = NULL;
122 return -EINVAL;
123 }
124
125 return 0;
126}
127
128static int zlib_compress_init(struct crypto_pcomp *tfm)
129{
130 int ret;
131 struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
132 struct z_stream_s *stream = &dctx->comp_stream;
133
134 ret = zlib_deflateReset(stream);
135 if (ret != Z_OK)
136 return -EINVAL;
137
138 return 0;
139}
140
141static int zlib_compress_update(struct crypto_pcomp *tfm,
142 struct comp_request *req)
143{
144 int ret;
145 struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
146 struct z_stream_s *stream = &dctx->comp_stream;
147
148 pr_debug("avail_in %u, avail_out %u\n", req->avail_in, req->avail_out);
149 stream->next_in = req->next_in;
150 stream->avail_in = req->avail_in;
151 stream->next_out = req->next_out;
152 stream->avail_out = req->avail_out;
153
154 ret = zlib_deflate(stream, Z_NO_FLUSH);
155 switch (ret) {
156 case Z_OK:
157 break;
158
159 case Z_BUF_ERROR:
160 pr_debug("zlib_deflate could not make progress\n");
161 return -EAGAIN;
162
163 default:
164 pr_debug("zlib_deflate failed %d\n", ret);
165 return -EINVAL;
166 }
167
168 pr_debug("avail_in %u, avail_out %u (consumed %u, produced %u)\n",
169 stream->avail_in, stream->avail_out,
170 req->avail_in - stream->avail_in,
171 req->avail_out - stream->avail_out);
172 req->next_in = stream->next_in;
173 req->avail_in = stream->avail_in;
174 req->next_out = stream->next_out;
175 req->avail_out = stream->avail_out;
176 return 0;
177}
178
179static int zlib_compress_final(struct crypto_pcomp *tfm,
180 struct comp_request *req)
181{
182 int ret;
183 struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
184 struct z_stream_s *stream = &dctx->comp_stream;
185
186 pr_debug("avail_in %u, avail_out %u\n", req->avail_in, req->avail_out);
187 stream->next_in = req->next_in;
188 stream->avail_in = req->avail_in;
189 stream->next_out = req->next_out;
190 stream->avail_out = req->avail_out;
191
192 ret = zlib_deflate(stream, Z_FINISH);
193 if (ret != Z_STREAM_END) {
194 pr_debug("zlib_deflate failed %d\n", ret);
195 return -EINVAL;
196 }
197
198 pr_debug("avail_in %u, avail_out %u (consumed %u, produced %u)\n",
199 stream->avail_in, stream->avail_out,
200 req->avail_in - stream->avail_in,
201 req->avail_out - stream->avail_out);
202 req->next_in = stream->next_in;
203 req->avail_in = stream->avail_in;
204 req->next_out = stream->next_out;
205 req->avail_out = stream->avail_out;
206 return 0;
207}
208
209
210static int zlib_decompress_setup(struct crypto_pcomp *tfm, void *params,
211 unsigned int len)
212{
213 struct zlib_ctx *ctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
214 struct z_stream_s *stream = &ctx->decomp_stream;
215 struct nlattr *tb[ZLIB_DECOMP_MAX + 1];
216 int ret = 0;
217
218 ret = nla_parse(tb, ZLIB_DECOMP_MAX, params, len, NULL);
219 if (ret)
220 return ret;
221
222 zlib_decomp_exit(ctx);
223
224 ctx->decomp_windowBits = tb[ZLIB_DECOMP_WINDOWBITS]
225 ? nla_get_u32(tb[ZLIB_DECOMP_WINDOWBITS])
226 : DEF_WBITS;
227
228 stream->workspace = kzalloc(zlib_inflate_workspacesize(), GFP_KERNEL);
229 if (!stream->workspace)
230 return -ENOMEM;
231
232 ret = zlib_inflateInit2(stream, ctx->decomp_windowBits);
233 if (ret != Z_OK) {
234 kfree(stream->workspace);
235 stream->workspace = NULL;
236 return -EINVAL;
237 }
238
239 return 0;
240}
241
242static int zlib_decompress_init(struct crypto_pcomp *tfm)
243{
244 int ret;
245 struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
246 struct z_stream_s *stream = &dctx->decomp_stream;
247
248 ret = zlib_inflateReset(stream);
249 if (ret != Z_OK)
250 return -EINVAL;
251
252 return 0;
253}
254
255static int zlib_decompress_update(struct crypto_pcomp *tfm,
256 struct comp_request *req)
257{
258 int ret;
259 struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
260 struct z_stream_s *stream = &dctx->decomp_stream;
261
262 pr_debug("avail_in %u, avail_out %u\n", req->avail_in, req->avail_out);
263 stream->next_in = req->next_in;
264 stream->avail_in = req->avail_in;
265 stream->next_out = req->next_out;
266 stream->avail_out = req->avail_out;
267
268 ret = zlib_inflate(stream, Z_SYNC_FLUSH);
269 switch (ret) {
270 case Z_OK:
271 case Z_STREAM_END:
272 break;
273
274 case Z_BUF_ERROR:
275 pr_debug("zlib_inflate could not make progress\n");
276 return -EAGAIN;
277
278 default:
279 pr_debug("zlib_inflate failed %d\n", ret);
280 return -EINVAL;
281 }
282
283 pr_debug("avail_in %u, avail_out %u (consumed %u, produced %u)\n",
284 stream->avail_in, stream->avail_out,
285 req->avail_in - stream->avail_in,
286 req->avail_out - stream->avail_out);
287 req->next_in = stream->next_in;
288 req->avail_in = stream->avail_in;
289 req->next_out = stream->next_out;
290 req->avail_out = stream->avail_out;
291 return 0;
292}
293
294static int zlib_decompress_final(struct crypto_pcomp *tfm,
295 struct comp_request *req)
296{
297 int ret;
298 struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
299 struct z_stream_s *stream = &dctx->decomp_stream;
300
301 pr_debug("avail_in %u, avail_out %u\n", req->avail_in, req->avail_out);
302 stream->next_in = req->next_in;
303 stream->avail_in = req->avail_in;
304 stream->next_out = req->next_out;
305 stream->avail_out = req->avail_out;
306
307 if (dctx->decomp_windowBits < 0) {
308 ret = zlib_inflate(stream, Z_SYNC_FLUSH);
309 /*
310 * Work around a bug in zlib, which sometimes wants to taste an
311 * extra byte when being used in the (undocumented) raw deflate
312 * mode. (From USAGI).
313 */
314 if (ret == Z_OK && !stream->avail_in && stream->avail_out) {
315 const void *saved_next_in = stream->next_in;
316 u8 zerostuff = 0;
317
318 stream->next_in = &zerostuff;
319 stream->avail_in = 1;
320 ret = zlib_inflate(stream, Z_FINISH);
321 stream->next_in = saved_next_in;
322 stream->avail_in = 0;
323 }
324 } else
325 ret = zlib_inflate(stream, Z_FINISH);
326 if (ret != Z_STREAM_END) {
327 pr_debug("zlib_inflate failed %d\n", ret);
328 return -EINVAL;
329 }
330
331 pr_debug("avail_in %u, avail_out %u (consumed %u, produced %u)\n",
332 stream->avail_in, stream->avail_out,
333 req->avail_in - stream->avail_in,
334 req->avail_out - stream->avail_out);
335 req->next_in = stream->next_in;
336 req->avail_in = stream->avail_in;
337 req->next_out = stream->next_out;
338 req->avail_out = stream->avail_out;
339 return 0;
340}
341
342
343static struct pcomp_alg zlib_alg = {
344 .compress_setup = zlib_compress_setup,
345 .compress_init = zlib_compress_init,
346 .compress_update = zlib_compress_update,
347 .compress_final = zlib_compress_final,
348 .decompress_setup = zlib_decompress_setup,
349 .decompress_init = zlib_decompress_init,
350 .decompress_update = zlib_decompress_update,
351 .decompress_final = zlib_decompress_final,
352
353 .base = {
354 .cra_name = "zlib",
355 .cra_flags = CRYPTO_ALG_TYPE_PCOMPRESS,
356 .cra_ctxsize = sizeof(struct zlib_ctx),
357 .cra_module = THIS_MODULE,
358 .cra_init = zlib_init,
359 .cra_exit = zlib_exit,
360 }
361};
362
363static int __init zlib_mod_init(void)
364{
365 return crypto_register_pcomp(&zlib_alg);
366}
367
368static void __exit zlib_mod_fini(void)
369{
370 crypto_unregister_pcomp(&zlib_alg);
371}
372
373module_init(zlib_mod_init);
374module_exit(zlib_mod_fini);
375
376MODULE_LICENSE("GPL");
377MODULE_DESCRIPTION("Zlib Compression Algorithm");
378MODULE_AUTHOR("Sony Corporation");