Root/
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 | |
39 | struct zlib_ctx { |
40 | struct z_stream_s comp_stream; |
41 | struct z_stream_s decomp_stream; |
42 | int decomp_windowBits; |
43 | }; |
44 | |
45 | |
46 | static 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 | |
57 | static 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 | |
68 | static int zlib_init(struct crypto_tfm *tfm) |
69 | { |
70 | return 0; |
71 | } |
72 | |
73 | static 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 | |
82 | static 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 | |
128 | static 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 | |
141 | static 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 | ret = req->avail_out - stream->avail_out; |
169 | pr_debug("avail_in %u, avail_out %u (consumed %u, produced %u)\n", |
170 | stream->avail_in, stream->avail_out, |
171 | req->avail_in - stream->avail_in, ret); |
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 ret; |
177 | } |
178 | |
179 | static 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 | ret = req->avail_out - stream->avail_out; |
199 | pr_debug("avail_in %u, avail_out %u (consumed %u, produced %u)\n", |
200 | stream->avail_in, stream->avail_out, |
201 | req->avail_in - stream->avail_in, ret); |
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 ret; |
207 | } |
208 | |
209 | |
210 | static 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 | |
242 | static 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 | |
255 | static 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 | ret = req->avail_out - stream->avail_out; |
284 | pr_debug("avail_in %u, avail_out %u (consumed %u, produced %u)\n", |
285 | stream->avail_in, stream->avail_out, |
286 | req->avail_in - stream->avail_in, ret); |
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 ret; |
292 | } |
293 | |
294 | static 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 | ret = req->avail_out - stream->avail_out; |
332 | pr_debug("avail_in %u, avail_out %u (consumed %u, produced %u)\n", |
333 | stream->avail_in, stream->avail_out, |
334 | req->avail_in - stream->avail_in, ret); |
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 ret; |
340 | } |
341 | |
342 | |
343 | static 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 | |
363 | static int __init zlib_mod_init(void) |
364 | { |
365 | return crypto_register_pcomp(&zlib_alg); |
366 | } |
367 | |
368 | static void __exit zlib_mod_fini(void) |
369 | { |
370 | crypto_unregister_pcomp(&zlib_alg); |
371 | } |
372 | |
373 | module_init(zlib_mod_init); |
374 | module_exit(zlib_mod_fini); |
375 | |
376 | MODULE_LICENSE("GPL"); |
377 | MODULE_DESCRIPTION("Zlib Compression Algorithm"); |
378 | MODULE_AUTHOR("Sony Corporation"); |
379 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9