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