blob: 739b8fca4cea518777f569372ded2e0b751dcd49 [file] [log] [blame]
Geert Uytterhoevenbf68e652009-03-04 15:15:49 +08001/*
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();
Joe Perchesc8484592010-11-27 16:30:39 +080098 stream->workspace = vzalloc(workspacesize);
Geert Uytterhoevenbf68e652009-03-04 15:15:49 +080099 if (!stream->workspace)
100 return -ENOMEM;
101
Geert Uytterhoevenbf68e652009-03-04 15:15:49 +0800102 ret = zlib_deflateInit2(stream,
103 tb[ZLIB_COMP_LEVEL]
104 ? nla_get_u32(tb[ZLIB_COMP_LEVEL])
105 : Z_DEFAULT_COMPRESSION,
106 tb[ZLIB_COMP_METHOD]
107 ? nla_get_u32(tb[ZLIB_COMP_METHOD])
108 : Z_DEFLATED,
109 tb[ZLIB_COMP_WINDOWBITS]
110 ? nla_get_u32(tb[ZLIB_COMP_WINDOWBITS])
111 : MAX_WBITS,
112 tb[ZLIB_COMP_MEMLEVEL]
113 ? nla_get_u32(tb[ZLIB_COMP_MEMLEVEL])
114 : DEF_MEM_LEVEL,
115 tb[ZLIB_COMP_STRATEGY]
116 ? nla_get_u32(tb[ZLIB_COMP_STRATEGY])
117 : Z_DEFAULT_STRATEGY);
118 if (ret != Z_OK) {
119 vfree(stream->workspace);
120 stream->workspace = NULL;
121 return -EINVAL;
122 }
123
124 return 0;
125}
126
127static int zlib_compress_init(struct crypto_pcomp *tfm)
128{
129 int ret;
130 struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
131 struct z_stream_s *stream = &dctx->comp_stream;
132
133 ret = zlib_deflateReset(stream);
134 if (ret != Z_OK)
135 return -EINVAL;
136
137 return 0;
138}
139
140static int zlib_compress_update(struct crypto_pcomp *tfm,
141 struct comp_request *req)
142{
143 int ret;
144 struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
145 struct z_stream_s *stream = &dctx->comp_stream;
146
147 pr_debug("avail_in %u, avail_out %u\n", req->avail_in, req->avail_out);
148 stream->next_in = req->next_in;
149 stream->avail_in = req->avail_in;
150 stream->next_out = req->next_out;
151 stream->avail_out = req->avail_out;
152
153 ret = zlib_deflate(stream, Z_NO_FLUSH);
154 switch (ret) {
155 case Z_OK:
156 break;
157
158 case Z_BUF_ERROR:
159 pr_debug("zlib_deflate could not make progress\n");
160 return -EAGAIN;
161
162 default:
163 pr_debug("zlib_deflate failed %d\n", ret);
164 return -EINVAL;
165 }
166
Geert Uytterhoeven3ce858c2009-05-27 15:05:02 +1000167 ret = req->avail_out - stream->avail_out;
Geert Uytterhoevenbf68e652009-03-04 15:15:49 +0800168 pr_debug("avail_in %u, avail_out %u (consumed %u, produced %u)\n",
169 stream->avail_in, stream->avail_out,
Geert Uytterhoeven3ce858c2009-05-27 15:05:02 +1000170 req->avail_in - stream->avail_in, ret);
Geert Uytterhoevenbf68e652009-03-04 15:15:49 +0800171 req->next_in = stream->next_in;
172 req->avail_in = stream->avail_in;
173 req->next_out = stream->next_out;
174 req->avail_out = stream->avail_out;
Geert Uytterhoeven3ce858c2009-05-27 15:05:02 +1000175 return ret;
Geert Uytterhoevenbf68e652009-03-04 15:15:49 +0800176}
177
178static int zlib_compress_final(struct crypto_pcomp *tfm,
179 struct comp_request *req)
180{
181 int ret;
182 struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
183 struct z_stream_s *stream = &dctx->comp_stream;
184
185 pr_debug("avail_in %u, avail_out %u\n", req->avail_in, req->avail_out);
186 stream->next_in = req->next_in;
187 stream->avail_in = req->avail_in;
188 stream->next_out = req->next_out;
189 stream->avail_out = req->avail_out;
190
191 ret = zlib_deflate(stream, Z_FINISH);
192 if (ret != Z_STREAM_END) {
193 pr_debug("zlib_deflate failed %d\n", ret);
194 return -EINVAL;
195 }
196
Geert Uytterhoeven3ce858c2009-05-27 15:05:02 +1000197 ret = req->avail_out - stream->avail_out;
Geert Uytterhoevenbf68e652009-03-04 15:15:49 +0800198 pr_debug("avail_in %u, avail_out %u (consumed %u, produced %u)\n",
199 stream->avail_in, stream->avail_out,
Geert Uytterhoeven3ce858c2009-05-27 15:05:02 +1000200 req->avail_in - stream->avail_in, ret);
Geert Uytterhoevenbf68e652009-03-04 15:15:49 +0800201 req->next_in = stream->next_in;
202 req->avail_in = stream->avail_in;
203 req->next_out = stream->next_out;
204 req->avail_out = stream->avail_out;
Geert Uytterhoeven3ce858c2009-05-27 15:05:02 +1000205 return ret;
Geert Uytterhoevenbf68e652009-03-04 15:15:49 +0800206}
207
208
209static int zlib_decompress_setup(struct crypto_pcomp *tfm, void *params,
210 unsigned int len)
211{
212 struct zlib_ctx *ctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
213 struct z_stream_s *stream = &ctx->decomp_stream;
214 struct nlattr *tb[ZLIB_DECOMP_MAX + 1];
215 int ret = 0;
216
217 ret = nla_parse(tb, ZLIB_DECOMP_MAX, params, len, NULL);
218 if (ret)
219 return ret;
220
221 zlib_decomp_exit(ctx);
222
223 ctx->decomp_windowBits = tb[ZLIB_DECOMP_WINDOWBITS]
224 ? nla_get_u32(tb[ZLIB_DECOMP_WINDOWBITS])
225 : DEF_WBITS;
226
227 stream->workspace = kzalloc(zlib_inflate_workspacesize(), GFP_KERNEL);
228 if (!stream->workspace)
229 return -ENOMEM;
230
231 ret = zlib_inflateInit2(stream, ctx->decomp_windowBits);
232 if (ret != Z_OK) {
233 kfree(stream->workspace);
234 stream->workspace = NULL;
235 return -EINVAL;
236 }
237
238 return 0;
239}
240
241static int zlib_decompress_init(struct crypto_pcomp *tfm)
242{
243 int ret;
244 struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
245 struct z_stream_s *stream = &dctx->decomp_stream;
246
247 ret = zlib_inflateReset(stream);
248 if (ret != Z_OK)
249 return -EINVAL;
250
251 return 0;
252}
253
254static int zlib_decompress_update(struct crypto_pcomp *tfm,
255 struct comp_request *req)
256{
257 int ret;
258 struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
259 struct z_stream_s *stream = &dctx->decomp_stream;
260
261 pr_debug("avail_in %u, avail_out %u\n", req->avail_in, req->avail_out);
262 stream->next_in = req->next_in;
263 stream->avail_in = req->avail_in;
264 stream->next_out = req->next_out;
265 stream->avail_out = req->avail_out;
266
267 ret = zlib_inflate(stream, Z_SYNC_FLUSH);
268 switch (ret) {
269 case Z_OK:
270 case Z_STREAM_END:
271 break;
272
273 case Z_BUF_ERROR:
274 pr_debug("zlib_inflate could not make progress\n");
275 return -EAGAIN;
276
277 default:
278 pr_debug("zlib_inflate failed %d\n", ret);
279 return -EINVAL;
280 }
281
Geert Uytterhoeven3ce858c2009-05-27 15:05:02 +1000282 ret = req->avail_out - stream->avail_out;
Geert Uytterhoevenbf68e652009-03-04 15:15:49 +0800283 pr_debug("avail_in %u, avail_out %u (consumed %u, produced %u)\n",
284 stream->avail_in, stream->avail_out,
Geert Uytterhoeven3ce858c2009-05-27 15:05:02 +1000285 req->avail_in - stream->avail_in, ret);
Geert Uytterhoevenbf68e652009-03-04 15:15:49 +0800286 req->next_in = stream->next_in;
287 req->avail_in = stream->avail_in;
288 req->next_out = stream->next_out;
289 req->avail_out = stream->avail_out;
Geert Uytterhoeven3ce858c2009-05-27 15:05:02 +1000290 return ret;
Geert Uytterhoevenbf68e652009-03-04 15:15:49 +0800291}
292
293static int zlib_decompress_final(struct crypto_pcomp *tfm,
294 struct comp_request *req)
295{
296 int ret;
297 struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
298 struct z_stream_s *stream = &dctx->decomp_stream;
299
300 pr_debug("avail_in %u, avail_out %u\n", req->avail_in, req->avail_out);
301 stream->next_in = req->next_in;
302 stream->avail_in = req->avail_in;
303 stream->next_out = req->next_out;
304 stream->avail_out = req->avail_out;
305
306 if (dctx->decomp_windowBits < 0) {
307 ret = zlib_inflate(stream, Z_SYNC_FLUSH);
308 /*
309 * Work around a bug in zlib, which sometimes wants to taste an
310 * extra byte when being used in the (undocumented) raw deflate
311 * mode. (From USAGI).
312 */
313 if (ret == Z_OK && !stream->avail_in && stream->avail_out) {
314 const void *saved_next_in = stream->next_in;
315 u8 zerostuff = 0;
316
317 stream->next_in = &zerostuff;
318 stream->avail_in = 1;
319 ret = zlib_inflate(stream, Z_FINISH);
320 stream->next_in = saved_next_in;
321 stream->avail_in = 0;
322 }
323 } else
324 ret = zlib_inflate(stream, Z_FINISH);
325 if (ret != Z_STREAM_END) {
326 pr_debug("zlib_inflate failed %d\n", ret);
327 return -EINVAL;
328 }
329
Geert Uytterhoeven3ce858c2009-05-27 15:05:02 +1000330 ret = req->avail_out - stream->avail_out;
Geert Uytterhoevenbf68e652009-03-04 15:15:49 +0800331 pr_debug("avail_in %u, avail_out %u (consumed %u, produced %u)\n",
332 stream->avail_in, stream->avail_out,
Geert Uytterhoeven3ce858c2009-05-27 15:05:02 +1000333 req->avail_in - stream->avail_in, ret);
Geert Uytterhoevenbf68e652009-03-04 15:15:49 +0800334 req->next_in = stream->next_in;
335 req->avail_in = stream->avail_in;
336 req->next_out = stream->next_out;
337 req->avail_out = stream->avail_out;
Geert Uytterhoeven3ce858c2009-05-27 15:05:02 +1000338 return ret;
Geert Uytterhoevenbf68e652009-03-04 15:15:49 +0800339}
340
341
342static struct pcomp_alg zlib_alg = {
343 .compress_setup = zlib_compress_setup,
344 .compress_init = zlib_compress_init,
345 .compress_update = zlib_compress_update,
346 .compress_final = zlib_compress_final,
347 .decompress_setup = zlib_decompress_setup,
348 .decompress_init = zlib_decompress_init,
349 .decompress_update = zlib_decompress_update,
350 .decompress_final = zlib_decompress_final,
351
352 .base = {
353 .cra_name = "zlib",
354 .cra_flags = CRYPTO_ALG_TYPE_PCOMPRESS,
355 .cra_ctxsize = sizeof(struct zlib_ctx),
356 .cra_module = THIS_MODULE,
357 .cra_init = zlib_init,
358 .cra_exit = zlib_exit,
359 }
360};
361
362static int __init zlib_mod_init(void)
363{
364 return crypto_register_pcomp(&zlib_alg);
365}
366
367static void __exit zlib_mod_fini(void)
368{
369 crypto_unregister_pcomp(&zlib_alg);
370}
371
372module_init(zlib_mod_init);
373module_exit(zlib_mod_fini);
374
375MODULE_LICENSE("GPL");
376MODULE_DESCRIPTION("Zlib Compression Algorithm");
377MODULE_AUTHOR("Sony Corporation");